Frost Field v1.0.2

===================
Frost Field v1.0.2
===================
My plan was to recreate Razor's Plasma Field Ability from DotA. Although, I could say the result is quite satisfying for me, the concept itself is definitely nothing new and creativity is an aspect that I'm quite lacking of. So I tried to amp the configurability aspect of the spell instead. Hopefully, it's good enough for hive's standard.

if you're a little confused about the min-max concept of the spell. Basically, the further away the enemy is, the more damage they will receive.
And also, the wave moves relative to caster's position. It is intended. I feel like I have to mention that.

Note: Don't spam it too much, the game might lag =P

Description
290312-a37855582f2f8473453a9589323ac5c5.png


290311-1c7fddade5b663c512f0350a9dea4e86.gif

How to Import
1. Copy "Frost Field" and "Frost Field (Slow)" ability from object editor
2. Copy "FrostField Caster" and "FrostField Missile" unit from object editor
3. Make sure your World Editor has "Automatically Create Unknown Variable" enabled. You can find it in File => Preferences
4. Copy all the triggers in "Frost Field" folder
5. Make sure the variables are assigned properly
6. Enjoy!

  • Frost Field Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- - --------
      • -------- This is configuration part of the spell --------
      • -------- You can change the values to whatever you desire to certain extent --------
      • -------- - --------
      • -------- This is the ability we will use to activate the spell --------
      • Set FRF_Ability = Frost Field
      • -------- - --------
      • -------- This is the ability we will use to slow enemy units --------
      • -------- Make sure it has the same amount of levels as the main ability --------
      • Set FRF_DummyAbility = Frost Field (Slow)
      • -------- - --------
      • -------- This is the order ID of the ability we will use to slow enemy units --------
      • -------- Based on Slow Ability --------
      • -------- For reference: https://github.com/nestharus/JASS/blob/master/jass/Systems/OrderIds/script.j --------
      • Set FRF_DummyOrderID = 852075
      • -------- - --------
      • -------- Do you want to slow nearby enemy units? Yes = True, No = False --------
      • Set FRF_SlowEffect = True
      • -------- - --------
      • -------- These are dummy units we will use in the spell --------
      • -------- FRF_DummyType[1] is projectile dummy --------
      • -------- FRF_DummyType[2] is buff caster dummy --------
      • -------- FRF_DummyType[3] is tree/debris destroyer --------
      • Set FRF_DummyType[1] = FrostField Missile
      • Set FRF_DummyType[2] = FrostField Caster
      • Set FRF_DummyType[3] = Peasant
      • -------- - --------
      • -------- The number of projectiles spawned --------
      • Set FRF_NumberOfProjectiles[1] = 36
      • Set FRF_NumberOfProjectiles[2] = 36
      • Set FRF_NumberOfProjectiles[3] = 36
      • -------- - --------
      • -------- The size of the summoned projectiles --------
      • Set FRF_ProjectileSize[1] = 1.00
      • Set FRF_ProjectileSize[2] = 1.00
      • Set FRF_ProjectileSize[3] = 1.00
      • -------- - --------
      • -------- The height of the summoned projectiles --------
      • Set FRF_ProjectileHeight[1] = 100.00
      • Set FRF_ProjectileHeight[2] = 100.00
      • Set FRF_ProjectileHeight[3] = 100.00
      • -------- - --------
      • -------- This determines how far the projectiles will be spawned from the caster at the start --------
      • Set FRF_StartOffset[1] = 50.00
      • Set FRF_StartOffset[2] = 50.00
      • Set FRF_StartOffset[3] = 50.00
      • -------- - --------
      • -------- The Radius of Frost Field aka how far the projectiles will travel --------
      • Set FRF_Radius[1] = 750.00
      • Set FRF_Radius[2] = 750.00
      • Set FRF_Radius[3] = 750.00
      • -------- - --------
      • -------- The Speed of the projectiles when they travel --------
      • Set FRF_BaseSpeed[1] = 600.00
      • Set FRF_BaseSpeed[2] = 600.00
      • Set FRF_BaseSpeed[3] = 600.00
      • -------- - --------
      • -------- The Aoe of Projectiles used for dealing damage --------
      • Set FRF_Aoe[1] = 150.00
      • Set FRF_Aoe[2] = 150.00
      • Set FRF_Aoe[3] = 150.00
      • -------- - --------
      • -------- The minimum possible damage dealt by the spell --------
      • Set FRF_MinDamage[1] = 60.00
      • Set FRF_MinDamage[2] = 100.00
      • Set FRF_MinDamage[2] = 140.00
      • -------- - --------
      • -------- The maximum possible damage dealt by the spell --------
      • Set FRF_MaxDamage[1] = 160.00
      • Set FRF_MaxDamage[2] = 250.00
      • Set FRF_MaxDamage[3] = 340.00
      • -------- - --------
      • -------- In case you want to deal a fixed damage, you can change it here --------
      • -------- Fixed Damage: True, Min-Max Damage: False --------
      • Set FRF_FixedDamage = False
      • -------- - --------
      • -------- This will be the fixed damage value per level --------
      • Set FRF_Damage[1] = 110.00
      • Set FRF_Damage[2] = 160.00
      • Set FRF_Damage[3] = 210.00
      • -------- - --------
      • -------- Damage Type and Attack Type of the spell --------
      • Set FRF_AtkType = Spells
      • Set FRF_DmgType = Cold
      • -------- - --------
      • -------- Do you want to damage buildings as well? Yes = True, No = False --------
      • Set FRF_DamageBuilding = False
      • -------- - --------
      • -------- You can adjust the damage multiplier against building here --------
      • -------- 1.00: Full damage, 2.00: Double Damage, 0.50: Half Damage --------
      • Set FRF_BuildingDmgFactor[1] = 1.00
      • Set FRF_BuildingDmgFactor[2] = 1.00
      • Set FRF_BuildingDmgFactor[3] = 1.00
      • -------- - --------
      • -------- Do you want to projectiles to come back after expanding outwards? Yes = True, No = False --------
      • Set FRF_ProjectileReturn = True
      • -------- - --------
      • -------- Do you want to do pathing check? Yes = True, No = False --------
      • -------- With pathing check enabled, the spell will immediately destroy a projectile if it finds unwalkable path --------
      • -------- 0: Pathing Check Disabled, 1: Pathing Check for Cliffs and Doodads only, 2: Pathing Check for Buildings, Destructibles, Cliffs, and Doodads --------
      • Set FRF_PathingCheck = 0
      • -------- - --------
      • -------- This determines how far the system does path check in front of projectiles --------
      • Set FRF_PathCheckOffset[1] = 128.00
      • Set FRF_PathCheckOffset[2] = 128.00
      • Set FRF_PathCheckOffset[3] = 128.00
      • -------- - --------
      • -------- Do you want to kill nearby trees? Yes = True, No = False --------
      • Set FRF_KillTrees = True
      • -------- - --------
      • -------- The "attack" option below will destroy any valid debris, from trees to barrels. --------
      • -------- If you just want to destroy trees, change the string to: harvest --------
      • Set FRF_TreeOrDebris = attack
      • -------- - --------
      • -------- If debris is to be killed, how far away must it be? --------
      • Set FRF_DestRadius[1] = 150.00
      • Set FRF_DestRadius[2] = 150.00
      • Set FRF_DestRadius[3] = 150.00
      • -------- - --------
      • -------- Special effect we will use for the spell --------
      • -------- EffectStr[1] will appear when the wave hits enemy units --------
      • -------- EffectStr[2] will appear when the wave returns to the caster --------
      • Set FRF_EffectStr[1] = Abilities\Weapons\FrostWyrmMissile\FrostWyrmMissile.mdl
      • Set FRF_EffectStr[2] = Abilities\Spells\Undead\FreezingBreath\FreezingBreathMissile.mdl
      • -------- - --------
      • -------- Attachment point for the special effect --------
      • -------- SFXAttachmentPoint[1] will correspond to EffectStr[1] --------
      • Set FRF_SFXAttachmentPoint[1] = origin
      • -------- - --------
      • -------- +> OFF LIMIT <+ --------
      • -------- Do not change anything below this point --------
      • -------- or else the spell might not work as intended --------
      • -------- make sure you know what you are doing --------
      • -------- - --------
      • -------- Advanced Config for Frost Field Trigger --------
      • -------- - --------
      • -------- Preload --------
      • Set FRF_Loc[1] = (Center of (Playable map area))
      • -------- - --------
      • Unit - Create 1 FRF_DummyType[1] for Neutral Passive at FRF_Loc[1] facing Default building facing degrees
      • Unit - Remove (Last created unit) from the game
      • Unit - Create 1 FRF_DummyType[2] for Neutral Passive at FRF_Loc[1] facing Default building facing degrees
      • Unit - Remove (Last created unit) from the game
      • Unit - Create 1 FRF_DummyType[3] for Neutral Passive at FRF_Loc[1] facing Default building facing degrees
      • -------- - --------
      • Special Effect - Create a special effect attached to the FRF_SFXAttachmentPoint[1] of (Last created unit) using FRF_EffectStr[1]
      • Special Effect - Destroy (Last created special effect)
      • -------- - --------
      • Unit - Add FRF_Ability to (Last created unit)
      • Unit - Remove FRF_Ability from (Last created unit)
      • Unit - Add FRF_DummyAbility to (Last created unit)
      • Unit - Remove FRF_DummyAbility from (Last created unit)
      • Unit - Add Harvest (Ghouls Lumber) to (Last created unit)
      • Unit - Remove Harvest (Ghouls Lumber) from (Last created unit)
      • Unit - Remove (Last created unit) from the game
      • -------- - --------
      • -------- Create dummy caster --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FRF_SlowEffect Equal to True
        • Then - Actions
          • -------- Create dummy caster --------
          • Unit - Create 1 FRF_DummyType[2] for Neutral Passive at FRF_Loc[1] facing Default building facing degrees
          • Set FRF_DummyCaster = (Last created unit)
          • -------- Add dummy ability (slow) --------
          • Unit - Add FRF_DummyAbility to FRF_DummyCaster
        • Else - Actions
      • -------- - --------
      • -------- Create tree / debris destroyer --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FRF_KillTrees Equal to True
        • Then - Actions
          • -------- Create tree / debris destroyer --------
          • Unit - Create 1 FRF_DummyType[3] for Neutral Passive at FRF_Loc[1] facing Default building facing degrees
          • Set FRF_TreeDebrisKiller = (Last created unit)
          • -------- Add Harvest just to be safe --------
          • Unit - Add Harvest (Ghouls Lumber) to FRF_TreeDebrisKiller
          • -------- Add locust just to be safe --------
          • Custom script: call UnitAddAbility( udg_FRF_TreeDebrisKiller, 'Aloc' )
          • -------- Hide the dummy --------
          • Unit - Hide FRF_TreeDebrisKiller
        • Else - Actions
      • -------- - --------
      • -------- Determines the interval of periodic timer in Frost Field loop trigger --------
      • Set FRF_Interval = 0.03
      • -------- - --------
      • -------- The loop trigger itself --------
      • Set FRF_LoopTrigger = Frost Field Loop <gen>
      • -------- - --------
      • -------- Adding periodic timer with the interval as event to the trigger --------
      • Trigger - Add to FRF_LoopTrigger the event (Time - Every FRF_Interval seconds of game time)
      • -------- - --------
      • -------- Store the value of map border to prevent crashing from projectile going over --------
      • Set FRF_MapMinX = (Min X of (Playable map area))
      • Set FRF_MapMaxX = (Max X of (Playable map area))
      • Set FRF_MapMinY = (Min Y of (Playable map area))
      • Set FRF_MapMaxY = (Max Y of (Playable map area))
      • -------- - --------
      • Custom script: call RemoveLocation( udg_FRF_Loc[1] )
  • Frost Field Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to FRF_Ability
    • Actions
      • Set FRF_SpellCount = (FRF_SpellCount + 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FRF_SpellCount Equal to 1
        • Then - Actions
          • Trigger - Turn on FRF_LoopTrigger
        • Else - Actions
          • -------- loop is already on; do nothing --------
      • -------- - --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • FRF_RecycledSize Equal to 0
        • Then - Actions
          • Set FRF_MaxIndex = (FRF_MaxIndex + 1)
          • Set FRF_Spell_ID = FRF_MaxIndex
        • Else - Actions
          • Set FRF_RecycledSize = (FRF_RecycledSize - 1)
          • Set FRF_Spell_ID = FRF_RecycledStack[FRF_RecycledSize]
      • -------- - --------
      • Set FRF_NodeNext[FRF_Spell_ID] = 0
      • Set FRF_NodeNext[FRF_NodePrev[0]] = FRF_Spell_ID
      • Set FRF_NodePrev[FRF_Spell_ID] = FRF_NodePrev[0]
      • Set FRF_NodePrev[0] = FRF_Spell_ID
      • -------- - --------
      • -------- - --------
      • Set FRF_Caster[FRF_Spell_ID] = (Triggering unit)
      • Set FRF_Owner[FRF_Spell_ID] = (Triggering player)
      • Set FRF_Level[FRF_Spell_ID] = (Level of FRF_Ability for FRF_Caster[FRF_Spell_ID])
      • Set FRF_TargetDistance[FRF_Spell_ID] = (FRF_Radius[FRF_Level[FRF_Spell_ID]] + FRF_StartOffset[FRF_Level[FRF_Spell_ID]])
      • Set FRF_Velocity[FRF_Spell_ID] = (FRF_BaseSpeed[FRF_Level[FRF_Spell_ID]] x FRF_Interval)
      • Set FRF_Stage[FRF_Spell_ID] = 1
      • Set FRF_RelativeOffset[FRF_Spell_ID] = 0.00
      • Set FRF_TotalDamage[FRF_Spell_ID] = 0.00
      • Set FRF_IsOutward[FRF_Spell_ID] = True
      • -------- - --------
      • Custom script: if udg_FRF_DamagedGroup[udg_FRF_Spell_ID] == null then
      • Custom script: set udg_FRF_DamagedGroup[udg_FRF_Spell_ID] = CreateGroup( )
      • Custom script: endif
      • Custom script: if udg_FRF_DamagedGroup2[udg_FRF_Spell_ID] == null then
      • Custom script: set udg_FRF_DamagedGroup2[udg_FRF_Spell_ID] = CreateGroup( )
      • Custom script: endif
      • Custom script: if udg_FRF_SlowedGroup[udg_FRF_Spell_ID] == null then
      • Custom script: set udg_FRF_SlowedGroup[udg_FRF_Spell_ID] = CreateGroup( )
      • Custom script: endif
      • Custom script: if udg_FRF_ProjectileGroup[udg_FRF_Spell_ID] == null then
      • Custom script: set udg_FRF_ProjectileGroup[udg_FRF_Spell_ID] = CreateGroup( )
      • Custom script: endif
      • -------- - --------
      • For each (Integer FRF_TempInt) from 1 to FRF_NumberOfProjectiles[FRF_Level[FRF_Spell_ID]], do (Actions)
        • Loop - Actions
          • Set FRF_Angle[FRF_Spell_ID] = ((Real(FRF_TempInt)) x (360.00 / (Real(FRF_NumberOfProjectiles[FRF_Level[FRF_Spell_ID]]))))
          • -------- - --------
          • Set FRF_Loc[1] = (Position of FRF_Caster[FRF_Spell_ID])
          • Set FRF_Loc[2] = (FRF_Loc[1] offset by FRF_StartOffset[FRF_Level[FRF_Spell_ID]] towards FRF_Angle[FRF_Spell_ID] degrees)
          • -------- - --------
          • Unit - Create 1 FRF_DummyType[1] for Neutral Passive at FRF_Loc[2] facing FRF_Angle[FRF_Spell_ID] degrees
          • Set FRF_TempUnit = (Last created unit)
          • -------- - --------
          • Unit Group - Add FRF_TempUnit to FRF_ProjectileGroup[FRF_Spell_ID]
          • -------- - --------
          • Animation - Change FRF_TempUnit's size to ((100.00 x FRF_ProjectileSize[FRF_Level[FRF_Spell_ID]])%, (100.00 x FRF_ProjectileSize[FRF_Level[FRF_Spell_ID]])%, (100.00 x FRF_ProjectileSize[FRF_Level[FRF_Spell_ID]])%) of its original size
          • Animation - Change FRF_TempUnit flying height to FRF_ProjectileHeight[FRF_Level[FRF_Spell_ID]] at 0.00
          • -------- - --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FRF_MaxIndex Equal to 1
            • Then - Actions
              • Trigger - Turn on FRF_LoopTrigger
            • Else - Actions
          • -------- - --------
          • Custom script: call RemoveLocation(udg_FRF_Loc[1])
          • Custom script: call RemoveLocation(udg_FRF_Loc[2])
  • Frost Field Loop
    • Events
    • Conditions
    • Actions
      • Set FRF_Spell_ID = 0
      • For each (Integer FRF_LoopInt) from 1 to FRF_SpellCount, do (Actions)
        • Loop - Actions
          • Set FRF_Spell_ID = FRF_NodeNext[FRF_Spell_ID]
          • -------- - --------
          • -------- Stage 1 - Handle movements (Outwards & Inwards) --------
          • -------- Stage 2 - Recycle --------
          • -------- - --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • FRF_Stage[FRF_Spell_ID] Equal to 1
            • Then - Actions
              • -------- Check if projectile is around --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Number of units in FRF_ProjectileGroup[FRF_Spell_ID]) Not equal to 0
                • Then - Actions
                  • -------- Projectile's still around --------
                  • -------- Check if it has reached the maximum distance and the direction is outward --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • FRF_RelativeOffset[FRF_Spell_ID] Less than FRF_TargetDistance[FRF_Spell_ID]
                      • FRF_IsOutward[FRF_Spell_ID] Equal to True
                    • Then - Actions
                      • -------- It has not; Increase relative offset --------
                      • Set FRF_RelativeOffset[FRF_Spell_ID] = (FRF_RelativeOffset[FRF_Spell_ID] + FRF_Velocity[FRF_Spell_ID])
                    • Else - Actions
                      • -------- It has; it should now moves Inward, reset the DamageGroup so it can be reused --------
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • FRF_IsOutward[FRF_Spell_ID] Equal to True
                        • Then - Actions
                          • Unit Group - Remove all units from FRF_DamagedGroup[FRF_Spell_ID]
                          • Set FRF_IsOutward[FRF_Spell_ID] = False
                        • Else - Actions
                      • -------- Check if user wants the projectiles to return aka move inwards --------
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • FRF_ProjectileReturn Equal to True
                        • Then - Actions
                          • -------- They do; Check if the projectiles are still far away --------
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • FRF_RelativeOffset[FRF_Spell_ID] Less than or equal to FRF_StartOffset[FRF_Spell_ID]
                            • Then - Actions
                              • -------- The projectiles are close; Proceed to Stage 2 and skip the remaining actions --------
                              • Set FRF_Stage[FRF_Spell_ID] = 2
                              • Skip remaining actions
                            • Else - Actions
                              • -------- Still far away; Decrease the relative offset --------
                              • Set FRF_RelativeOffset[FRF_Spell_ID] = (FRF_RelativeOffset[FRF_Spell_ID] - FRF_Velocity[FRF_Spell_ID])
                        • Else - Actions
                          • -------- They don't; Immediately proceed to Stage 2 and skip the remaining actions --------
                          • Set FRF_Stage[FRF_Spell_ID] = 2
                          • Skip remaining actions
                  • -------- Store the location of caster --------
                  • Set FRF_Loc[1] = (Position of FRF_Caster[FRF_Spell_ID])
                  • -------- Enumerate the dummy units --------
                  • Unit Group - Pick every unit in FRF_ProjectileGroup[FRF_Spell_ID] and do (Actions)
                    • Loop - Actions
                      • Set FRF_TempUnit = (Picked unit)
                      • -------- Store the location for dummy movement --------
                      • Set FRF_Loc[2] = (FRF_Loc[1] offset by FRF_RelativeOffset[FRF_Spell_ID] towards (Facing of FRF_TempUnit) degrees)
                      • -------- Store the location for path check --------
                      • Set FRF_Loc[3] = (FRF_Loc[2] offset by FRF_PathCheckOffset[FRF_Level[FRF_Spell_ID]] towards (Facing of FRF_TempUnit) degrees)
                      • -------- Do path check --------
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • FRF_PathingCheck Equal to 1
                        • Then - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (Terrain pathing at FRF_Loc[3] of type Walkability is off) Equal to True
                            • Then - Actions
                              • -------- It is not pathable; Remove projectile from the group --------
                              • Unit Group - Remove FRF_TempUnit from FRF_ProjectileGroup[FRF_Spell_ID]
                              • -------- Destroy the projectile --------
                              • Custom script: call SetWidgetLife(udg_FRF_TempUnit, 0.405)
                              • -------- Clean up --------
                              • Custom script: call RemoveLocation(udg_FRF_Loc[2])
                              • Custom script: call RemoveLocation(udg_FRF_Loc[3])
                              • -------- Skip moving the dummy unit --------
                              • Skip remaining actions
                            • Else - Actions
                        • Else - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • FRF_PathingCheck Greater than 1
                            • Then - Actions
                              • -------- Run PurgeandFire Check Walkability System --------
                              • Set CP_Point = FRF_Loc[3]
                              • Trigger - Run Check Walkability <gen> (ignoring conditions)
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • CP_PointIsWalkable Equal to True
                                • Then - Actions
                                  • -------- Pathable; do nothing --------
                                • Else - Actions
                                  • -------- It is not pathable; Remove projectile from the group --------
                                  • Unit Group - Remove FRF_TempUnit from FRF_ProjectileGroup[FRF_Spell_ID]
                                  • -------- Destroy the projectile --------
                                  • Custom script: call SetWidgetLife(udg_FRF_TempUnit, 0.405)
                                  • -------- Clean up --------
                                  • Custom script: call RemoveLocation(udg_FRF_Loc[2])
                                  • Custom script: call RemoveLocation(udg_FRF_Loc[3])
                                  • -------- Skip moving the dummy unit --------
                                  • Skip remaining actions
                            • Else - Actions
                      • -------- Check if the projectiles are still within the map borders --------
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (X of FRF_Loc[2]) Less than FRF_MapMaxX
                          • (X of FRF_Loc[2]) Greater than FRF_MapMinX
                          • (Y of FRF_Loc[2]) Less than FRF_MapMaxY
                          • (Y of FRF_Loc[2]) Greater than FRF_MapMinY
                        • Then - Actions
                          • -------- Move the dummy unit --------
                          • Custom script: call SetUnitX(udg_FRF_TempUnit, GetLocationX(udg_FRF_Loc[2]) )
                          • Custom script: call SetUnitY(udg_FRF_TempUnit, GetLocationY(udg_FRF_Loc[2]) )
                        • Else - Actions
                          • -------- It is outside the boundary; Remove projectile from the group --------
                          • Unit Group - Remove FRF_TempUnit from FRF_ProjectileGroup[FRF_Spell_ID]
                          • -------- Destroy the projectile --------
                          • Custom script: call SetWidgetLife(udg_FRF_TempUnit, 0.405)
                          • -------- Clean up --------
                          • Custom script: call RemoveLocation(udg_FRF_Loc[2])
                          • Custom script: call RemoveLocation(udg_FRF_Loc[3])
                          • -------- Skip moving the dummy unit --------
                          • Skip remaining actions
                      • -------- Enumerate units within range --------
                      • Custom script: set bj_wantDestroyGroup = true
                      • Unit Group - Pick every unit in (Units within FRF_Aoe[FRF_Level[FRF_Spell_ID]] of FRF_Loc[2]) and do (Actions)
                        • Loop - Actions
                          • Set FRF_PickedUnit = (Picked unit)
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • (FRF_PickedUnit belongs to an enemy of FRF_Owner[FRF_Spell_ID]) Equal to True
                              • (FRF_PickedUnit is in FRF_DamagedGroup[FRF_Spell_ID]) Equal to False
                              • (FRF_PickedUnit is alive) Equal to True
                            • Then - Actions
                              • -------- Total damage = ( RelativeOffset / (TargetDistance) ) * Max Damage --------
                              • Set FRF_TotalDamage[FRF_Spell_ID] = ((FRF_RelativeOffset[FRF_Spell_ID] / FRF_TargetDistance[FRF_Spell_ID]) x FRF_MaxDamage[FRF_Level[FRF_Spell_ID]])
                              • -------- If Total Damage is lower than expected Minimum Damage, then use Minimum Damage instead --------
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • FRF_TotalDamage[FRF_Spell_ID] Less than FRF_MinDamage[FRF_Level[FRF_Spell_ID]]
                                • Then - Actions
                                  • Set FRF_TotalDamage[FRF_Spell_ID] = FRF_MinDamage[FRF_Level[FRF_Spell_ID]]
                                • Else - Actions
                                  • -------- If Total Damage is greater than expected Maximum Damage, then use Maximum Damage instead --------
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • FRF_TotalDamage[FRF_Spell_ID] Greater than FRF_MaxDamage[FRF_Spell_ID]
                                    • Then - Actions
                                      • Set FRF_TotalDamage[FRF_Spell_ID] = FRF_MaxDamage[FRF_Level[FRF_Spell_ID]]
                                    • Else - Actions
                              • -------- Check whether the end user wants to use Fixed Damage or not --------
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • FRF_FixedDamage Equal to True
                                • Then - Actions
                                  • Set FRF_TotalDamage[FRF_Spell_ID] = FRF_Damage[FRF_Level[FRF_Spell_ID]]
                                • Else - Actions
                              • -------- Check whether it's a structure or not --------
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • (FRF_PickedUnit is A structure) Equal to False
                                • Then - Actions
                                  • -------- Create and destroy special effect --------
                                  • Special Effect - Create a special effect attached to the FRF_SFXAttachmentPoint[1] of FRF_PickedUnit using FRF_EffectStr[1]
                                  • Special Effect - Destroy (Last created special effect)
                                  • -------- Deal damage --------
                                  • Unit - Cause FRF_Caster[FRF_Spell_ID] to damage FRF_PickedUnit, dealing FRF_TotalDamage[FRF_Spell_ID] damage of attack type FRF_AtkType and damage type FRF_DmgType
                                  • -------- Check if end user wants to slow enemy units --------
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • FRF_SlowEffect Equal to True
                                      • (FRF_PickedUnit is in FRF_SlowedGroup[FRF_Spell_ID]) Equal to False
                                    • Then - Actions
                                      • -------- Match the level first --------
                                      • Unit - Set level of FRF_DummyAbility for FRF_DummyCaster to FRF_Level[FRF_Spell_ID]
                                      • -------- Slow them --------
                                      • Custom script: call IssueTargetOrderById( udg_FRF_DummyCaster, udg_FRF_DummyOrderID, udg_FRF_PickedUnit )
                                      • -------- Prevent them from being frozen more than once --------
                                      • Unit Group - Add FRF_PickedUnit to FRF_SlowedGroup[FRF_Spell_ID]
                                    • Else - Actions
                                • Else - Actions
                                  • -------- It is a structure but does user want to deal damage to building? --------
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • FRF_DamageBuilding Equal to True
                                    • Then - Actions
                                      • Set FRF_TotalDamage[FRF_Spell_ID] = (FRF_TotalDamage[FRF_Spell_ID] x FRF_BuildingDmgFactor[FRF_Level[FRF_Spell_ID]])
                                      • -------- Deal damage --------
                                      • Unit - Cause FRF_Caster[FRF_Spell_ID] to damage FRF_PickedUnit, dealing FRF_TotalDamage[FRF_Spell_ID] damage of attack type FRF_AtkType and damage type FRF_DmgType
                                    • Else - Actions
                              • -------- Prevent them from getting damaged more than once --------
                              • Unit Group - Add FRF_PickedUnit to FRF_DamagedGroup[FRF_Spell_ID]
                            • Else - Actions
                      • -------- Enumerate destructibles within range --------
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • FRF_KillTrees Equal to True
                        • Then - Actions
                          • Destructible - Pick every destructible within FRF_DestRadius[FRF_Level[FRF_Spell_ID]] of FRF_Loc[2] and do (Actions)
                            • Loop - Actions
                              • Set FRF_TempDest = (Picked destructible)
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • (Current life of FRF_TempDest) Greater than 0.40
                                • Then - Actions
                                  • Custom script: call IssueTargetOrder( udg_FRF_TreeDebrisKiller, udg_FRF_TreeOrDebris, udg_FRF_TempDest )
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • (Current order of FRF_TreeDebrisKiller) Equal to (Order(FRF_TreeOrDebris))
                                    • Then - Actions
                                      • Destructible - Set life of FRF_TempDest to 0.40
                                    • Else - Actions
                                  • Unit - Order FRF_TreeDebrisKiller to Stop
                                • Else - Actions
                        • Else - Actions
                      • -------- Clean up --------
                      • Custom script: call RemoveLocation(udg_FRF_Loc[2])
                      • Custom script: call RemoveLocation(udg_FRF_Loc[3])
                  • -------- Clean up --------
                  • Custom script: call RemoveLocation(udg_FRF_Loc[1])
                • Else - Actions
                  • -------- No projectile's around; Immediately proceed to stage 2 --------
                  • Set FRF_Stage[FRF_Spell_ID] = 2
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • FRF_Stage[FRF_Spell_ID] Equal to 2
                • Then - Actions
                  • -------- The Instance is finished; Kill the dummy unit --------
                  • Unit Group - Pick every unit in FRF_ProjectileGroup[FRF_Spell_ID] and do (Actions)
                    • Loop - Actions
                      • Set FRF_TempUnit = (Picked unit)
                      • Custom script: call SetWidgetLife(udg_FRF_TempUnit, 0.405)
                  • Unit Group - Remove all units from FRF_ProjectileGroup[FRF_Spell_ID]
                  • -------- Recycle --------
                  • Custom script: call DestroyGroup( udg_FRF_DamagedGroup[udg_FRF_Spell_ID] )
                  • Custom script: set udg_FRF_DamagedGroup[udg_FRF_Spell_ID] = null
                  • Custom script: call DestroyGroup( udg_FRF_SlowedGroup[udg_FRF_Spell_ID] )
                  • Custom script: set udg_FRF_SlowedGroup[udg_FRF_Spell_ID] = null
                  • Custom script: call DestroyGroup( udg_FRF_ProjectileGroup[udg_FRF_Spell_ID] )
                  • Custom script: set udg_FRF_ProjectileGroup[udg_FRF_Spell_ID] = null
                  • -------- - --------
                  • Set FRF_RecycledStack[FRF_RecycledSize] = FRF_Spell_ID
                  • Set FRF_RecycledSize = (FRF_RecycledSize + 1)
                  • Set FRF_NodeNext[FRF_NodePrev[FRF_Spell_ID]] = FRF_NodeNext[FRF_Spell_ID]
                  • Set FRF_NodePrev[FRF_NodeNext[FRF_Spell_ID]] = FRF_NodePrev[FRF_Spell_ID]
                  • -------- - --------
                  • Set FRF_SpellCount = (FRF_SpellCount - 1)
                  • -------- Turn off trigger when no instance left --------
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • FRF_SpellCount Equal to 0
                    • Then - Actions
                      • Trigger - Turn off FRF_LoopTrigger
                    • Else - Actions
                      • -------- There's still instance going; do not turn off --------
                • Else - Actions

