• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[GUI-Friendly] Unit Fusion System

Unit Fusion System is a System handling Spells making unit Fuse/tribute for summoning.

Features:
  • Define own custom Conditions for material (like over 50% HP)
  • Supports Target Point/Unit/non-Target Casts
  • Instant Fusions
  • FadeIn Fusions (Fusion Result starts with 100% transparency and become more visible over time)
  • PullTogehter Fusions (the Fusion parts will be pulled together)
  • PullTogehter and FadeIn can be combined
  • "Quite easy" adding additional Fusion/Sacrifice Spells
  • Kill/Remove Merging Unita and pull together a new unitType
  • Supports Hero Fusions.
  • Supports Timed Fusions, with defusions or simple timed life.
JASS:
//Unit Fusion System V8a
//by Tasyen
//======================================
//API
//======================================
//   function UnitDefusion takes unit u, boolean killed, integer indexFusion returns nothing
//       tries to defuse an unit u, killed, or Defusionen Index
//       indexFusion is faster, but most times you won't know the DefusionIndex
//       If you do not know the defusion index call it with -1

//   function UnitFusionStartFadeIn takes unit u, real dur, real hiddenStateAfter, string sfx, boolean immunDuringFade returns nothing
//       pauses an unit and makes it transparent both will be lost over time.
//       the pausing will be reapplied every timeout of "UnitFusionTimerSpeed"
//       - u the unit
//       - dur how long the process will take in seconds.
//       - hiddenStateAfter this much % will the unit stay transparent after finishing the process (expects a number from 0 to 100)
//       - sfx the modell which will displayed during the fadein
//       - immunDuringFade if true the unit becomes invulnurable during fadein.
//       - This will throw an FadeInFinish-Event with Fusion_Event_SpellData = -1
//       - To Manipulate this used Fusion_Object use udg_Fusion_Current_CreatedObject right after calling it.

//   function UnitFusionTimerStart takes nothing returns nothing
//       Activaded automatacly normally.
//       will start the System-timer, withthat the fusion behaviour overall.
//       use it if you stopped the timer manually.
//       "udg_Fusion_System_TimerDisabled = true" will prevent starting the timer that way.

//======================================
//   Fusion_Data_HeroStatsMod:
//    0 uses Results default
//    1 uses Average of fusion members
//    2 uses sum of fusion members

//   Fusion_Data_DefuseAfterDurHP is a % value between 0 and 1 or -1 to use Fusions %

//   Fusion_Event
//
//       1 = NonHero Fusion
//       2 = Hero Fusion
//       3 = FadeInFinish
//       -1 = Defusion

//       Fusion_Event_Fusion = the Fusion
//       Fusion_Event_Source = the Source casted fusion spell.
//       Fusion_Event_Group  = the merged/demerged Units; is does not contain substitutes.
//       Fusion_Event_SpellData = the data index of the used Spell for fusing.
//       Fusion_Event_SpellLevel = the level with which the fusion was casted.

//       Fusion_Event_SpellObject = the FusionObjectIndex with this you an manipulate data inside an event, most time unneeded.


// Arrays here generally start with 1

//======================================
//Constants
//======================================
//Will the Fusion will be automatacly added to the selection of the owning Player?
constant function UnitFusionAutoSelect takes nothing returns boolean
   return true
endfunction
constant function UnitFusionErrorMsg takes nothing returns string
   return "Misses:"
endfunction   

//ErrorMessage for each Missing Wildcard Unit.
//Looks like this:
//Misses:
// - Peon
// - Warlock
// - WildCard-Unit
// - WildCard-Unit
//
constant function UnitFusionWildCard takes nothing returns string
   return " - WildCard-Unit"
endfunction

//How much is an unit transparent while flying 255 = 0%, 0 = 100%
constant function UnitFusionFade takes nothing returns integer
   return 128
endfunction

//This is used to get units beeing at the edge of the AoE Indikator of a spell, beeing inside wither their hitbox.
constant function UnitMaxColison takes nothing returns real
   return 200.0
endfunction


//Intervale used for fadingIn/Pulling
constant function UnitFusionTimerSpeed  takes nothing returns real
   return 0.05
endfunction   

//How long one does see what is missing to fuse in seconds
constant function UnitFusionErrorDur takes nothing returns real
   return 11.0
endfunction

constant function UnitFusionEventNonHero takes nothing returns real
   return 1.0
endfunction
constant function UnitFusionEventHero takes nothing returns real
   return 2.0
endfunction
constant function UnitFusionEventFadeInFinish takes nothing returns real
   return 3.0
endfunction
constant function UnitFusionEventDefusion takes nothing returns real
   return -1.0
endfunction
//The Fusion_Current_ItemStack usage for Defusions starts at Offset.
constant function UnitDefusionItemOffset takes nothing returns integer
   return 200
endfunction
//Used by Fusion_Object_Type
constant function UnitFusionTypePull takes nothing returns string
   return "UnitFusionPulled"
endfunction
constant function UnitFusionTypeDefusion takes nothing returns string
   return "UnitDefusionMonitor"
endfunction
constant function UnitFusionTypeFadeIn takes nothing returns string
   return "UnitFusionFadeIn"
endfunction

function StringIsEmpty takes string s returns boolean
   return s == null or s == ""
endfunction
//======================================
//Code - Starts
//======================================
//This will return the Entering Index of the new Object
//And creates an pointer pointing at it.
//The LastUsed ObjectIndex is saved in udg_Fusion_Current_CreatedObject.
function UnitFusionInserObject takes nothing returns integer
   local integer returnValue
   if  udg_Fusion_System_ReuseSize > 0 then
       set returnValue = udg_Fusion_System_Reuse[udg_Fusion_System_ReuseSize]
       set udg_Fusion_System_ReuseSize = udg_Fusion_System_ReuseSize - 1
   else
       set udg_Fusion_System_ObjectSize = udg_Fusion_System_ObjectSize + 1
       set returnValue = udg_Fusion_System_ObjectSize
   endif
   set udg_Fusion_Current_CreatedObject = returnValue
   //Create Pointer with used Object.
   set udg_Fusion_System_Pointer_Last = udg_Fusion_System_Pointer_Last + 1
   set udg_Fusion_System_Pointer[udg_Fusion_System_Pointer_Last] = returnValue
   set udg_Fusion_Object_Pointer[returnValue] = udg_Fusion_System_Pointer_Last
   set udg_Fusion_System_Pointer_Finished[udg_Fusion_System_Pointer_Last] = false
   return returnValue
endfunction


//Will give you the index of an free spot in the Defusion Items.
function UnitDefusionItemGetFreeSpot takes nothing returns integer
   local integer returnValue
   if udg_Fusion_Item_DefReuseSize > 0 then
       set returnValue = udg_Fusion_Item_DefReuse[udg_Fusion_Item_DefReuseSize]
       set udg_Fusion_Item_DefReuseSize = udg_Fusion_Item_DefReuseSize - 1
   else
       set udg_Fusion_Item_DefSize = udg_Fusion_Item_DefSize + 1
       set returnValue = udg_Fusion_Item_DefSize
   endif
   return returnValue
endfunction

// Will Create a new Fusion Object
//Cloning Fusion, Source,LEvel,Owner,Data, add all Units to the a group
function UnitFusionCloneObject takes integer sourceObject returns integer
   local integer indexObject = UnitFusionInserObject()
   if udg_Fusion_Object_Loc[indexObject] == null then
       set udg_Fusion_Object_Loc[indexObject] = Location(GetLocationX(udg_Fusion_Object_Loc[sourceObject]),GetLocationY(udg_Fusion_Object_Loc[sourceObject]))
   else
       call MoveLocation(udg_Fusion_Object_Loc[indexObject],GetLocationX(udg_Fusion_Object_Loc[sourceObject]),GetLocationY(udg_Fusion_Object_Loc[sourceObject]))
   endif
   if udg_Fusion_Object_Group[indexObject] == null then
       set udg_Fusion_Object_Group[indexObject] = CreateGroup()
   else
       call GroupClear(udg_Fusion_Object_Group[indexObject])
   endif
   set udg_Fusion_Object_Fusion[indexObject] = udg_Fusion_Object_Fusion[sourceObject]
   set udg_Fusion_Object_Source[indexObject] = udg_Fusion_Object_Source[sourceObject]
   set udg_Fusion_Object_SpellLevel[indexObject] = udg_Fusion_Object_SpellLevel[sourceObject]

   set udg_Fusion_Object_DataIndex[indexObject] = udg_Fusion_Object_DataIndex[sourceObject]
   set udg_Fusion_Object_Effect[indexObject] = null
   call GroupAddGroup(udg_Fusion_Object_Group[sourceObject],udg_Fusion_Object_Group[indexObject])
//   call printCloneGroups(udg_Fusion_Object_Group[sourceObject],udg_Fusion_Object_Group[indexObject])
   return indexObject
endfunction

function UnitFusionItemStackRemove takes integer indexChoosen returns nothing
   //That makes no sense
   if indexChoosen == -1 then
       return
   endif
   if indexChoosen != udg_Fusion_Item_Size then
       set udg_Fusion_Item_Item[indexChoosen] = udg_Fusion_Item_Item[udg_Fusion_Item_Size]
       set udg_Fusion_Item_Owner[indexChoosen] =  udg_Fusion_Item_Owner[udg_Fusion_Item_Size]
   endif
   set udg_Fusion_Item_Item[udg_Fusion_Item_Size] = null
   set udg_Fusion_Item_Owner[udg_Fusion_Item_Size] = null
   set udg_Fusion_Item_Size = udg_Fusion_Item_Size - 1   
endfunction

//Returns the Item with the highest Level from the stack.
//removeReturned = true will take it from the stack and countdown.
function UnitFusionBestItem takes nothing returns integer
   local integer LoopA = 1
   local integer indexChoosen = -1
   local integer level = 0
   //Find one with highestLevel
   loop
       exitwhen LoopA > udg_Fusion_Item_Size
       if level <  GetItemLevel(udg_Fusion_Item_Item[LoopA]) then
           set indexChoosen = LoopA
           set level = GetItemLevel(udg_Fusion_Item_Item[LoopA])
       endif
       set LoopA = LoopA + 1
   endloop
   return indexChoosen
endfunction

//Returns the ItemTypeId of the item with the highest Level from the stack.
//removeReturned = true will take it from the stack and countdown.
//might be unneeded
function UnitFusionBestItemType takes boolean removeReturned returns integer
   local integer LoopA = 1
   local integer indexChoosen = -1
   local integer itemId = 0
   local integer level = 0
   //Find one with highestLevel
   loop
       exitwhen LoopA > udg_Fusion_Item_Size
       if level <  GetItemLevel(udg_Fusion_Item_Item[LoopA]) then
           set indexChoosen = LoopA
           set itemId = GetItemTypeId(udg_Fusion_Item_Item[indexChoosen])
           set level = GetItemLevel(udg_Fusion_Item_Item[LoopA])
       endif
       set LoopA = LoopA + 1
   endloop
   //Reindex?
   if removeReturned and indexChoosen != -1 then
       if indexChoosen != udg_Fusion_Item_Size then
           set udg_Fusion_Item_Item[indexChoosen] = udg_Fusion_Item_Item[udg_Fusion_Item_Size]
           set udg_Fusion_Item_Owner[udg_Fusion_Item_Size] =  udg_Fusion_Item_Owner[udg_Fusion_Item_Size]
       else
           set udg_Fusion_Item_Item[udg_Fusion_Item_Size] = null
           set udg_Fusion_Item_Owner[udg_Fusion_Item_Size] = null
       endif
       set udg_Fusion_Item_Size = udg_Fusion_Item_Size - 1
   endif
   return itemId
