Splitting Skull
I was bored so I made this spell. It is MUI but you cannot cast more than 1 instance per unit because it attaches data to the caster.
The spell requires: xebasic, xefx, xecollider, xepreload, GroupUtils, PruneGroup
Description (Taken from tooltip):
Creates a skull speeding at a target unit splitting into more skulls as it hits the targets, damage is based on how many units it has bounced on and therefore weakened.
Btw, I LOOOOVE that structs can act like poiners <3 <3 <3 <3
I was bored so I made this spell. It is MUI but you cannot cast more than 1 instance per unit because it attaches data to the caster.
The spell requires: xebasic, xefx, xecollider, xepreload, GroupUtils, PruneGroup
Description (Taken from tooltip):
Creates a skull speeding at a target unit splitting into more skulls as it hits the targets, damage is based on how many units it has bounced on and therefore weakened.
JASS:
scope SplittingSkull initializer init
// requires xebasic, xefx, xecollider, xepreload, GroupUtils, PruneGroup
/*=============================================================
*
* Splitting Skull by Gwypaas
* v 1.2
*
*
*
* Changelog
*
* v1.0 Initial release
* v1.1 Fixed some small things
* v1.2 Can now use any number of instances on the same unit, also added a model that is displayed
when the target is damaged. (Can be turned off). Does not use any unit indexer anymore.
*
*==============================================================*/
private keyword data
globals
private constant integer SPELL_ID = 039;A000039; // The ability ID
private constant string MODEL_PATH = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl" // Model Path
private constant string ON_HIT_MODEL_PATH = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl" // The model used when a target is hit by the spell.
private constant boolean SHOW_ON_HIT_MODEL = true // If the spell should create any on hit effect or not
private constant real HIT_MODEL_SCALE = .75
private constant real INITIAL_SPEED = 300 // The speed every new projectile starts with
private constant real ACCELERATION = 200 // The projectiles accelaration
private constant real MAX_SPEED = 1500 // The max speed a projectile can reach
private constant real MAX_BOUNCE_RANGE = 500 // The highest possible range it can fire a new missile to when splitting
private constant real EXPIRATION_TIME = 100 // How long time it will be able to follow a unit before removed.
private constant real INITIAL_SCALE = 1.2 // The initial scale of the projectile
private constant real PROJECTILE_Z = 50 // The fly height the projectile uses.
private constant real ANGLE_SPEED = 1 // How fast the projectile can turn when homing in radians. WARNING CAN NOT BE 0!
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL // The attack type the spell uses.
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL// The damage type the spell uses.
private constant weapontype WEAPON_TYPE = null// The weapon type the spell uses.
// Private globals DO NOT TOUCH
private data tmpdata
endglobals
// Use this function to specify the damage beased on lvl and the current bounce.
private constant function Damage takes integer lvl, integer bounce returns real
return (100*lvl)/(bounce*.5)
endfunction
// The amount of bounces based on lvl
private constant function Bounces takes integer lvl returns integer
return 4
endfunction
// The amount of units the projectile can split into upon a hit based on the current bounce.
private constant function Splits takes integer lvl, integer bouce returns integer
return 3
endfunction
// The speed that the splits will start with
private constant function SplitSpeed takes integer bounce, real currSpeed returns real
return 300.
endfunction
// The scale based on the current bounce the splits should have
private constant function NewScale takes integer bounce returns real
return 1.
endfunction
// The units the splits can target.
private function bounceFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(tmpdata.sowner)) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false
endfunction
// Function used to get new targets, modified so it wont select units that already has been targetted
private function LowDistFitnessAndRight takes unit u returns real
local real x
local real y
if IsUnitInGroup(u,tmpdata.targUnits.tUnits) == true then
return 10000000
endif
set x = GetUnitX(u)-tmpdata.x
set y = GetUnitY(u)-tmpdata.y
return -x*x-y*y
endfunction
private struct targettedUnits
integer projCount
group tUnits
static method create takes nothing returns thistype
local thistype tmp = .allocate()
set tmp.projCount = 0
set tmp.tUnits = NewGroup()
return tmp
endmethod
method onDestroy takes nothing returns nothing
call ReleaseGroup(.tUnits)
set .tUnits = null
endmethod
endstruct
private struct data extends xecollider
unit sowner
integer currBounce
targettedUnits targUnits
method dataTerminate takes nothing returns nothing
static if (not SHOW_ON_HIT_MODE) then
set .scale = HIT_MODEL_SCALE
call .flash(ON_HIT_MODEL_PATH)
endif
set targUnits.projCount = targUnits.projCount - 1
if .targUnits.projCount <= 0 then
call .targUnits.destroy()
endif
set .sowner = null
call .terminate()
endmethod
method loopControl takes nothing returns nothing
if GetWidgetLife(.targetUnit) <= .405 then
call .dataTerminate()
endif
endmethod
method onUnitHit takes unit target returns nothing
local data d
local integer i = 0
local unit u
local integer lvl
if target == .targetUnit then
set lvl = GetUnitAbilityLevel(sowner, SPELL_ID)
set .currBounce = .currBounce + 1
call UnitDamageTarget(sowner, .targetUnit, Damage(lvl, currBounce) , false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
if .currBounce <= Bounces(lvl) then
set tmpdata = this
call GroupEnumUnitsInRange(ENUM_GROUP, .x,.y, MAX_BOUNCE_RANGE, function bounceFilter)
call PruneGroup(ENUM_GROUP, LowDistFitnessAndRight, Splits(lvl, .currBounce), NO_FITNESS_LIMIT)
set u = FirstOfGroup(ENUM_GROUP)
loop
exitwhen u == null
if IsUnitInGroup(u, .targUnits.tUnits) == false then
set d = data.create(.x,.y, Atan2((GetUnitY(u)- .y), (GetUnitX(u) - .x)))
set d.targUnits = .targUnits
set .targUnits.projCount = .targUnits.projCount + 1
call GroupAddUnit(d.targUnits.tUnits, u)
set d.sowner = .sowner
set d.currBounce = .currBounce
set d.fxpath = MODEL_PATH
set d.targetUnit = u
set d.angleSpeed = ANGLE_SPEED
set d.owner = .owner
set d.expirationTime = EXPIRATION_TIME
set d.speed = SplitSpeed(.currBounce, .speed)
set d.acceleration = ACCELERATION
set d.maxSpeed = MAX_SPEED
set d.z = PROJECTILE_Z
set d.scale = NewScale(.currBounce)
endif
call GroupRemoveUnit(ENUM_GROUP, u)
set u = null
set u = FirstOfGroup(ENUM_GROUP)
endloop
call GroupClear(ENUM_GROUP)
endif
call .dataTerminate()
endif
set u = null
endmethod
endstruct
function Splitting_Skull_Cast takes unit caster, unit target returns nothing
local real x = GetUnitX(caster)
local real y = GetUnitY(caster)
local data d = data.create(x,y, Atan2((GetUnitY(target)- y), (GetUnitX(target) - x)))
set d.targUnits = targettedUnits.create()
set d.targUnits.projCount = 1
set d.sowner = caster
set d.currBounce = 0
set d.fxpath = MODEL_PATH
set d.targetUnit = target
set d.angleSpeed = ANGLE_SPEED
set d.owner = GetOwningPlayer(caster)
set d.expirationTime = EXPIRATION_TIME
set d.speed = INITIAL_SPEED
set d.acceleration = ACCELERATION
set d.maxSpeed = MAX_SPEED
set d.z = PROJECTILE_Z
set d.scale = INITIAL_SCALE
call GroupAddUnit(d.targUnits.tUnits, target)
set caster = null
set target = null
endfunction
private function cond takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call Splitting_Skull_Cast(GetTriggerUnit(), GetSpellTargetUnit())
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(trig, function cond)
call XE_PreloadAbility(SPELL_ID)
endfunction
endscope
Btw, I LOOOOVE that structs can act like poiners <3 <3 <3 <3