Credits
PurgeandFire - For his awesome system - Check Walkability

Changelog
v1.0.2
  • Optimized the code
v1.0.1
  • Added check for map borders
v1.0
  • First Public Release
Previews
Contents

Frost Field v1.0.2 (Map)

Reviews
Antares
You're removing the location FRF_Loc[3] twice during the pathing checks. When you assign CP_Point to that location, you're not cloning the location, you're just assigning a new variable pointing to the same one, so destroying CP_Point is not...

Rheiko

Spell Reviewer
Level 26
Joined
Aug 27, 2013
Messages
4,214
Cool :) Hi there.
Yo! long time no see!

Edit:
I made a small update. I noticed I overwrote FRF_Loc[1] at config trigger twice, lol. Clumsy me. Glad you didn't notice, @Daffa. =P
I also replaced Kill FRF_TempUnit to call SetUnitWidget because I heard the former has a slightly lower performance.
I tried to compare them and it does feel like SetUnitWidget is better but it could be just a placebo effect. I don't know.
I wish I could do a benchmark but I don't know where to start. So, I'll just leave it that way for now.
 
Last edited:
Level 13
Joined
Mar 29, 2012
Messages
542
Yo! long time no see!

Edit:
I made a small update. I noticed I overwrote FRF_Loc[1] at config trigger twice, lol. Clumsy me. Glad you didn't notice, @Daffa. =P
I also replaced Kill FRF_TempUnit to call SetUnitWidget because I heard the former has a slightly lower performance.
I tried to compare them and it does feel like SetUnitWidget is better but it could be just a placebo effect. I don't know.
I wish I could do a benchmark but I don't know where to start. So, I'll just leave it that way for now.
Looks like you are having good time making spells now. :D
I forgot how to review a submission hehe.