endfunction

//Take items from the ItemStack and move them to the Unit u.
//Will give the hero the best (heighest Level) first
function UnitFusionGiveItems takes unit u returns nothing
   local integer creationCount = UnitInventorySize(u)
   local integer indexChoosen
   local integer indexCurrent
   local integer indexLast
   loop
       exitwhen 0 == udg_Fusion_Item_Size or creationCount == 0
       set indexChoosen = UnitFusionBestItem()
       if indexChoosen != 0 then
           call UnitAddItem (u, udg_Fusion_Item_Item[indexChoosen])
           call UnitFusionItemStackRemove(indexChoosen)
           set creationCount = creationCount - 1
       else
           set creationCount = 0
       endif   
   endloop   
endfunction

//Take items from the ItemStack and move them to the Unit u.
//Will give the hero the best (heighest Level) first
// This will save items and old owners.
function UnitFusionGiveItemsSave takes unit u, integer indexDefusion returns nothing
   local integer creationCount = UnitInventorySize(u)
   local integer indexChoosen
   local integer indexCurrent
   local integer indexLast
   //This is the End point of this Defusions items.
   set udg_Fusion_Object_Int3[indexDefusion] = UnitDefusionItemGetFreeSpot()
   set indexCurrent =udg_Fusion_Object_Int3[indexDefusion]
   loop
       exitwhen 0 == udg_Fusion_Item_Size or creationCount == 0
       set indexChoosen = UnitFusionBestItem()
       if indexChoosen != -1 then
           set indexLast = indexCurrent
           set indexCurrent = UnitDefusionItemGetFreeSpot()
           set udg_Fusion_Item_DefNext[indexLast] = indexCurrent
           set udg_Fusion_Item_Item[indexCurrent] = udg_Fusion_Item_Item[indexChoosen]
           set udg_Fusion_Item_Owner[indexCurrent] = udg_Fusion_Item_Owner[indexChoosen]
           //call BJDebugMsg(GetItemName(udg_Fusion_Item_Item[indexCurrent])+ GetHeroProperName(udg_Fusion_Item_Owner[indexCurrent]))
           call UnitAddItem (u, udg_Fusion_Item_Item[indexChoosen])
           call UnitFusionItemStackRemove(indexChoosen)
           
           set creationCount = creationCount - 1
       else   
           set creationCount = 0   
       endif
   endloop   
   //finish the list
   set udg_Fusion_Item_DefNext[indexCurrent] = -1
endfunction

//This Loads all Items of the unit into the stack.
//Will Count up udg_Fusion_Item_Size
function UnitFusionLoadInventory takes unit u returns nothing
   local integer LoopInventory = 0
   //Gain Items
   if UnitInventorySize(u) != 0 then
       loop
           exitwhen LoopInventory == UnitInventorySize(u)
           if UnitItemInSlot(u, LoopInventory) != null then
               set udg_Fusion_Item_Size = udg_Fusion_Item_Size + 1
               set udg_Fusion_Item_Item[udg_Fusion_Item_Size] =  UnitItemInSlot(u, LoopInventory)
               set udg_Fusion_Item_Owner[udg_Fusion_Item_Size] =  u
           endif   
           set LoopInventory = LoopInventory + 1
       endloop
   endif
endfunction

//Overall timer
//Executes sub actions based on Type.
//Main Index for sub functions are
//   udg_Fusion_Current_Pointer the pointer
function UnitFusionTimer takes nothing returns nothing
   local integer LoopA = 1
   //call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,1,I2S(udg_Fusion_System_Pointer_Last))
   loop
       exitwhen LoopA > udg_Fusion_System_Pointer_Last
       if not udg_Fusion_System_Pointer_Finished[LoopA] then
           set udg_Fusion_Current_Pointer = LoopA
           set udg_Fusion_Current_Object = udg_Fusion_System_Pointer[LoopA]
           set udg_Fusion_Current_Data = udg_Fusion_Object_DataIndex[udg_Fusion_Current_Object]
           call ExecuteFunc(udg_Fusion_Object_Type[udg_Fusion_Current_Object])
       else
           //Mark DataRow reusable
           set udg_Fusion_System_ReuseSize = udg_Fusion_System_ReuseSize + 1
           set udg_Fusion_System_Reuse[udg_Fusion_System_ReuseSize] = udg_Fusion_System_Pointer[LoopA]
           //Pointer Reindex
           set udg_Fusion_Object_Pointer[udg_Fusion_System_Pointer[LoopA]] = udg_Fusion_System_Pointer_Last
           set udg_Fusion_System_Pointer_Finished[LoopA] = udg_Fusion_System_Pointer_Finished[udg_Fusion_System_Pointer_Last]
           set udg_Fusion_System_Pointer[LoopA] = udg_Fusion_System_Pointer[udg_Fusion_System_Pointer_Last]
           set udg_Fusion_System_Pointer_Last = udg_Fusion_System_Pointer_Last - 1
           set LoopA = LoopA - 1
       endif   
       set LoopA = LoopA + 1
   endloop
   if udg_Fusion_System_Pointer_Last == 0 then
       call PauseTimer(udg_Fusion_System_Timer)
   endif
endfunction

//This is called to (re)start the timer.
function UnitFusionTimerStart takes nothing returns nothing
   if not udg_Fusion_System_TimerDisabled then
       call TimerStart(udg_Fusion_System_Timer, UnitFusionTimerSpeed(),true, function UnitFusionTimer)
   endif
endfunction

function UnitDropInventory takes unit u returns nothing
   local integer LoopInventory = UnitInventorySize(u)
   if UnitInventorySize(u) != 0 then
       loop
           set LoopInventory = LoopInventory - 1
           exitwhen LoopInventory == -1
           call UnitRemoveItemFromSlot(u, LoopInventory)
       endloop
   endif   
endfunction

//This function will defusion an fusion unit.
//call it with index -1, if you don't know the Defusion index.
function UnitDefusion takes unit u, boolean killed, integer indexFusion returns nothing
   local integer LoopA = 1
   local integer LoopB
   local integer itemIndex
   local integer indexData
   local real x
   local real x2
   local real y
   local real y2
   local real angleInc
   local real angle
   local item i
   local integer amountMats
   local integer exp
   local unit fog
   local boolean wasSelected = IsUnitSelected(u, GetOwningPlayer(u))
   //Skip calls using unit without anyone in users.
   if FirstOfGroup(udg_Fusion_System_DefusionUser) == null and indexFusion == -1 then
       return
   endif   
   //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, "Defusion")
   //When indexFusion unknown find it.
   if indexFusion == - 1 then
       loop
           exitwhen LoopA > udg_Fusion_System_Pointer_Last
           //Find Defusion
           if u == udg_Fusion_Object_Fusion[udg_Fusion_System_Pointer[LoopA]]   then
//               call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, "Nr:"+I2S(LoopA))
               set indexFusion = udg_Fusion_System_Pointer[LoopA]
               set udg_Fusion_System_Pointer_Finished[LoopA] = true
               exitwhen true
           endif
           set LoopA = LoopA + 1
       endloop
       set x = GetUnitX(u)
       set y = GetUnitY(u)
   else
       set udg_Fusion_System_Pointer_Finished[udg_Fusion_Object_Pointer[indexFusion]] = true
       set u = udg_Fusion_Object_Fusion[indexFusion]
       set x = GetLocationX(udg_Fusion_Object_Loc[indexFusion])
       set y = GetLocationY(udg_Fusion_Object_Loc[indexFusion])
   endif
   call GroupRemoveUnit(udg_Fusion_System_DefusionUser, u)
   //not found?
   if indexFusion == -1 then
       return
   endif   
   set indexData = udg_Fusion_Object_DataIndex[indexFusion]
   set amountMats = CountUnitsInGroup (udg_Fusion_Object_Group[indexFusion])
   //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5,  GetObjectName(udg_Fusion_Data_Spell[indexData]))
   call DestroyEffect( AddSpecialEffect(udg_Fusion_Data_DefuseSFX[indexData],x,y))
   //Drop Items and give them to their owners.
   if   udg_Fusion_Object_Int3[indexFusion] != -1 then
       set itemIndex = udg_Fusion_Object_Int3[indexFusion]
       loop
           exitwhen itemIndex == -1
           //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, "Item Nr: "+I2S(itemIndex)+GetItemName(udg_Fusion_Item_Item[itemIndex]))
           if UnitHasItem(u, udg_Fusion_Item_Item[itemIndex]) then
   //           call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, "Give it to: "+I2S(itemIndex)+GetHeroProperName(udg_Fusion_Item_Owner[itemIndex]))
               call PauseUnit(udg_Fusion_Item_Owner[itemIndex], false)
               call ShowUnit(udg_Fusion_Item_Owner[itemIndex], true)
               call UnitAddItem(udg_Fusion_Item_Owner[itemIndex], udg_Fusion_Item_Item[itemIndex])
           endif
           //mark as free.
           set udg_Fusion_Item_DefReuseSize = udg_Fusion_Item_DefReuseSize + 1
           set udg_Fusion_Item_DefReuse[udg_Fusion_Item_DefReuseSize] = itemIndex
           //next item
           set itemIndex = udg_Fusion_Item_DefNext[itemIndex]
       endloop
       //Drop all items from the fusion.
       set LoopA = UnitInventorySize(u) - 1
       loop
           exitwhen LoopA == -1
           call SetItemPosition( UnitItemInSlot (u, LoopA),x,y)
           set LoopA = LoopA - 1
       endloop
   endif   
   //Prepare Event
   set udg_Fusion_Event_Fusion = u
   set udg_Fusion_Event_Source = udg_Fusion_Object_Source[indexFusion]
   set udg_Fusion_Event_SpellData = udg_Fusion_Object_DataIndex[indexFusion]
   set udg_Fusion_Event_SpellLevel = udg_Fusion_Object_SpellLevel[indexFusion]
   set udg_Fusion_Event_SpellObject = indexFusion
   call  GroupClear (udg_Fusion_Event_Group)
   //Remove the Fuion?
   if udg_Fusion_Data_Remove[indexData] then
       call RemoveUnit(u)
   else
       //Might be a Custom call to defuse
       call KillUnit(u)
   endif
   set angle = 0
   set angleInc = 2 * bj_PI / amountMats
   //Current Exp - old exp = gained Exp
   set exp = R2I((udg_Fusion_Object_Int2[indexFusion] - udg_Fusion_Object_Int1[indexFusion]) * udg_Fusion_Data_DefusionEXPMulti[indexData] / amountMats)
   //Restore fusion Parts.
   loop
       set fog = FirstOfGroup (udg_Fusion_Object_Group[indexFusion])
       exitwhen fog == null
       call GroupRemoveUnit(udg_Fusion_Object_Group[indexFusion],fog)
       set x2 = x + udg_Fusion_Data_DefuseDistance[indexData]*Cos(angle)
       set y2 = y + udg_Fusion_Data_DefuseDistance[indexData]*Sin(angle)
       set angle = angle + angleInc
   //   call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, "ReGain "+GetUnitName(fog) )
       //Make Mats useable
       call SetUnitPosition(fog, x2,y2)
       call ShowUnit(fog,true)
       call PauseUnit(fog,false)
       call SetUnitInvulnerable(fog, false )
       call SetUnitPathing (fog, true)
       call SetUnitVertexColor(fog,255,255,255,255)
       call UnitRemoveAbility(fog ,'Arav')
       call AddHeroXP(fog, exp, true)
       if udg_Fusion_Data_DefuseDispellMat[indexData] then
           call UnitRemoveBuffs(fog, true, true)
       endif
       call SetUnitFacing(fog,bj_RADTODEG * Atan2(y - y2, x - x2))
       //Load defused material into fusionGroup
       call GroupAddUnit(udg_Fusion_Event_Group, fog)
       //What shall happen with the mat?
       if (killed and not udg_Fusion_Data_DefuseOnKilled[indexData]) or ( not killed and not udg_Fusion_Data_DefuseAfterDur[indexData] )then
           //units are lost.
           call UnitDropInventory(fog)
           call RemoveUnit(fog)
       else
           call SetUnitUseFood( fog, true )
           if wasSelected then
               call SelectUnitAddForPlayer( fog, GetOwningPlayer(fog))
           endif
           if killed then
               //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, "Fusion killed/Removed - Set Restored Unit's Life to ="+GetUnitName(u)+" "+ R2S(( GetUnitState(u, UNIT_STATE_MAX_LIFE))* udg_Fusion_Data_DefuseOnKilledHP[indexData]))
               call SetUnitState(fog, UNIT_STATE_LIFE, GetUnitState(fog, UNIT_STATE_MAX_LIFE) * udg_Fusion_Data_DefuseOnKilledHP[indexData])
               call SetUnitState(fog, UNIT_STATE_MANA, GetUnitState(fog, UNIT_STATE_MAX_MANA) * udg_Fusion_Data_DefuseOnKilledMP[indexData])
           else
               if udg_Fusion_Data_DefuseAfterDurHP[indexData] == -1 then
                   call SetUnitState(fog, UNIT_STATE_LIFE, GetUnitState(fog, UNIT_STATE_MAX_LIFE) * udg_Fusion_Object_Real1[indexFusion])
               else
                   call SetUnitState(fog, UNIT_STATE_LIFE, GetUnitState(fog, UNIT_STATE_MAX_LIFE) * udg_Fusion_Data_DefuseAfterDurHP[indexData])
               endif
               if udg_Fusion_Data_DefuseAfterDurMP[indexData] == -1 then
                   call SetUnitState(fog, UNIT_STATE_MANA, GetUnitState(fog, UNIT_STATE_MAX_MANA) * udg_Fusion_Object_Real2[indexFusion])
               else
                   call SetUnitState(fog, UNIT_STATE_MANA, GetUnitState(fog, UNIT_STATE_MAX_MANA) * udg_Fusion_Data_DefuseAfterDurMP[indexData])
               endif
           endif
       endif
   endloop
   //Throw Event
   set udg_Fusion_Event = UnitFusionEventDefusion()
   set udg_Fusion_Event = 0
