Spell Splitting Skull

Viikuna

No Marlo no game.
Reaction score
265
Or maybe something like:



JASS:
integer array SkullsCreatedByCaster
integer array ActiveSpellInstancesOfCaster
integer array SkullsCreatedByInstance[50][50]

// when spell is casted:

local integer id=GetUnitId(unit)
set ActiveSpellInstancesOfCaster[id]=ActiveSpellInstancesOfCaster[id]+1
set .id=ActiveSpellInstancesOfCaster[id]

// when skull is created:

local integer id=GetUnitId(unit)
set SkullsCreatedByCaster[id]=SkullsCreatedByCaster[id]+1
set SkullsCreatedByInstance[id][.id]=SkullsCreatedByInstance[id][.id]+1


or something like that
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
Made so my current version supports multiple instances on a single unit abusing that structs works more like pointers, too bad it fucks up on first cast and kills the closest units instantly and then does nothing more :/ (The rest of the casts works fine)

Anyone have any idea what the problem might be? This is the current code:
JASS:
scope SplittingSkull initializer init 
// requires xebasic, xefx, xecollider, xepreload, GroupUtils, PruneGroup
/*=============================================================
*    
*              Splitting Skull by Gwypaas
*                        v 1.2
*
*
*
*   Changelog
*
* v1.1 Fixed some small things
*
*==============================================================*/




private keyword data
globals
    private constant integer SPELL_ID = 'A000' // The ability ID
    private constant string MODEL_PATH = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl" // Model Path
    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 ClearTextMessages()
        call ReleaseGroup(.tUnits)
        set .tUnits = null
    endmethod
endstruct


private struct data extends xecollider
    //static integer array projectileCount
    unit sowner
    integer currBounce 
    //group dmgedUnits
    targettedUnits targUnits
    
    method dataTerminate takes nothing returns nothing
        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 
        //call BJDebugMsg("Projectiles: " + I2S(targUnits.projCount))
        //call BJDebugMsg("Proj struct ID: " + I2S(.targUnits))
        if GetWidgetLife(.targetUnit) <= .405 then
            //set id = GetUnitId(.sowner)
            //set data.projectileCount[id] = data.projectileCount[id] - 1
            call .dataTerminate()
        endif
    endmethod
    
    method onUnitHit takes unit target returns nothing
        local data d
        local integer i = 0
        local unit u
        //local integer id
        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)
            //set id = data.projectileCount[GetUnitId(.sowner)]
            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.projCount = d.targUnits.projCount + 1
                        //set d.dmgedUnits = .dmgedUnits 
                        set d.targUnits = .targUnits
                        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()
            
            
            //set .sowner = null
            //set .speed = 0
            //call .terminate()
            
        endif
        
        set u = null
    endmethod
endstruct



private function act takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local unit u1 = GetSpellTargetUnit()
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)

    local data d = data.create(x,y,  Atan2((GetUnitY(u1)- y), (GetUnitX(u1) - x)))
    set d.targUnits = targettedUnits.create()
    set d.targUnits.projCount = 1
    
    
    //set d.dmgedUnits = NewGroup()
    
    set d.sowner = u
    set d.currBounce = 0
    
    set d.fxpath = MODEL_PATH
    set d.targetUnit = u1
    set d.angleSpeed = ANGLE_SPEED
    set d.owner = GetOwningPlayer(u)
    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
    
    
    
    
    //set data.projectileCount[GetUnitId(d.sowner)] = 1
    call GroupAddUnit(d.targUnits.tUnits, u1)

    set u = null
    set u1 = null
endfunction

private function cond takes nothing returns boolean
    if GetSpellAbilityId() == SPELL_ID then
        call act()
    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


Edit - I just realized, should I implement something so you can specify what model you want to show when you hit the target unit and the projectile dies?
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
o_O makes me lose interest already
Why? XE is just one system but I need to specify what I use, most people already use some Unit Indexer and Autoindex can be swapped to almost all unit indexing systems without change of syntax, some systems may force you to change "GetUnitId()" to "GetUnitIndex()" or something like that but nothing major so anyone should really be able to do it, GroupUtils is a library that most maps should already have since it has everything you need for all your group usage. The only libraries that you need to "import" is XE if you don't have it and PruneGroup, both which are really useful for other apllications too. And why would I mimic what PruneGroup does when you can use it efficencly and easy without making any bad looking code or multiple things in the map doing the same.

Also, next version will probably not use any unit indexer becasue I worked around the Unit Indexing part abusing structs to work as pointers to objects instead of objects.



Edit - I found the cause for it only working on first cast, (Later I realized that it fucked up when I tried to use the Same Unit Instancebility part too) and the problem was that the "Targetted Units" group was released prematurely casuing it to target the same units within possible bounce range over and over again until the max amount of bounces was used. I fixed it but the solution was so ugly and bad that I would never release it as a resources because it could possibly fuckup if the player tweaks the constants enough. The solution I tried was: In the onUnitHit method, if the group is null allocate a new one.
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
Double posting now because it has been updated, refer to the changelog for more info.


Btw, what do you think about me adding this feature to the spell: In the configuration header you can specify how long time a projectile stays "inside" a unit waiting to bounce onto the next one.
 

tooltiperror

Super Moderator
Reaction score
231
Good to know your working on Island Defense! :thup:
 

tooltiperror

Super Moderator
Reaction score
231
Maybe it would help if you made the model customizable.
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
It is :banghead: :banghead:

JASS:
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.
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
Bumping makes the world go around the world go around bumping makes the world go around world go around.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top