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.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top