endfunction

//Will get one the Life and Mana from the Defusion. To use on defusion.
//   Int1 = Old EP
//   Int2 = current ep
//   Real1 = current Life
//   Real2 = current Mana.
function UnitDefusionMonitor takes nothing returns nothing
   local integer indexObject = udg_Fusion_System_Pointer[udg_Fusion_Current_Pointer]
   
   //was Removed?
   if GetUnitState(udg_Fusion_Object_Fusion[indexObject], UNIT_STATE_MAX_LIFE) != 0  then

       set udg_Fusion_Object_Int2[indexObject] = GetHeroXP (udg_Fusion_Object_Fusion[indexObject])
       call MoveLocation(udg_Fusion_Object_Loc[indexObject],GetUnitX(udg_Fusion_Object_Fusion[indexObject]),GetUnitY(udg_Fusion_Object_Fusion[indexObject]))
       
   //Percent
       set udg_Fusion_Object_Real1[indexObject] = GetUnitState( udg_Fusion_Object_Fusion[indexObject], UNIT_STATE_LIFE)/ GetUnitState( udg_Fusion_Object_Fusion[indexObject], UNIT_STATE_MAX_LIFE)
       if GetUnitState(udg_Fusion_Object_Fusion[indexObject], UNIT_STATE_MAX_MANA) != 0 then
           set udg_Fusion_Object_Real2[indexObject] = GetUnitState( udg_Fusion_Object_Fusion[indexObject], UNIT_STATE_MANA)/GetUnitState( udg_Fusion_Object_Fusion[indexObject], UNIT_STATE_MAX_MANA)
       endif   
   else
       //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, "Force Defusion: ")
       call UnitDefusion(null, true, indexObject)
   endif   
endfunction

//Triggers if an Unit dies.
function UnitDefusionDeath takes nothing returns nothing
   if IsUnitInGroup (GetTriggerUnit(), udg_Fusion_System_DefusionUser) then
       call UnitDefusion(GetTriggerUnit(), (GetKillingUnit() != null), -1 )
   endif
endfunction

//This will use the given Fusion_Object and use it for defusion
//If you need an new one call it with InserObject or with cloneObject
function UnitDefusionTransformObject takes integer indexObject returns nothing
   set udg_Fusion_Object_Int1[indexObject] = GetHeroXP (udg_Fusion_Object_Fusion[indexObject])
   set udg_Fusion_Object_Int2[indexObject] =  udg_Fusion_Object_Int1[indexObject]
   //Next Line saves defusions if they do not take over items
   set udg_Fusion_Object_Int3[indexObject] = -1
   set udg_Fusion_Object_Type[indexObject] = UnitFusionTypeDefusion()
   call DestroyEffect(udg_Fusion_Object_Effect[indexObject])
   set udg_Fusion_Object_Effect[indexObject] = null
   call GroupAddUnit(udg_Fusion_System_DefusionUser, udg_Fusion_Object_Fusion[indexObject])
   call UnitFusionTimerStart()
endfunction

//perfoms actions an Material for Defusion should do.
function UnitDefusionSaveMatObject takes unit u, integer indexObject returns nothing
   call GroupAddUnit(udg_Fusion_Object_Group[indexObject], u)
   call SetUnitInvulnerable(u, true )
   call PauseUnit(u, true )
   call ShowUnit(u, false)
   call SetUnitUseFood( u, false )
   call SetUnitPathing (u, false)
   call SetUnitPositionLoc(u, udg_Fusion_System_SaveLoc)
endfunction

//Action
// Real1 = remaningDur in seconds.
// Real2 = CurrentTransparenz
// Real3 = Transparenz change a intervale
function UnitFusionFadeIn  takes nothing returns nothing
   local integer indexObject = udg_Fusion_System_Pointer[udg_Fusion_Current_Pointer]
   local integer indexData = udg_Fusion_Object_DataIndex[indexObject]
   //Stop instantly when Fusion Unit was removed/dead.
   if GetWidgetLife(udg_Fusion_Object_Fusion[indexObject]) < 0.405 then
       set udg_Fusion_System_Pointer_Finished[udg_Fusion_Current_Pointer] = true
       call DestroyEffect(udg_Fusion_Object_Effect[indexObject])
       set udg_Fusion_Object_Effect[indexObject] = null
       return
   endif
   //Calc new Transperence
   set udg_Fusion_Object_Real2[indexObject] = udg_Fusion_Object_Real2[indexObject] + udg_Fusion_Object_Real3[indexObject]
   call SetUnitVertexColor(udg_Fusion_Object_Fusion[indexObject], 255, 255, 255, R2I(udg_Fusion_Object_Real2[indexObject]))
   set udg_Fusion_Object_Real1[indexObject] = udg_Fusion_Object_Real1[indexObject] - UnitFusionTimerSpeed()
   
   //FadeIn Finish?
   if udg_Fusion_Object_Real1[indexObject] <= 0.0 then
       set udg_Fusion_System_Pointer_Finished[udg_Fusion_Current_Pointer] = true
       //Finish destroy
       if not udg_Fusion_Object_Bool[indexObject] then
           call SetUnitInvulnerable(udg_Fusion_Object_Fusion[indexObject], false )
       endif
       call PauseUnit(udg_Fusion_Object_Fusion[indexObject], false)
       if UnitFusionAutoSelect() then
           call SelectUnitAddForPlayer(udg_Fusion_Object_Fusion[indexObject], GetOwningPlayer(udg_Fusion_Object_Fusion[indexObject]))
       endif
       //call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, UnitFusionErrorDur(),  udg_Fusion_Data_SFXResult[indexData] +" "+ udg_Fusion_Data_SFXResultAttacht[indexData] )
       call DestroyEffect(AddSpecialEffectTarget( udg_Fusion_Data_SFXResult[indexData] , udg_Fusion_Object_Fusion[indexObject],udg_Fusion_Data_SFXResultAttacht[indexData] ))
       call DestroyEffect(udg_Fusion_Object_Effect[indexObject])
       set udg_Fusion_Object_Effect[indexObject] = null
       //Throw Event
       call  GroupClear (udg_Fusion_Event_Group)
       call GroupAddGroup(udg_Fusion_Object_Group[indexObject], udg_Fusion_Event_Group)
       set udg_Fusion_Event_Fusion = udg_Fusion_Object_Fusion[indexObject]
       set udg_Fusion_Event_Source = udg_Fusion_Object_Source[indexObject]
       set udg_Fusion_Event_SpellData = indexData
       set udg_Fusion_Event_SpellLevel = udg_Fusion_Object_SpellLevel[indexObject]
       set udg_Fusion_Event_SpellObject = indexObject
       set udg_Fusion_Event = UnitFusionEventFadeInFinish()
       set udg_Fusion_Event = 0
       //this will clean the objct on next timer intervale
       else
           call PauseUnit(udg_Fusion_Object_Fusion[indexObject], true)
       endif   
endfunction