Maybe you could try writing this in Jass since you are using a lot of custom scripts.
It's somehow easy after you try learning a couple of lines, and it also boosts performance a bit. :)
 

Rheiko

Spell Reviewer
Level 26
Joined
Aug 27, 2013
Messages
4,214
Looks like you are having good time making spells now. :D
I forgot how to review a submission hehe.
Haha, yes! I got some spell ideas after coming back. Also trying to revive the spell section while I'm working on them.
No worries, I'm sure they'll be coming back to you in an instant!

Maybe you could try writing this in Jass since you are using a lot of custom scripts.
It's somehow easy after you try learning a couple of lines, and it also boosts performance a bit. :)
I'm planning to do so in the near future. For the time being, I prefer to work with something I know better which is GUI.
But I have a JASS spell coming up, although the progress is rather slow (because I suck), you can look forward to it. =)
 

Rheiko

Spell Reviewer
Level 26
Joined
Aug 27, 2013
Messages
4,214
Ah probably because it goes out of bounds. You can turn on the pathing check to prevent this. Although it will kill the projectile early if it collides with doodads and stuffs. Are you sure you really need 10000?

I'll add a check for map bound in the next version. Thanks for the feedback.

Edit:
I made a quick update to v1.0.1 to fix the crashing issue. You can try downloading the new version, sir @tlstkwjr.
You don't have to turn on the pathing check. It should work now. Hopefully.
I will try to further optimize the code down the line, the more I look at it, it looks like a complete mess from my perspective, lol.
 