//Takeover an FusionObject for fading in
//To start an new call it with cloneObject(orginalObject)
// Real1 = remaningDur in seconds.
// Real2 = CurrentTransparenz
// Real3 = Transparenz change a intervale
function UnitFusionStartFadeInTakeOver  takes integer indexObject returns nothing
   local integer indexData = udg_Fusion_Object_DataIndex[indexObject]
   if not udg_Fusion_Data_FadeVulnerable[indexData] then
       call SetUnitInvulnerable( udg_Fusion_Object_Fusion[indexObject], true )
   endif
   set udg_Fusion_Object_Bool[indexObject] = udg_Fusion_Data_FadeVulnerable[indexData]
   call PauseUnit(udg_Fusion_Object_Fusion[indexObject], true)
   call SetUnitVertexColor(udg_Fusion_Object_Fusion[indexObject], 255, 255, 255, 0)
   if udg_Fusion_Object_Effect[indexObject] == null then
       set udg_Fusion_Object_Effect[indexObject] = AddSpecialEffect( udg_Fusion_Data_SFX[indexData] , GetUnitX(udg_Fusion_Object_Fusion[indexObject]),GetUnitY(udg_Fusion_Object_Fusion[indexObject]))
   endif
   set udg_Fusion_Object_Type[indexObject] = UnitFusionTypeFadeIn()
   set udg_Fusion_Object_Real1[indexObject] = udg_Fusion_Data_FadeIn[indexData]
   set udg_Fusion_Object_Real2[indexObject] = 0
   set udg_Fusion_Object_Real3[indexObject]= PercentToInt(100 - udg_Fusion_Data_FadeInHidden[indexData], 255) / udg_Fusion_Data_FadeIn[indexData] * UnitFusionTimerSpeed()
   //Duration above 0?
   if udg_Fusion_Data_ResultDur[indexData] + udg_Fusion_Data_ResultDurPerLevel[indexData] * udg_Fusion_Object_SpellLevel[indexObject] > 0 then
       call UnitApplyTimedLife( udg_Fusion_Object_Fusion[indexObject], 'BTLF', udg_Fusion_Data_ResultDur[indexData] + udg_Fusion_Data_ResultDurPerLevel[indexData] * udg_Fusion_Object_SpellLevel[indexObject] )
   endif   
   call UnitFusionTimerStart()
endfunction


//Start FadingIn for u
// Creates a new Fusion Object.
//This for peouple who want to use the fadeinEffect.
function UnitFusionStartFadeIn takes unit u, real dur, real hiddenStateAfter, string sfx, boolean immunDuringFade returns nothing
   local integer indexObject
   set indexObject = UnitFusionInserObject()
   if immunDuringFade then
       call SetUnitInvulnerable( u, true )
   endif
   set udg_Fusion_Object_Bool[indexObject] = immunDuringFade
   call PauseUnit(u, true)
   call SetUnitVertexColor(u, 255, 255, 255, 0)
   //Call it without unit.
   set udg_Fusion_Object_Fusion[indexObject] = u
   set udg_Fusion_Object_Type[indexObject] = UnitFusionTypeFadeIn()
   set udg_Fusion_Object_Real1[indexObject] = dur
   set udg_Fusion_Object_DataIndex[indexObject] = -1
   //Create new effect?
   set udg_Fusion_Object_Effect[indexObject] = AddSpecialEffect( sfx, GetUnitX(u), GetUnitY(u) )
   set udg_Fusion_Object_Real2[indexObject] = 0
   set udg_Fusion_Object_Real3[indexObject]= PercentToInt(100 - hiddenStateAfter, 255) / dur * UnitFusionTimerSpeed()
   call UnitFusionTimerStart()
endfunction

function UnitFusionPulledGetItems takes nothing returns nothing
   call UnitFusionLoadInventory(GetEnumUnit())
endfunction

//Subfunction - UnitFusionPulled
//Removes the material
function UnitFusionCleanMats takes nothing returns nothing
   local unit u = GetEnumUnit()
   call ShowUnit(u, false)
   call ShowUnit(u, true)
   if udg_Fusion_Data_Remove[udg_Fusion_Current_Data] then
           call UnitDropInventory(u)
           call RemoveUnit(u)
       else
           call KillUnit(u)   
   endif
   set u = null
endfunction

//Subfunction - UnitFusionPulled
//Removes substitutes missles
function UnitFusionPulledClean takes nothing returns nothing
   local unit u = GetEnumUnit()
   if GetUnitTypeId(u) == udg_Fusion_Data_Replace[udg_Fusion_Current_Data] then
       call GroupRemoveUnit(udg_Fusion_Object_Group[udg_Fusion_Current_Object] , u)
       call RemoveUnit(u)
   endif
   set u = null   
endfunction

//Loads real Units into the FusionEventGroup
//Old - Unneeded
function UnitFusionLoadParts takes nothing returns nothing
   if  GetUnitTypeId(GetEnumUnit()) != udg_Fusion_Data_Replace[udg_Fusion_Current_Data] then
       call GroupAddUnit (udg_Fusion_Event_Group, GetEnumUnit())
   endif
endfunction

//Subfunction - UnitFusionPulled
//Group Enumeration of Current FusionGroup
//Real1 = Speed
//Real2 = ZSpeed
function UnitFusionPulledPull takes nothing returns nothing
   local integer indexObject = udg_Fusion_System_Pointer[udg_Fusion_Current_Pointer]
   local integer indexData = udg_Fusion_Object_DataIndex[indexObject]
   local unit u = GetEnumUnit()
   local real x
   local real y
   local real angle
   //call BJDebugMsg("Pull")
   //do not move real units if there is an replace.
   if udg_Fusion_Data_Replace[indexData] != 0 and GetUnitTypeId(u) != udg_Fusion_Data_Replace[indexData] then
       return
   endif
   //Flying enabled?
   if udg_Fusion_Object_Real2[indexObject] > 0 then
        call SetUnitFlyHeight( u, ( GetUnitFlyHeight(u) + udg_Fusion_Object_Real2[indexObject] ), 0.00 )
    endif
   //Avoid Moving units over the Location; hidden defusion using an replace do not matter.
    if    IsUnitInRangeLoc(u, udg_Fusion_Object_Loc[indexObject], udg_Fusion_Object_Real1[indexObject]) or ((udg_Fusion_Data_DefuseAfterDur[indexData] or udg_Fusion_Data_DefuseOnKilled[indexData]) and udg_Fusion_Data_Replace[indexData] == 0 and GetUnitTypeId(u) != udg_Fusion_Data_Replace[indexData]) then
       call SetUnitPositionLoc( u, udg_Fusion_Object_Loc[indexObject] )
    else
       set udg_Fusion_Object_Bool[indexObject] = false
       set x = GetUnitX(u)
       set y = GetUnitY(u)
       set angle =  Atan2(GetLocationY(udg_Fusion_Object_Loc[indexObject]) - y, GetLocationX(udg_Fusion_Object_Loc[indexObject]) - x)
       set x = x + udg_Fusion_Object_Real1[indexObject] * Cos(angle)
       set y = y + udg_Fusion_Object_Real1[indexObject] * Sin(angle)
    call SetUnitPosition( u, x,y )
    endif
endfunction

//Group Action to take defusion Material out of play.
function UnitFusionSaveMat takes nothing returns nothing
   call UnitDefusionSaveMatObject(GetEnumUnit(),udg_Fusion_Current_Object)
endfunction

//Spawns the Fusion
//Based on the Fusion Object
//Will fill udg_Fusion_Object_Fusion[indexObject] with the fusion.

function UnitFusionSpawn takes integer indexObject, real x, real y returns nothing
   local integer indexData = udg_Fusion_Object_DataIndex[indexObject]
   local unit u = CreateUnit (GetOwningPlayer(udg_Fusion_Object_Source[indexObject]), udg_Fusion_Data_Result[indexData], x, y, 270 )
   local boolean hasInventory =  (UnitInventorySize(u) != 0)
   local boolean defusion = udg_Fusion_Data_DefuseAfterDur[indexData] or udg_Fusion_Data_DefuseOnKilled[indexData]
   call UnitAddAbility(u, udg_Fusion_Data_SkillGained[indexData])
   set udg_Fusion_Object_Fusion[indexObject] = u
   call  SetHeroLevel (u, udg_Fusion_Object_Int1[indexObject], false)
   
   //Which Statmod, statsmod 0 is default?
   if udg_Fusion_Data_HeroStatsMod[indexData] != 0 then
       call  SetHeroAgi (u, udg_Fusion_Object_Int2[indexObject], true)
       call  SetHeroStr (u, udg_Fusion_Object_Int3[indexObject], true)
       call  SetHeroInt (u, udg_Fusion_Object_Int4[indexObject], true)
   endif
   
   //Load Items.
   if hasInventory then
       set udg_Fusion_Item_Size = 0
       call ForGroup(udg_Fusion_Object_Group[indexObject], function UnitFusionPulledGetItems)
   endif       
   
   //Defusion?
   if defusion then
       call UnitDefusionTransformObject(indexObject)
       set udg_Fusion_Current_Object = indexObject
       call ForGroup(udg_Fusion_Object_Group[indexObject],  function UnitFusionSaveMat)
       set udg_Fusion_System_Pointer_Finished[udg_Fusion_Current_Pointer] = false
       if   hasInventory then
           call UnitFusionGiveItemsSave(u, indexObject)
       endif   
   else
       set udg_Fusion_System_Pointer_Finished[udg_Fusion_Current_Pointer] = true
       if   hasInventory then
           call UnitFusionGiveItems(u)
       endif
   endif
   
   //apply Averrage Life?
   if udg_Fusion_Data_ResultHPAverage[indexData] then
       call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_MAX_LIFE) * udg_Fusion_Object_Real3[indexObject] )
   endif
   //apply Averrage Mana?
   if udg_Fusion_Data_ResultMPAverage[indexData] then
       call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MAX_MANA) * udg_Fusion_Object_Real4[indexObject] )
   endif
   
   //Throw Event
   call GroupClear(udg_Fusion_Event_Group)
   //call ForGroup(udg_Fusion_Object_Group[indexObject], function UnitFusionLoadParts)
   call GroupAddGroup(udg_Fusion_Object_Group[indexObject], udg_Fusion_Event_Group)
   set udg_Fusion_Event_Fusion = u
   set udg_Fusion_Event_Source = udg_Fusion_Object_Source[indexObject]
   set udg_Fusion_Event_SpellData = indexData
   set udg_Fusion_Event_SpellLevel = udg_Fusion_Object_SpellLevel[indexObject]
   set udg_Fusion_Event_SpellObject = indexObject
   if IsUnitType (u, UNIT_TYPE_HERO) then
       set udg_Fusion_Event = UnitFusionEventHero()
   else
       set udg_Fusion_Event = UnitFusionEventNonHero()
   endif   
   set udg_Fusion_Event = 0
   
   //Start FadeIn after pulling?
   if udg_Fusion_Data_FadeIn[indexData] > 0 then
       //Start a new Fusion Object or takeover?
       if udg_Fusion_System_Pointer_Finished[udg_Fusion_Current_Pointer] then
           call UnitFusionStartFadeInTakeOver(indexObject)
           set udg_Fusion_System_Pointer_Finished[udg_Fusion_Current_Pointer] = false
       else
           call UnitFusionStartFadeInTakeOver(UnitFusionCloneObject(indexObject))
       endif
   else
       // Timed Life > 0
       // Constant + Level * Levelboni
       if udg_Fusion_Data_ResultDur[indexData] + ( udg_Fusion_Data_ResultDurPerLevel[indexData]* udg_Fusion_Object_SpellLevel[indexObject]) > 0 then
           call UnitApplyTimedLife( u, 'BTLF', udg_Fusion_Data_ResultDur[indexData] + ( udg_Fusion_Data_ResultDurPerLevel[indexData]* udg_Fusion_Object_SpellLevel[indexObject]) )
       endif
       call DestroyEffect(udg_Fusion_Object_Effect[indexObject])
       set udg_Fusion_Object_Effect[indexObject] = null
       if UnitFusionAutoSelect() then
           call SelectUnitAddForPlayer(u, GetOwningPlayer(udg_Fusion_Object_Source[indexObject]))
       endif
       call DestroyEffect(AddSpecialEffectTarget( udg_Fusion_Data_SFXResult[indexData] , u,udg_Fusion_Data_SFXResultAttacht[indexData] ))
   endif
   
   //Clean mats if no defusion
   if not defusion then
       call ForGroup(udg_Fusion_Object_Group[indexObject], function UnitFusionCleanMats)
   endif
   set u = null
endfunction


//Timer Sub Action Pull Fusion
// Real = Speed       
// Real2 = ZSpeed
// Real3 = life On Fusion
// Real4 = Mana on fusion
// Int1 = heroLevel
// Int2 = heroAgi
// Int3 = heroStr
// Int4 = heroInt
function UnitFusionPulled takes nothing returns nothing
   local integer indexObject = udg_Fusion_System_Pointer[udg_Fusion_Current_Pointer]
   set udg_Fusion_Object_Bool[indexObject] = true
   //Loop All running fusion
   //Used in the Subfunction
   call ForGroup(udg_Fusion_Object_Group[indexObject], function UnitFusionPulledPull)
   //All parts are near Enough?
   if udg_Fusion_Object_Bool[indexObject] then
       call ForGroup(udg_Fusion_Object_Group[indexObject], function UnitFusionPulledClean)
       call UnitFusionSpawn(indexObject, GetLocationX(udg_Fusion_Object_Loc[indexObject]),GetLocationY(udg_Fusion_Object_Loc[indexObject]))
   endif   
endfunction

function UnitFusionIsPart takes unit u, integer indexData returns boolean
   local integer indexEnd = udg_Fusion_System_UnitTypesEnd[indexData]
   local integer indexCurrent = udg_Fusion_System_UnitTypesStart[indexData]
   set udg_Fusion_Current_FilterUnit = u
   //Can Unit be part of Fusion?
   loop
       if udg_Fusion_Data_UnitTypes[indexCurrent] == 0 and udg_Fusion_System_Units[indexCurrent] == null then
           if TriggerEvaluate(udg_Fusion_Data_FilterWild[indexData]) then
               set udg_Fusion_System_Units[indexCurrent] = u
               return true
           endif
       endif
       set indexCurrent = indexCurrent + 1
       exitwhen indexCurrent > indexEnd
   endloop
   return false
endfunction

function UnitFusionIsSpecificPart takes unit u, integer indexData returns boolean
   local integer indexEnd = udg_Fusion_System_UnitTypesEnd[indexData]
   local integer indexCurrent = udg_Fusion_System_UnitTypesStart[indexData]
   local integer uTyp = GetUnitTypeId(u)
   //Can Unit be part of Fusion?
   loop
       if uTyp == udg_Fusion_Data_UnitTypes[indexCurrent] and udg_Fusion_System_Units[indexCurrent] == null  then
           set udg_Fusion_System_Units[indexCurrent] = u
           return true   
       endif
       set indexCurrent = indexCurrent + 1
       exitwhen indexCurrent > indexEnd
   endloop
   return false
endfunction


function UnitFusionPickAny takes nothing returns nothing
   call UnitFusionIsPart(GetEnumUnit(), udg_Fusion_Current_Data)
   call GroupRemoveUnit(udg_Fusion_Object_Group[0], GetEnumUnit())
endfunction

function UnitFusionPickSpecific takes nothing returns nothing
   if UnitFusionIsSpecificPart(GetEnumUnit(), udg_Fusion_Current_Data) then
       call GroupRemoveUnit(udg_Fusion_Object_Group[0], GetEnumUnit())
   endif
endfunction

function UnitFusionPickSelected takes nothing returns nothing
   if IsUnitSelected(GetEnumUnit(), GetTriggerPlayer()) then
       call UnitFusionPickSpecific()
   endif
endfunction

function UnitFusionFilter takes unit u, integer indexData returns boolean
   local integer indexEnd = udg_Fusion_System_UnitTypesEnd[indexData]
   local integer indexCurrent = udg_Fusion_System_UnitTypesStart[indexData]
   local real life = GetWidgetLife(u)
   set udg_Fusion_Current_FilterUnit = u
   //Allowed?
   //Ordercheck;
   //Filter Units if OrderCheck is set and Unit has not the expectedOrder.
   if not (udg_Fusion_Data_OrderCheck[indexData] == "" or udg_Fusion_Data_OrderCheck[indexData] == null ) and OrderId(udg_Fusion_Data_OrderCheck[indexData]) !=  GetUnitCurrentOrder(u)  then
       return false
   endif
   if not TriggerEvaluate(udg_Fusion_Data_FilterPre[indexData]) and udg_Fusion_Data_FilterPre[indexData] != null  then
       return false
   endif   
   
   if not TriggerEvaluate(udg_Fusion_Data_FilterMain[indexData]) and udg_Fusion_Data_FilterMain[indexData] != null then
       return false
   endif   

   //Needed UnitTypes pass
   loop
       if GetUnitTypeId(u) == udg_Fusion_Data_UnitTypes[indexCurrent] or udg_Fusion_Data_UnitTypes[indexCurrent] == 0 then
           return true
       endif
       set indexCurrent = indexCurrent + 1
       exitwhen indexCurrent > indexEnd
   endloop
   //if something goes terrible wrong.
   return false
endfunction

function UnitFusionFilterEnum takes nothing returns boolean
   if IsUnitPaused(GetFilterUnit()) or IsUnitIllusion(GetFilterUnit()) or not IsUnitInRangeLoc(GetFilterUnit(), udg_Fusion_Object_Loc[0], udg_Fusion_Object_Real1[0]) then
       return false
   endif
   return UnitFusionFilter(GetFilterUnit(),udg_Fusion_Current_Data)
endfunction

function UnitFusionFilterMessage takes integer indexData, integer indexCurrent, string s returns string
   local string seperator = "\n"
   set s = s +seperator
   //Specific UnitType?
   if udg_Fusion_Data_UnitTypes[indexCurrent] != null then
       set s = s + " - " + GetObjectName(udg_Fusion_Data_UnitTypes[indexCurrent])
   else
       set s = s + UnitFusionWildCard()
   endif
   set seperator = null
   return s
endfunction

//Generate ErrorWarrinng
function UnitFusionErrorMessage takes integer indexData, string s returns string
   local string seperator = "\n"
   set s = "|cffffcc00"+ GetObjectName(udg_Fusion_Data_Spell[indexData])+"|r"
   if udg_Fusion_Data_FilterPre[indexData]!= null and udg_Fusion_Data_ErrorMsgPre[indexData] != null and udg_Fusion_Data_ErrorMsgPre[indexData] !="" then
       set s = s + seperator
       set s = s + udg_Fusion_Data_ErrorMsgPre[indexData]
   endif
   if udg_Fusion_Data_FilterMain[indexData]!= null and udg_Fusion_Data_ErrorMsgMain[indexData] != null and udg_Fusion_Data_ErrorMsgMain[indexData] !="" then
       set s = s + seperator
       set s = s + udg_Fusion_Data_ErrorMsgMain[indexData]
   endif
   if udg_Fusion_Data_FilterWild[indexData]!= null and udg_Fusion_Data_ErrorMsgWild[indexData] != null and udg_Fusion_Data_ErrorMsgWild[indexData] !=""   then
       set s = s + seperator
       set s = s + udg_Fusion_Data_ErrorMsgWild[indexData]
   endif
   set s = s + seperator + UnitFusionErrorMsg()
   set seperator = null
   return s
endfunction


function UnitFusionLoadGroup takes integer indexCurrent, integer indexEnd, integer indexObject returns nothing
   loop
       call GroupAddUnit(udg_Fusion_Object_Group[indexObject], udg_Fusion_System_Units[indexCurrent])
       set   indexCurrent = indexCurrent + 1
       exitwhen indexCurrent > indexEnd
   endloop   
endfunction