Last edited:
You're removing the location FRF_Loc[3] twice during the pathing checks. When you assign CP_Point to that location, you're not cloning the location, you're just assigning a new variable pointing to the same one, so destroying CP_Point is not necessary.

The fact that you copied this giant block of code just for the reverse direction is a bit wasteful. Surely there's a simple way to do that with one such block.

If you want to increase the performance of the spell, you could do the following:
  • Move FRF_Loc[1] = (Position of FRF_Caster[FRF_Spell_ID] outside of the loop over dummy units, as it's always the same location.
  • Reduce the number of array lookups by presaving certain values that are looked up often, such as FRF_Aoe[FRF_Level[FRF_Spell_ID]], to non-array globals. This will also make your triggers more readable.
  • Move the (FRF_PickedUnit is alive) Equal to True condition to the bottom, because it's the least likely to return false.
Certainly not an extensive list.

It's a shame that you have to use dummy units for this.
oz5zzyxqru471-1.png

But I guess that makes it viable for 1.26 users. If you're a 1.31 or Reforged user, consider replacing the dummy units with special effects to improve performance.

Apart from all that, the config is well-done, the spell looks great, and there are no glaring issues.

Approved
 

Rheiko

Spell Reviewer
Level 26
Joined
Aug 27, 2013
Messages
4,214
You're removing the location FRF_Loc[3] twice during the pathing checks. When you assign CP_Point to that location, you're not cloning the location, you're just assigning a new variable pointing to the same one, so destroying CP_Point is not necessary.
Really? I didn't know that. I thought it would leak. So for this case, removing one of them should be sufficient?

The fact that you copied this giant block of code just for the reverse direction is a bit wasteful. Surely there's a simple way to do that with one such block.

If you want to increase the performance of the spell, you could do the following:
  • Move FRF_Loc[1] = (Position of FRF_Caster[FRF_Spell_ID] outside of the loop over dummy units, as it's always the same location.
  • Reduce the number of array lookups by presaving certain values that are looked up often, such as FRF_Aoe[FRF_Level[FRF_Spell_ID]], to non-array globals. This will also make your triggers more readable.
  • Move the (FRF_PickedUnit is alive) Equal to True condition to the bottom, because it's the least likely to return false.
Certainly not an extensive list.
I completely agree with you, thanks for letting me know!
and Thank you so much for the approval!
 
Really? I didn't know that. I thought it would leak. So for this case, removing one of them should be sufficient?
There are primitive types and reference types. Primitive types (strings ,integers, reals, and booleans) store a value, so changing one does not change a copy.
JASS:
set A = 5
set B = A
set A = 3
//B is still 5.
All other variable types are reference types. They do not store a value, but are a pointer to an object somewhere behind the curtains, so if you're changing the underlying object, it will affect all variables referencing it.
JASS:
set Location1 = Location(0, 0)
set Location2 = Location1
call MoveLocation(Location1, 1000, 1000)
//Location2 is now at (1000, 1000)
 

Rheiko

Spell Reviewer
Level 26
Joined
Aug 27, 2013
Messages
4,214
Updated to v1.0.2 - Code Optimization

I reduced the block of code so now it only does the movement once. If the user wants the projectiles to return, only the offset will change.
I can feel the wonderful performance increase! (I have a potato machine) Felt good.
I also did what Antares suggested. Though I'm still not sure what's the best way to do this:
Reduce the number of array lookups by presaving certain values that are looked up often, such as FRF_Aoe[FRF_Level[FRF_Spell_ID]], to non-array globals. This will also make your triggers more readable.

@Antares, if you don't mind, would you please review the updated version? =)
 
Updated to v1.0.2 - Code Optimization

I reduced the block of code so now it only does the movement once. If the user wants the projectiles to return, only the offset will change.
I can feel the wonderful performance increase! (I have a potato machine) Felt good.
I also did what Antares suggested. Though I'm still not sure what's the best way to do this:


@Antares, if you don't mind, would you please review the updated version? =)
You can create another variable as non-array variant and set it at the start of cast or loop, depending when the value is needed.

Set FRF_SpellAOE = FRF_Aoe[FRF_Level[FRF_Spell_ID]]

Then use the FRF_SpellAOE in place of the array version for that trigger portion
 
Maybe I should put it inside the loop, yeah?
I'm afraid the value will change when a multi-cast happens if I put it in the cast trigger.
Easiest way: place it next to whenever the value for the arrayed version is altered.

Say:
(Variable value change)
Set variable to non-array here
 
Level 2
Joined
Jan 9, 2025
Messages
7
Very cool ability. Is there a way to add a pause before returning the projectiles to their original location?
I am trying to insert a third stage (in between the first and the second) to make projectiles stop for a bit at the outward position.

Edit: I added a simple condition at the ~end of the first stage yet somehow it doesnt feel right in the game.
1736705892364.png
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Very cool ability. Is there a way to add a pause before returning the projectiles to their original location?
I am trying to insert a third stage (in between the first and the second) to make projectiles stop for a bit at the outward position.
If you dig through the Frost Field Loop trigger you can find this comment (a note from the author) and the Action that handles the returning movement:
  • Else - Actions
    • -------- Still far away; Decrease the relative offset --------
    • Set FRF_RelativeOffset[FRF_Spell_ID] = (FRF_RelativeOffset[FRF_Spell_ID] - FRF_Velocity[FRF_Spell_ID])
You could add a delay here by introducing a new Real array variable that will keep track of elapsed time.

This should work:
  • Else - Actions
    • -------- Still far away; Decrease the relative offset --------
    • Set FRF_Return_Delay[FRF_Spell_ID] = (FRF_Return_Delay[FRF_Spell_ID] - FRF_Interval)
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • FRF_Return_Delay[FRF_Spell_ID] Less than or equal to 0.01
      • Then - Actions
        • Set FRF_RelativeOffset[FRF_Spell_ID] = (FRF_RelativeOffset[FRF_Spell_ID] - FRF_Velocity[FRF_Spell_ID])
      • Else - Actions
FRF_Return_Delay[] is the new variable.

You also need to Set this variable inside of the Frost Field Cast trigger. You can add it after the other configurable variables:
  • ...
  • Set FRF_IsOutward[FRF_Spell_ID] = True
  • Set FRF_Return_Delay[FRF_Spell_ID] = 1.50
Now the projectiles will pause for 1.50 seconds, adjust this to whatever value you want.
 
Last edited:
Level 2
Joined
Jan 9, 2025
Messages
7
If you dig through the Frost Field Loop trigger you can find this comment (a note from the author) and the Action that handles the returning movement:
  • Else - Actions
    • -------- Still far away; Decrease the relative offset --------
    • Set FRF_RelativeOffset[FRF_Spell_ID] = (FRF_RelativeOffset[FRF_Spell_ID] - FRF_Velocity[FRF_Spell_ID])
You could add a delay here by introducing a new Real array variable that will keep track of elapsed time.

This should work:
  • Else - Actions
    • -------- Still far away; Decrease the relative offset --------
    • Set FRF_Return_Delay[FRF_Spell_ID] = (FRF_Return_Delay[FRF_Spell_ID] - FRF_Interval)
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • FRF_Return_Delay[FRF_Spell_ID] Less than or equal to 0.01
      • Then - Actions
        • Set FRF_RelativeOffset[FRF_Spell_ID] = (FRF_RelativeOffset[FRF_Spell_ID] - FRF_Velocity[FRF_Spell_ID])
      • Else - Actions
FRF_Return_Delay[] is the new variable.

You also need to Set this variable inside of the Frost Field Cast trigger. You can add it after the other configurable variables:
  • ...
  • Set FRF_IsOutward[FRF_Spell_ID] = True
  • Set FRF_Return_Delay[FRF_Spell_ID] = 1.50
Now the projectiles will pause for 1.50 seconds, adjust this to whatever value you want.
It works well, thanks.

Now a different problem occurred.
The projectiles move with the caster while "paused". I would like them to stay in place and then move towards the caster when the FRF_Return_Delay runs out. Right now they teleport to a position around the caster and then start moving. I tried to work with the variables a bit to no avail.
I presume this is where i need to make certain changes...
1736720534035.png
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Making such a change requires fundamentally altering how this spell calculates the position of all of the frost novas. Each nova's position is stored as a "distance offset" relative to the caster rather than absolute XY coordinates or even distance/angle, so to place them temporarily on the ground requires decoupling the projectiles from the caster. After pausing the novas, the math to get them to return to the caster from their now-decoupled position is not exceedingly complex but does need to be worked out because otherwise they will either teleport or not all arrive at the caster's position at the same time (and the direction/angle they all have to move in the meantime is unique for each nova).

The code just isn't set up to do that easily, though it could be modified with a lot of effort. I don't think it's worth it. You could try to fake it by using a dummy caster to cast this spell instead of letting the actual caster use it directly. Then you can manipulate the position of the 'true' dummy caster to place the novas where you want them to be (lock dummy in place for N seconds, then make another periodic section to quickly move the dummy from that position to the caster's current position).
 
Top