function UnitFusionFuse takes integer indexData, location loc returns nothing
   local integer indexEnd = udg_Fusion_System_UnitTypesEnd[indexData]
   local integer indexCurrent = udg_Fusion_System_UnitTypesStart[indexData]
   local integer amount = ( udg_Fusion_System_UnitTypesEnd[indexData] - udg_Fusion_System_UnitTypesStart[indexData] ) + 1
   local real x = 0
   local real y = 0
   local real x2 = 0
   local real y2 = 0
   local boolean isValidFusion = true
   local unit u
   local integer level = GetUnitAbilityLevel(GetTriggerUnit(), udg_Fusion_Data_Spell[indexData])
   local real life = 0
   local real mana = 0
   local string s
   local boolean defusion = udg_Fusion_Data_DefuseAfterDur[indexData] or udg_Fusion_Data_DefuseOnKilled[indexData]
   local integer heroLevel = 0
   local integer heroAgi = 0
   local integer heroStr = 0
   local integer heroInt = 0
   local integer indexEnter
   local boolean isResultHero = IsUnitIdType(udg_Fusion_Data_Result[indexData], UNIT_TYPE_HERO)
   set udg_Fusion_Current_Data = indexData
   //clear old data
   call GroupClear(udg_Fusion_Object_Group[0])
   loop
       set udg_Fusion_System_Units[indexCurrent] = null
       exitwhen indexCurrent == indexEnd
       set indexCurrent = indexCurrent + 1
   endloop
   
   //Index 0 is never used, so one can use it here.
   set udg_Fusion_Object_Real1[0] = udg_Fusion_Data_AoE[indexData] + ( level * udg_Fusion_Data_AoEPerLevel[indexData])
   
   if udg_Fusion_Object_Real1[0] > 0 then
       set udg_Fusion_Object_Loc[0] = loc
       call GroupEnumUnitsInRangeOfLoc(udg_Fusion_Object_Group[0], loc, udg_Fusion_Object_Real1[0] + UnitMaxColison() , udg_Fusion_System_Filter)
       set udg_Fusion_Object_Loc[0] = null
   endif   
   //AutoAddCaster?
   if udg_Fusion_Data_AutoAddCaster[indexData] and UnitFusionFilter(GetTriggerUnit(),indexData ) then
       if   UnitFusionIsSpecificPart(GetTriggerUnit(), udg_Fusion_Current_Data) then
           call GroupRemoveUnit(udg_Fusion_Object_Group[0], GetTriggerUnit())
       endif
   endif
   //AutoAddTarget?
   if udg_Fusion_Data_AutoAddTarget[indexData] and UnitFusionFilter(GetSpellTargetUnit(),indexData ) then
       if UnitFusionIsSpecificPart(GetSpellTargetUnit(), udg_Fusion_Current_Data) then
           call GroupRemoveUnit(udg_Fusion_Object_Group[0], GetSpellTargetUnit())
       endif
   endif
   //First check selected
   call ForGroup(udg_Fusion_Object_Group[0], function UnitFusionPickSelected)
   //then check for defined unittypes
   call ForGroup(udg_Fusion_Object_Group[0], function UnitFusionPickSpecific)
   
   //then use wildcard unittypes
   set indexCurrent = udg_Fusion_System_UnitTypesStart[indexData]
   
   //spell has wildcards?
   loop
       if udg_Fusion_Data_UnitTypes[indexCurrent] == 0 then
           call ForGroup(udg_Fusion_Object_Group[0], function UnitFusionPickAny)
           exitwhen true
       endif   
       set indexCurrent = indexCurrent + 1
       exitwhen indexCurrent > indexEnd
   endloop
   
   //All MergingParts found?
   set indexCurrent = udg_Fusion_System_UnitTypesStart[indexData]
   set s = UnitFusionErrorMessage(indexData,"")
   loop
       if udg_Fusion_System_Units[indexCurrent] == null then
           set isValidFusion = false
           set s = UnitFusionFilterMessage(indexData,indexCurrent,s)
       else
           set x = x + GetUnitX(udg_Fusion_System_Units[indexCurrent])
           set y = y + GetUnitY(udg_Fusion_System_Units[indexCurrent])
           set life = life + GetUnitStatePercent(udg_Fusion_System_Units[indexCurrent] , UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE)
           set mana = mana + GetUnitStatePercent(udg_Fusion_System_Units[indexCurrent] , UNIT_STATE_MANA, UNIT_STATE_MAX_MANA)
           //HeroStuf
           if IsUnitType(udg_Fusion_System_Units[indexCurrent] ,UNIT_TYPE_HERO) and isResultHero then
               set heroLevel = heroLevel +  GetHeroLevel (udg_Fusion_System_Units[indexCurrent])
               set heroAgi = heroAgi + GetHeroAgi(udg_Fusion_System_Units[indexCurrent], udg_Fusion_Data_HeroAllowBoniStats[indexData])
               set heroStr = heroStr + GetHeroStr(udg_Fusion_System_Units[indexCurrent], udg_Fusion_Data_HeroAllowBoniStats[indexData])
               set heroInt = heroInt + GetHeroInt(udg_Fusion_System_Units[indexCurrent], udg_Fusion_Data_HeroAllowBoniStats[indexData])
           endif
       endif
       set indexCurrent = indexCurrent + 1
       exitwhen indexCurrent > indexEnd
   endloop
   
   if isValidFusion then
       set x= x/amount
       set y = y/amount
       set life = (life/amount)*0.01
       set mana = (mana/amount) *0.01
           
       set indexCurrent = udg_Fusion_System_UnitTypesStart[indexData]
       if udg_Fusion_Data_SpawnAtTargetPoint[indexData] then
           set x = GetLocationX(loc)
           set y = GetLocationY(loc)   
       endif
       
       
       //Gives an free spot in the Object Arrays.
       set indexEnter = UnitFusionInserObject()
       set udg_Fusion_Current_Pointer = udg_Fusion_System_Pointer_Last
       set udg_Fusion_Current_Object = indexEnter
       set udg_Fusion_Object_DataIndex[indexEnter] = indexData
       set udg_Fusion_Object_Source[indexEnter] = GetTriggerUnit()
       set udg_Fusion_Object_Fusion[indexEnter] = null
       set udg_Fusion_Object_SpellLevel[indexEnter] = level
       set udg_Fusion_Object_Real3[indexEnter] = life
       set udg_Fusion_Object_Real4[indexEnter] = mana
       
       if isResultHero then
           if not udg_Fusion_Data_HeroSumLevel[indexData] then
               set heroLevel = heroLevel / amount
           endif
           if udg_Fusion_Data_HeroStatsMod[indexData] == 1 then
               set heroAgi = heroAgi / amount
               set heroStr = heroStr / amount
               set heroInt = heroInt / amount
           endif
           set udg_Fusion_Object_Int1[indexEnter] = heroLevel
           set udg_Fusion_Object_Int2[indexEnter] = heroAgi
           set udg_Fusion_Object_Int3[indexEnter] = heroStr
           set udg_Fusion_Object_Int4[indexEnter] = heroInt
       endif
       
       
       if udg_Fusion_Object_Group[indexEnter] == null then
           set udg_Fusion_Object_Group[indexEnter] = CreateGroup()
       else
           call GroupClear (udg_Fusion_Object_Group[indexEnter])
       endif   
       
       call UnitFusionLoadGroup(indexCurrent,indexEnd,indexEnter)
       
       if udg_Fusion_Object_Loc[indexEnter] == null then
           set udg_Fusion_Object_Loc[indexEnter] = Location(x,y)
       else
           call MoveLocation(udg_Fusion_Object_Loc[indexEnter],x,y)
       endif
       //Mark the Object as finished?
       //Pulled Fusions / Defusions / Fadeins will take over this object.
       //set udg_Fusion_System_Pointer_Finished[udg_Fusion_System_Pointer_Last] = not (udg_Fusion_Data_Speed[indexData] > 0 or udg_Fusion_Data_FadeIn[indexData] > 0 or defusion)
               
       // Pull together:
       if udg_Fusion_Data_Speed[indexData] > 0 then
           set udg_Fusion_Object_Effect[indexEnter] = AddSpecialEffect( udg_Fusion_Data_SFX[indexData],x,y )
           set udg_Fusion_Object_Type[indexEnter] = UnitFusionTypePull()
           //For Pull Real 1 is Speed.
           set udg_Fusion_Object_Real1[indexEnter] = udg_Fusion_Data_Speed[indexData] * UnitFusionTimerSpeed()
           //For Pull Real2 is ZSpeed
           set udg_Fusion_Object_Real2[indexEnter] = udg_Fusion_Data_SpeedZ[indexData] * UnitFusionTimerSpeed()

           //Pull orginal units?
           if udg_Fusion_Data_Replace[indexData] == 0 then
               loop
                   call SetUnitFacing(udg_Fusion_System_Units[indexCurrent],bj_RADTODEG * Atan2(y - GetUnitY(udg_Fusion_System_Units[indexCurrent]), x - GetUnitX(udg_Fusion_System_Units[indexCurrent])))
                   call SetUnitVertexColor(udg_Fusion_System_Units[indexCurrent], 255, 255, 255, UnitFusionFade())
                   call DestroyEffect(AddSpecialEffectTarget( udg_Fusion_Data_SFXUnit[indexData] , udg_Fusion_System_Units[indexCurrent], udg_Fusion_Data_SFXUnitAttacht[indexData] ))
                   call UnitAddAbility(udg_Fusion_System_Units[indexCurrent], 'Arav' )
                   if not defusion then
                       call UnitAddAbility(udg_Fusion_System_Units[indexCurrent] , 'Aloc')
                   else
                       call SetUnitInvulnerable(udg_Fusion_System_Units[indexCurrent], true )
                       call  SetUnitPathing (udg_Fusion_System_Units[indexCurrent], false)
                   endif
                   call PauseUnit(udg_Fusion_System_Units[indexCurrent], true )
                   set   indexCurrent = indexCurrent + 1
                   exitwhen indexCurrent > indexEnd
               endloop
           else
           //Pull substitutes
               loop
                   set x2 =GetUnitX(udg_Fusion_System_Units[indexCurrent])
                   set y2 =GetUnitY(udg_Fusion_System_Units[indexCurrent])
                   call DestroyEffect(AddSpecialEffect( udg_Fusion_Data_SFXUnit[indexData],x2,y2 ))
                   if not defusion then
                       if udg_Fusion_Data_Remove[indexData] then
                           //Hide and Pause until pull is finished.
                           call ShowUnit(udg_Fusion_System_Units[indexCurrent], false)
                           call PauseUnit(udg_Fusion_System_Units[indexCurrent], true)
                       else
                           call KillUnit(udg_Fusion_System_Units[indexCurrent])
                       endif
                   else
                       call SetUnitInvulnerable(udg_Fusion_System_Units[indexCurrent], true )
                       call PauseUnit(udg_Fusion_System_Units[indexCurrent], true )
                       call  SetUnitPathing (udg_Fusion_System_Units[indexCurrent], false)
                       call  ShowUnit (udg_Fusion_System_Units[indexCurrent], false)
                   endif   
                   //Substitudes should flying
                   set u = CreateUnit(GetTriggerPlayer(), udg_Fusion_Data_Replace[indexData], x2, y2, bj_RADTODEG * Atan2(y - y2, x - x2) )                   
                   call GroupAddUnit( udg_Fusion_Object_Group[indexEnter], u )
                   call PauseUnit(u, true )
                   set   indexCurrent = indexCurrent + 1
                   exitwhen indexCurrent > indexEnd
               endloop   
               set u = null
           endif
           call UnitFusionTimerStart()
       else
           
           //Instant Fusion
           
           //Spawn Grafic Supstitudes
           loop
               set x2 =GetUnitX(udg_Fusion_System_Units[indexCurrent])
               set y2 =GetUnitY(udg_Fusion_System_Units[indexCurrent])
               call DestroyEffect(AddSpecialEffect( udg_Fusion_Data_SFXUnit[indexData],x2,y2 ))
               //Spawn extra Units, if wanted.
               //that is actually a hidden feature:
               //if one does not set speed but uses replaces it will summon units, where the old units were standing.
               //This summons have the same duration as the fusion.
               if  udg_Fusion_Data_Replace[indexData] != 0 then
                   set u = CreateUnit(GetTriggerPlayer(), udg_Fusion_Data_Replace[indexData], x2, y2, bj_RADTODEG * Atan2(y - y2, x - x2) )
                   // Timed Life > 0
                   // Constant + Level * Levelboni
                   if udg_Fusion_Data_ResultDur[indexData] + ( udg_Fusion_Data_ResultDurPerLevel[indexData]*level) > 0 then
                       call UnitApplyTimedLife(u, 'BTLF', udg_Fusion_Data_ResultDur[indexData] + ( udg_Fusion_Data_ResultDurPerLevel[indexData]*level))
                   endif
               endif   
               set   indexCurrent = indexCurrent + 1
               exitwhen indexCurrent > indexEnd
           endloop   
           call UnitFusionSpawn(indexEnter,x,y)
           set u = null
       endif   
   else
       call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0, 0, UnitFusionErrorDur(), s)
       call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0, 0, UnitFusionErrorDur(), ( "---------------" ))
   endif
   set s = null
   set u = null
endfunction

function UnitFusionCasting takes nothing returns nothing
   local integer indexData = 0
   local integer Id = GetSpellAbilityId()
   local location loc
   if IsUnitHidden(GetTriggerUnit()) then
       return
   endif
   //Find Casted Skill
   loop
       exitwhen indexData == udg_Fusion_CreationIndex
       if Id == udg_Fusion_Data_Spell[indexData]   then
           //Has TargetUnit
           if GetSpellTargetUnit() != null then
               set loc = GetUnitLoc(GetSpellTargetUnit())
           else
               //Has TargetLoc? not 100% correct, but its pretty unlikly to cast something exactly at 0/0.
               if GetSpellTargetX() == 0.0 and GetSpellTargetY() == 0.0 then
                   set loc = GetUnitLoc(GetTriggerUnit())
               else
                   set loc = GetSpellTargetLoc()
               endif   
           endif
           call UnitFusionFuse(indexData, loc)
           call RemoveLocation(loc)
           set loc = null
           return
       endif
       set indexData = indexData + 1
   endloop
endfunction

//On First Execution of this System
//Preload Skills
//Enable Fusion
//Enable Defusion death detecion
//Preload Specialeffects
//Autogenerate missing Atttachmentpoints
//Printout Warnings for using wildcard units but having no FilterWild
function UnitFusionPreload takes nothing returns nothing
   local unit u
   local integer LoopA = 0
   local integer LoopB
   local real x = GetRectMaxX (bj_mapInitialPlayableArea)
   local real y = GetRectMaxY (bj_mapInitialPlayableArea)
   call TriggerClearActions(gg_trg_UnitFusion)
   call TriggerAddAction( gg_trg_UnitFusion, function UnitFusionCasting )
   call TriggerRegisterAnyUnitEventBJ( gg_trg_UnitFusion, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoo', 0,0, 270 )
    call UnitAddAbility(u, 'Arav')
    call UnitAddAbility(u, 'Aloc')
    call RemoveUnit( u )
   set udg_Fusion_System_DefusionTrigger = CreateTrigger()
   call TriggerRegisterAnyUnitEventBJ( udg_Fusion_System_DefusionTrigger, EVENT_PLAYER_UNIT_DEATH )
   call TriggerAddAction(udg_Fusion_System_DefusionTrigger, function UnitDefusionDeath)
   set u = null
   set udg_Fusion_Item_DefSize = UnitDefusionItemOffset()
   
   //Define Filter for finding Mats.
   set udg_Fusion_System_Filter = Condition(function UnitFusionFilterEnum)
   
   //Preload specialeffects
   //Autofills Attachmentpoints, if needed
   loop
       exitwhen LoopA == udg_Fusion_CreationIndex

       call DestroyEffect(AddSpecialEffect( udg_Fusion_Data_SFXUnit[LoopA],x,y ))
       call DestroyEffect(AddSpecialEffect( udg_Fusion_Data_SFX[LoopA],x,y ))
       call DestroyEffect(AddSpecialEffect( udg_Fusion_Data_SFXResult[LoopA],x,y ))
       
       //Use default attachment point?
       if udg_Fusion_Data_SFXUnit[LoopA] != "" and StringIsEmpty(udg_Fusion_Data_SFXUnitAttacht[LoopA]) then
           set udg_Fusion_Data_SFXUnitAttacht[LoopA] = "chest"
       endif
       //Use default attachment point?
       if udg_Fusion_Data_SFXResult[LoopA] != "" and StringIsEmpty(udg_Fusion_Data_SFXResultAttacht[LoopA]) then
           set udg_Fusion_Data_SFXResultAttacht[LoopA] = "origin"
       endif
       
       set LoopA = LoopA + 1
   endloop
   
   //Printout Warnings for using Wildcards but not using Wildcard Filters.
   set LoopA = 0
   loop
       exitwhen LoopA == udg_Fusion_CreationIndex
       set LoopB = udg_Fusion_System_UnitTypesStart[LoopA]
       loop
           exitwhen LoopB > udg_Fusion_System_UnitTypesEnd[LoopA]
           if udg_Fusion_Data_UnitTypes[LoopB] == 0 then
               if udg_Fusion_Data_FilterWild[LoopA] == null then
                   call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30,"|cffff0000Warning|r: "+ GetObjectName(udg_Fusion_Data_Spell[LoopA])+" uses Wildcards but has no Wildcard Filter.")
                   exitwhen true
               endif
           endif
           set LoopB = LoopB + 1
       endloop
       set LoopA = LoopA + 1
   endloop
endfunction
//===============

function InitTrig_UnitFusion takes nothing returns nothing
    set gg_trg_UnitFusion = CreateTrigger(  )
    call TriggerAddAction( gg_trg_UnitFusion, function UnitFusionPreload )
endfunction

V8a)
  • now uses an constant boolexpress for filtering units.
  • now catches avaible material touching the AoE Indikator's edge with their hitbox.
  • SpecialEffects are now displayed on map start with maxium x & y of playablemap.
  • one can now select Specialeffect-models with the default World Editor drop down Menu.
    • ^When updating a old version you need to delete the SFX Variables inside variable Editor and copy the Unit Fusion Folder again or the specific variables from the fusion init file.
V8)
  • Merged Fusion Spawning into an own Function
  • Displays all SFX-Effects at MapCenter to preload them
  • AttachmentPoints-Now changeable, If unset autochoose Result=Origin Unit=Chest
  • Uses now GetObjectName
  • Some Improvments for Fusions without Wildcards and Without autoadd Caster/Target
  • Now Drops all Items before removing an Unit.
  • One can now stop further Fusion Progress by stopping "Fusion_System_Timer", if you use "Fusion_System_TimerDisabled=true" new casts won't restart the timer onitself. To restart the timer one can now use call UnitFusionTimerStart() but you have to make sure that "Fusion_System_TimerDisabled=False"
  • Deathcheck in Fadein 0.5 -> 0.405
  • Replaced bj_MAX_INVENTORY with UnitInventorySize(u)
  • Skips hero stuff, if the result is no hero.
  • Removed saving Condition as local and placed it directly into enumerate group.
  • Included some Document into the map
  • One can now change the output String for missing Wildcard Units.
V7)
Variables are now better structed:
Data, Current, System, Object, Item, Event .​
Combined Fusion-Events into 1 and improved the events.
Removed the Hardcoded allow Filters and instead one can now set 3 Filter-Triggers (Pre, Main, Wild)
Removed the global Fusion FadeIn Immunity, can be set per spell.
One can now preset an skill to be auto added to the fusion.
Now uses dynamic Pointers instead of dynamic indexing.
Autoselection is now deactivadeable with an constant.​
V6)
Fusions now takeover items from their mats.
Defusions adresses:
Passing Gained Exp, if wanted
Auras of waiting Units
dispell buffs of waiting units, if wanted
some bug with defusions, if beeing removed from the game​
Fixed an critcal bug with Defusions without Mana.
Insert an optional Ordercheck, if set units don't performing this Order will not pass.​
V5a) now calling Defusion with an index will correctly destroy the fusion unit instantly
V5)
Supports HeroFusions alot better.
Supports now defusion.
Added two Events to catch: FusionFadeInEvent = 1, FusionHeroEvent = 1
Missing mats Message overworked:
Supporting single target 2 unit fusions alot better.​
V4)
There is now the option to set the % hp/MP of the fusion to the average % of the used material, default off.
The Fusion Spells are now in another Folder called Fusion Spells.
removed an further weak bj.
Upadated missing Unit messages.
Fixed a bug Fusionparts using wrong speedZ​
V3a)
Replaced UnitApplyTimedLifeBJ with UnitApplyTimedLife
V3)
Allows to use wildcards now by using an bigger Amounts than setting unittypes.
This wildcard uses the new Allowed ones: Hero, Structure, Mech.
Specific Mats are first filled before filling wildcards.​
Inluded an new default filter mechanic
AllowDead/AllowLiving which has to be set.​
Fixed a bug with fadingIn playing wrong sfxes on finish if spamming fusions
FadingIn Units are now constanly paused to disallow unpausing them with other actions.
FadeIn Supports now an wanted %transparenzy using called Data_Hidden.
TargetUnit/Selfcasting will now check the unit for beeing an specificmaterial on first check.
added Fusion_Data_AoEPerLevel to make this more useable for hero skills/upgrades.
added Fusion_Data_ResultDur/PerLevel this adds a timed life effect after finishing the fusion
only if set above 0​

Creating a Fusion

FadeIn

Defusion

Hero Fusion

Filters

Event


Creating an new fusion is quite simple you copy an Fusion Spell Trigger change the Data_Spell additional you could change data.
Here you should know that only Fusion_Data_.... variables should be written.

Let's have a look on the basic Variables and there effect:
I do skip some with simlear naming and the perLevel Variables.
Variable Info

Fusion_CreationIndex
this is the index on which your Data is saved on. [Fusion_CreationIndex]

Fusion_Data_AmountOfMergers
tells the System how many Units will fuse.

Fusion_Data_Spell
is the spell which will use this fusion, they have to be unique inside the system.

Fusion_Data_Result
is the UnitType one Gains

Fusion_Data_UnitTypes
starting with [creationindex + 1] are specific Fusion Member Types, if you have not set the same number of specific members as Fusion_Data_AmountOfMergers, your spell will take wildcards. This is the only variable you create this way [Fusion_CreationIndex + 1/2/3/4]

Fusion_Data_ResultHPAverage
if set the fusion will have only the average % HP/MP of the units having mergered

Fusion_Data_ResultDur
the fusion will only hold for this amount of seconds.

Fusion_Data_SFX
this will be player while the Fusion is still building up

Fusion_Data_SFXResult
this will be displayed on the fusion when finished. using the death animation tag

Fusion_Data_SFXUnit
this will be displayed at the units having mergered. using the death animation tag

Fusion_Data_Speed
if set the Merging members will be pulled to the fusion point using this speed

Fusion_Data_Replace
if you set speed the merging members will not be pulled the spawn each a replace which will be pulled. You can only set 1 Replacetype on each spell

Fusion_Data_AoE
in which area around the Target Units will be considered for the fusion.

Fusion_Data_AutoAddCaster
ignoring the AoE and TargetLocation the Caster will be considered for Fusing if he passes the filters.

Fusion_Data_SpawnAtTargetPoint
if set the fusion will take place at the target, else it will be the average of cordinates of merging units

Fusion_Data_Remove
the merging Units will removed if set, default they die.

Fusion_Data_ErrorMsgMain
The Error Message for the MainFilter

Fusion_Data_FilterMain
The Main Filter units have to pass to be used for a fusion.

Fusion_Data_SkillGained
This Ability will be added to the Fusion


Fadein is an option you can use to let the Fusion starting hidden and then become more and more material.
If you use FadingIn Fusion_Data_SFXResult will be played first after finishing fadingIn.

While FadingIn the Fusion is paused and on default invulnerable.
The FadingIn Unit becomes repaused constantly.
After the FadingIn ended, the Event FadingInEvent becomes 1.0 to catch.

Fadingin can be combined with Pulled Fusion by setting speed and FadeIn
Variable Info

Fusion_Data_FadeIn
How long it takes to fadeIn, and beeing able to act, in seconds.

Fusion_Data_FadeInHidden
this variable allows you to keep the FadedIn Unit hidden by that %.

Fusion_Data_FadeVulnerable
When true the unit can be hit while the process.

One can use Fadein on any unit with this jassfunction
hiddenStateAfter is an value from 0 to 100.
JASS:
function UnitFusionStartFadeIn
takes unit u, real dur, real hiddenStateAfter, string sfx, boolean immunDuringFade
returns nothing
You can access the started Fadin Object by using udg_Fusion_Current_CreatedObject as Index to change Fusion_Object_x or attach an own custom Variable right after starting the fadein.
(changing Fusion_Object_Pointer or Fusion_System_Pointer is not recommended.)

If you start an Fadein Manually the used Fusion_Event_SpellData on the thrown Event is -1 and has no source, on default.

Defusion saves the Merged Units and restores them as soon the Fusion ends, what will happen with the units is choosen by setting the two Defusion Mods.
There are two versions of Defusion both can be set at the same time.
If the merged Units have items the Fusion will pick the highest Level Items and get equiped with them. As soon the Merged Units restore, they will regain their Items if the Fusion still had them.

DefusionOnKilled

The Fusion will defuse, if killed from someone.
Merged Units will use Fusion_Data_DefuseOnKilledHP/MP, beaware that if not set they are 0 and the merged units will die.

DefusionAfterDur

The Fusion defuses, if killed/Removed without a killer.

the Data_Remove variable will affect the Fusion.
While waiting for Defusion the Merged Units wait hidden/paused/invulnerable on an specific place on the map.
If an unallowed Defusion would appear the waiting Units will be restored and instantly removed.

Variable Info

Fusion_Data_DefuseDispellMat
on Defuse take away all buffs from the restored Units

Fusion_Data_DefuseDistance
how far away the units will be restored from the Defusion, they spawn in an circle form

Fusion_Data_DefuseSFX
played when defusion triggers, uses death animation tag.

Fusion_Data_DefusionEXPMulti
how much of gained exp will be transfered to the Merged Units. It shares it equaly between all Units no matter if they are hero or not. 1 = 50% for each in a 2 unit fusion.

Fusion_Data_DefuseAfterDur
Will defusie, if dies/Removed without killer.

Fusion_Data_DefuseAfterDurHP
how much % the merged units will have. If you set -1 the take over the fusions one. 1 = 100%; 0 = 0% 0.2 = 20%

Fusion_Data_DefuseOnKilled
Will defuse, if killed with an killer

Fusion_Data_DefuseOnKilledHP
how much % the merged units will have. -1 is not working here. 1 = 100%; 0 = 0%

FusionSaveLoc
is the Location which the waiting Merged Units will be sent to, is set inside UnitFusion Init.
Defusion can be initiated by using an jass call using this function:
JASS:
function UnitDefusion takes unit u, boolean killed, integer indexFusion returns nothing
     //This function will defusion an fusion unit.
     //call it with index -1, if you don't know the Defusion index.
     //call it with null unit if you know the index.
     //killed do this Fusion's OnKilled = true / AfterDur = false


Hero Fusions are supported. I recomment to always set both defusionMods to avoid removing Heroes.
Fusions for heroes are not so different from other Fusions.

Variable Info

Fusion_Data_HeroSumLevel
on default the fusionresult beeing an hero will have the average Level of merged heroes, if you set this the Levels will be summed up.

Fusion_Data_HeroStatsMod
is an integer expecting 3 aviable mods: 0/1/2. 0 default Stats, 1 average Stats of merged Units, 2 sum of Merged Heroes.

Fusion_Data_HeroAllowBoniStats
^^influences mod 1 and 2, if true green numbers will be taken in account

Fusion_Data_DefusionEXPMulti
how much of gained exp will be transfered to the Merged Units. It shares it equaly between all Units no matter if they are hero or not. 1 = 50% for each in a 2 unit fusion.


By using the filters right you will only fuse the units you want.

The System offers 2 Filters and 1 Wildcard Filter you can setup for each Fusion spell individual.
Filters don't have to be used but there might something wierd happen if yon do not use one.
Pre,Main,Wild

Pre and main are Filters Units have to pass to be accepted overall.
Wild is only needed if your Spell uses Wildcard material.

one could use only Pre or Main, but in my opinion there are often repeated conditions like:
  • living ally
  • living enemy
  • Dead
  • Own 50% or more HP
Most Spells will contain 1 of the ^ filters above. But still have some special rule. That's why there is pre and main.

This filter will run for each Unit beeing considered and is addressed with Fusion_Current_FilterUnit.

Such Filters are inside the Spellcasting event therefore one can use any Spell-Event variable one likes to use.


Pre: Regard Ownership, Lifestate.
Main: Regard UnitType/ other things.
Wild: exclude not wanted units.

A Filter is an Trigger only containing an Condition.
Only living Own units will pass.
  • Fusion Filter Own n Alive
    • Events
    • Conditions
      • (Owner of Fusion_Current_FilterUnit) Equal to (Triggering player)
      • (Fusion_Current_FilterUnit is alive) Equal to True
    • Actions
Filters need an ErrorMessage which can be setuped at the FusionSpell creation. The printout is:

Spellname in gold automaticly done
Pre-Msg
Main-Msg
Wild-Msg
Misses: automaticly added.
-Unit List automaticly added.




Events allow you to interact when an Fusion is summoned/Faded in or defuses.

All Fusion-Events using the same Variables, beeing different is the Event Value.

What one could do with an Fusion-Event?
This one will Slow the defused Units from the Captain Fusion which is based on the Spell 2 for 1.
  • Captain DeFusion
    • Events
      • Game - Fusion_Event becomes Equal to -1.00
    • Conditions
      • Fusion_Data_Spell[Fusion_Event_SpellData] Equal to 2 for 1
    • Actions
      • Unit - Create 1 Dummy Caster for Neutral Passive at Fusion_System_SaveLoc facing Default building facing degrees
      • Unit - Add a 0.60 second Generic expiration timer to (Last created unit)
      • Unit - Add Slow to (Last created unit)
      • Unit Group - Pick every unit in Fusion_Event_Group and do (Actions)
        • Loop - Actions
          • Unit - Order (Last created unit) to Human Sorceress - Slow (Picked unit)
The Event Variables
Variable Info

Fusion_Event
Tells you what happened

Fusion_Event_Fusion
The Unit beeing Fused/Fadedin/Defused

Fusion_Event_Group
The merged units.

Fusion_Event_Source
The Spellcaster who started the Fusion.

Fusion_Event_SpellData
Which Fusion Data is used.

Fusion_Event_SpellLevel
The Spell Level this fusion was called.

Fusion_Event_SpellObject
The Fusion Object Handeling the Fusion/Defusion/Fadein

Event Info

Fusion_Event == 1
A non-Hero Fusion was summoned

Fusion_Event == 2
A Hero Fusion was summoned

Fusion_Event == 3
A Fusion Ended Fadein.

Fusion_Event == -1
A Fusion defuses.




Previews
Contents

Unit Fusion V8a (Map)

Reviews
Dr Super Good
Useful, flexible and easy to use unit fusion/splitting system. Could be used for a variety of purposes ranging from fusion mechanics in a DBZ themed map to merging Templars into Archons in a StarCraft themed map. Very GUI friendly to use. Feedback...
Don't take that comment as serious. Here are some thoughts :
1. How about take HP and MP value and get the avg amount to apply at new unit (addon)?
2. What happens to timed fusion? Do the units die after it end?
3. You might want to separate samples from the core, just to make it easier to import.
 
Thanks for the idea input.

The goal is that one only has to define data in an fusion init.

Recreating fusion mats would sadly destroy old states/customizes.
Heroes, added abilities etc​

But now I realize that auto removing/killing fusions parts might be bad for defusions, better would be to hide/pause them and later regain them, but that would bite with (beeing convertered to aloc units or death animation). hmm there is surely an solution without dozens of tricks.
Well, defusion will take a while.
 
Last edited:
If you recreate an unit it will get an new handle (all datas of the unit saved in hashtables would have to be taken over) and an new index in some likely used Unit Indexers too ( that would be easier by seting custom Value).
If one adds abilities with Code they would have to be taken over too.
The bigger problem is actually a hero, which I want to support. A hero gets a new name.
^^ not automatable:
solution is either like you suggest throw an event and let the mapper do the job (which can be alot of work for him)
or not creating new units.
Edit: Updated to V4 defusion will be part of V5.
 
Last edited:
Updated to V7.
V7 offers better Events, the allowed states were replaced by Triggers beeing conditions (more freedom).
Variables are now better structed:
Data, Current, System, Object, Item, Event .
Data/current/Event are for users the important ones.

Data for creation.
Current for Filters / different usage
Event​

and is pretty hard to update.
 
Level 12
Joined
Jun 12, 2010
Messages
413
Definitely an interesting and well put together system. It is unique, could be useful and has an extensive API that would take very long to replicate (thus making the system useful). Since I can't immediately think of anything that the system is missing, I will give it a 5/5 :)

EDIT: Did not test if there were any bugs with ver 7, but there weren't any when I tested it before.
 
I am sorry, I can not test the map at the moment, but I took a quite long look at the code.

  • Hashtable can be used to:
    • Bind instance to SpellTypeId.
    • Bind UnitType(s) of registerable units for an instance.
    • Bind Instance to Fusioned unit.

  • function UnitFusionUse
    • Filter can be static/global instead of being dynamicly created and destroyed.
    • SpellTarget() seems always tried to be added to Fusion_Object_Group, even if it would not exist.
    • One FoG + counter for validated units could be used instead of 3 seperated ForGroup actions.

  • Some functions could be stored into locals, for performance.

  • Attachment point -> configurable.

  • Amount of required/merged units for a fusion can be stored/tracked of. It would simplify some calculations.

  • Could you please elaborate this code part a bit:
    JASS:
    loop 
            if udg_Fusion_Data_UnitTypes[indexCurrent] == 0 then
                set hasWildcard = true
                exitwhen true
            endif   
            set indexCurrent = indexCurrent + 1
            exitwhen indexCurrent > indexEnd
        endloop


  • GetAbilityName() -> GetObjectName()

  • I guess that logics of direct fusion creation can be merged with logics from the function where pulled units timer finished, and the fusion gets created. -> 1 function maybe. Is this possible?

  • Death check for 0.5 life is not idea. Google! ;) ^^

  • I'm not biggest fan from including each logics inside one system that might be avoided, such like those timed features/effects, like transparance. But I guess you want to keep it compact in your system, it's your good choice.

  • The systems looks like it does include some good default features which can be used, such like the onDeath, or timed effect. It makes it simpler to use those features, which is good. On the other side, providing all information, like list of fusioned units, etc + events would be more modular, and user would need to/could define such features himself, if needed.

  • I guess MAX_INVENTORY could be replaced by current max size of unit inventory.

  • The function to care for "best items" of a unit seems like a good idea.

The resource size pretty much increased, and got also harder to review. Not sure I could spot everything, if some other reviewer could practicaly test it, next to the theory approach, it would be cool, too. But I guess it should work pretty much and good.

The system looks good, I like it definitly more than old/other approaches.
 
Last edited:
Thanks for your feedback.
JASS:
loop 
        if udg_Fusion_Data_UnitTypes[indexCurrent] == 0 then
            set hasWildcard = true
            exitwhen true
        endif   
        set indexCurrent = indexCurrent + 1
        exitwhen indexCurrent > indexEnd
    endloop
this is an not used filter for the 3.group action.

Seems like There will be version 8, not sure if sooner or later.
 
Last edited:

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,180
Useful, flexible and easy to use unit fusion/splitting system. Could be used for a variety of purposes ranging from fusion mechanics in a DBZ themed map to merging Templars into Archons in a StarCraft themed map. Very GUI friendly to use.

Feedback:
  • Area of effect fusions do not factor in unit collision radius. For example the Fuse 2 ability might highlight 2 footmen at the edge of the targeting cursor but the system will fail to see them. The solution to this is enumerating units in a larger radius than desired and then filtering them using the IsUnitInRange natives, which do factor in unit collision radius, to get the units in the desired radius.
  • Upon starting the test map a lot of visual effects occur at the middle of the map with the game running at a low frame rate. I am guessing these are part of the preload mechanic however it does look quite ugly. I would suggest moving them to the edges of the map outside the starting camera position unless there is a mechanical reason that they have to be visible.
 
Top