• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Zephyr Contest #14 - Unique Summoning

Status
Not open for further replies.
Level 11
Joined
Jul 25, 2014
Messages
490
WIP1: Created a dummy unit, the test unit, didn't create the summoned unit yet, will do that later. Also created the configuration part of the code and brought some structure and documentation to it. Here it is:
JASS:
////////////////////// Demonic Ritual v1.00 by Meatmuffin ///////////////////////
// Conjures a flame spirit that circles the caster. Any contact with an enemy  //
// unit will damage it and duplicate the spirit, going into the opposite        //
// direction. Once the initial duration is over, the spirits evolve into       //
// fiery demons, shooting fireballs at nearby enemies.                         //
/////////////////////////////////////////////////////////////////////////////////
//
///////////////////////////// How to import? ////////////////////////////////////
//
// 1. Copy the base ability and the dummy if you don't have these yet;
// 2. Go to File -> Preferences and tick the box that says "Automatically
// create unknown variables when pasting trigger data";
// 3. Copy the folder "Demonic Ritual" including the Variable Creator;
// 4. Configure the spell to suit your needs;
// 5. Enjoy!
/////////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////// CONFIGURATION /////////////////////////////////
//
// The configuration is split into four parts:
//
// 1. General configuration - this configuration part is meant for you to
// match the primary spell aspects to your needs. Hardly needs changing.
//
// 2. Spirit configuration - this part is meant for you to configure the aspects
// of the spirits. You can call it "Phase 1" configuration.
//
// 3. Minion configuration - this part is meant for you to configure the aspects
// of the minions. You can call it "Phase 2" configuration.
//
// 4. Misc configuration - you don't really need to touch this. It is meant to
// increase the readability and efficiency inside the spell code itself.
//
///////////////////////////// General Configuration /////////////////////////////
//
// The timer speed. It is recommended that you do not change this, but there are
// other acceptable values, mostly ranging from 0.03 to 0.04.
constant function DR_TimerSpeed takes nothing returns real
    return 0.31250000
endfunction
//
// The ability ID. This must match the one in your map once you have imported the
// spell. Note that in most cases this will have to be changed.
constant function DR_AbiliyID takes nothing returns integer
    return 'A000'
endfunction
//
// The dummy ID. This must match the one in your map once you have imported the
// spell (if you don't have one already).
constant function DR_DummyID takes nothing returns integer
    return 'h000'
endfunction
//
// The player that we will create the dummies for. This doesn't have to match
// the player that is casting the spell since we store that onCast.
constant function DR_DummyPlayer takes nothing returns player
    return Player(14)
endfunction
//
////////////////////////////// Spirit Configuration /////////////////////////////
//
// The initial duration of the surrounding spirits, before they evolve into
// minions.
constant function DR_SpiritDuration takes nothing returns real
    return 4.00
endfunction
//
// The per level duration of the same as above.
constant function DR_SpiritDurationPerLevel takes nothing returns real
    return 2.50
endfunction
//
// The initial offset of the spirit (the distance between you and the spirits).
constant function DR_SpiritOffset takes nothing returns real
    return 200.00
endfunction
//
// The per level offset of the spirit.
constant function DR_SpiritOffsetPerLevel takes nothing returns real
    return 40.00
endfunction
//
// The initial turn rate of the spirits (how many angles per second does it turn).
constant function DR_SpiritTurnRate takes nothing returns real
    return 135.00 * DR_TimerSpeed()
endfunction
//
// The per level turn rate of the spirits.
constant function DR_SpiritTurnRatePerLevel takes nothing returns real
    return 45.00 * DR_TimerSpeed()
endfunction
//
// The initial damage when the spirits collide with enemies.
constant function DR_SpiritDamage takes nothing returns real
    return 20.00
endfunction
//
// The per level damage of the spirits.
constant function DR_SpiritDamagePerLevel takes nothing returns real
    return 30.00
endfunction
//
// The damage/duplication special effect. It is attached to the unit the spirit
// collided with.
constant function DR_DuplicationFX takes nothing returns string
    return "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
endfunction
//
// The special effect mentioned above, attachment point.
constant function DR_DuplicationAttachmentPoint takes nothing returns string
    return "overhead"
endfunction
//
// The initial max count of the spirits.
constant function DR_SpiritCount takes nothing returns integer
    return 2
endfunction
//
// The per level max count of the spirits.
constant function DR_SpiritCountPerLevel takes nothing returns integer
    return 4
endfunction
//
// The model of the spirit.
constant function DR_SpiritModel takes nothing returns string
    return "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
endfunction
//
// The attachment point of the model.
constant function DR_SpiritModelAttachmentPoint takes nothing returns string
    return "origin"
endfunction
//
// The scaling value (size) of the spirit. 1.00 is the default size.
constant function DR_SpiritSize takes nothing returns real
    return 1.00
endfunction
//
//////////////////////////// Minion Configuration //////////////////////////////
//
// The initial duration of the minions, once they have evolved from spirits.
// Note that once this duration ends, the spell will end as well.
constant function DR_MinionDuration takes nothing returns real
    return 4.00
endfunction
//
// The per level duration of the same as above.
constant function DR_MinionDurationPerLevel takes nothing returns real
    return 4.00
endfunction
//
// The initial movement speed of the minions. 522 is max unless you use a system
// which modifies that.
constant function DR_MinionMS takes nothing returns real
    return 200.00
endfunction
//
// The per level movement speed of the minions.
constant function DR_MinionMSPerLevel takes nothing returns real
    return 100.00
endfunction
//
// The initial radius in which the minions are attacking. If the caster moves out
// of this range, the minions will have to follow him until they reach this radius.
constant function DR_AttackingRadius takes nothing returns real
    return 300.00
endfunction
//
// The per level radius in which the minions are attacking.
constant function DR_AttackingRadiusPerLevel takes nothing returns real
    return 100.00
endfunction
//
// The initial radius in which the minions search for enemies around themselves.
// They choose a random target from a pool of units that are in range of them.
constant function DR_SearchRadius takes nothing returns real
    return 350.00
endfunction
//
// The per level radius in which the minions earch for enemies around themselves.
constant function DR_SearchRadiusPerLevel takes nothing returns real
    return 125.00
endfunction
//
// The minions shoot projectiles in a parabolic curve. This indicates the initial
// speed of the missiles. This does not have the limit of 522.
constant function DR_MissileSpeed takes nothing returns real
    return 400.00 * DR_TimerSpeed()
endfunction
//
// The missile speed per level.
constant function DR_MissileSpeedPerLevel takes nothing returns real
    return 75.00 * DR_TimerSpeed()
endfunction
//
// The height of the parabolic curve.
constant function DR_ParabolaHeight takes nothing returns real
    return 300.00
endfunction
//
// The initial damage when the missile lands.
constant function DR_MissileDamage takes nothing returns real
    return 30.00
endfunction
//
// The missile damage per level.
constant function DR_MissileDamagePerLevel takes nothing returns real
    return 25.00
endfunction
//
// The initial missile explosion AoE.
constant function DR_MissileExplosionAoE takes nothing returns real
    return 50.00
endfunction
//
// The missile explosion AoE per level.
constant function DR_MissileExplosionAoEPerLevel takes nothing returns real
    return 10.00
endfunction
//
// The missile model.
constant function DR_MissileModel takes nothing returns string
    return "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
endfunction
//
// The missile model attachment point.
constant function DR_MissileModelAttachmentPoint takes nothing returns string
    return "origin"
endfunction
//
// The missile explosion effect. The current setting is not having one because
// the missile model itself has an appropriate explosion.
constant function DR_MissileExplosionEffect takes nothing returns string
    return ""
endfunction
//
// The size of the missile.
constant function DR_MissileSize takes nothing returns real
    return 0.40
endfunction
//
//////////////////////////// Misc Configuration //////////////////////////////////
//
// The loop control integer values. It plays a big role in the StageID part of the
// spell, which divides the spell into seperate parts. The StageID variable helps
// implementing all the units (or parts, if you will) into a single linked list.
// It makes the linked list more sensitive when regarding maximum spell instances,
// but it will still be pretty hard to reach the max instance count. I believe the
// credit for this "system" goes to Tank-Commander, not sure, but he taught me 
// how to use it.
//
constant function DR_SpiritStageID takes nothing returns integer
    return 1
endfunction
//
constant function DR_MinionStageID takes nothing returns integer
    return 2
endfunction
//
constant function DR_MissileStageID takes nothing returns integer
    return 3
endfunction
//
//////////////////////////////// END OF CONFIGURATION /////////////////////////////
//
// The code below can be configurable, but it does require some programming
// knowledge.


// Semi-configurable. It filters out the wrong targets when the spirit/missile
// damages.
function DR_TargetFilter takes unit u, player pl returns boolean
    return not(IsUnitType(u, UNIT_TYPE_DEAD) == true) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE) == true) and IsUnitEnemy(u, pl) == true
endfunction


// Function used to get the location of the points used in the spell. It is meant
// to increase the visuals of the missile parabolic arc.
function DR_GetLocationZ takes real x, real y returns real
    return GetLocationZ(udg_DR_Location)
endfunction

// Function used to create a new instance in the linked list.
function DR_CreateNode takes nothing returns integer
    local integer Node
    return Node
endfunction


// Function used to recycle a no longer needed instance in the linked list.
function DR_Recycle takes integer Node returns nothing
    
endfunction


// Main function of the spell, each time it fires, a small part of the spell takes
// place, resulting in a continuous loop of functions.
function DR_Loop takes nothing returns nothing
    
endfunction


// The On Cast function. It is fired when a unit casts this spell. Creates a new
// instance using the DR_CreateNode function and initializes all the needed variables
// and units.
function DR_onCast takes nothing returns boolean
    return false
endfunction


// The initialization function
function InitTrig_Demonic_Ritual takes nothing returns nothing
    local trigger DR = CreateTrigger()
    call TriggerAddCondition(DR, Condition(function DR_onCast))
    call TriggerRegisterAnyUnitEventBJ(DR, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    set udg_DR_Location = Location(0.00, 0.00)
endfunction
319 lines already, hurr-durr. Some aspects of it might change or be added/removed.
 
Last edited:
Level 19
Joined
Jul 2, 2011
Messages
2,161
ok I'll be ready to submit in about an hour

I guess my 5 minutes was an over estimation of my ability, but I must admit this contest got me adding junk I never thought I'd care about^^
 
Level 19
Joined
Jul 2, 2011
Messages
2,161
I decided I can't participate in this event

I shouldn't have made it in my map. I had no idea how much work it would be to transfer the ability over to a blank Map

boy would it be a lot of work.

although I'm just happy that my trap summons idea works. its pretty cool.

it summons an invitbe unit that traps targeted that get near by. slowly torturing the trapped unit to death. on death the trap sends mana and health back to the caster.

perfect machine and it was less than an hour to make ^^
 
Level 7
Joined
Oct 19, 2015
Messages
286
I shouldn't have made it in my map. I had no idea how much work it would be to transfer the ability over to a blank Map
It can't possibly be more work than it was making it from scratch, and like you said:
it was less than an hour to make ^^
You still have a whole month. I'm sure you can find the time to move the spell to a new map. :)
 
Level 19
Joined
Jul 2, 2011
Messages
2,161
this let's see

it's 21 spells to transfer and a few trigs

that will take a whole to transfer but your right not very long

but na I'm happy with just having a cool spell. no need to enter it into a contest
 

Ardenian

A

Ardenian

As long as it is approved, yes.

Meanwhile, BonusMod is approved, tho graveyarded from some reason

Ah I see, thank you. Am always a bit confused about that Zephyr permission asking when using custom resources.
 

Chaosy

Tutorial Reviewer
Level 41
Joined
Jun 9, 2011
Messages
13,239
this let's see

it's 21 spells to transfer and a few trigs

that will take a whole to transfer but your right not very long

but na I'm happy with just having a cool spell. no need to enter it into a contest

If you've coded those 21 traps well it's still just two-three triggers.
 
Level 9
Joined
Oct 14, 2013
Messages
238
Does this leak? I mean do I have to clear the child before I save a new integer?

Untitled.png
 
WIP # 2

JASS:
library ContractOfSuffering/*
*************************************************************************************

    Contract of Suffering
    
        Signs a contract to call on your embodiment of suffering that escorts you in
        battle.
        
        Signs a contract. If the caster have received a certain amount of damage 
        in a given duration, it summons the embodiment. The Embodiment requires 20/25/30
        attacks to be killed, and has a timed life duration of 40/50/60 seconds.
        
        and has 3 abilities that it gains per level:
        Level 1 - Shadow Assault
            The Embodiment strikes a nearby random unit within 800 range, dealing 50/100/150
            damage to it's target and 15/30/45 damage to the units the embodiment passed
            through.
        
            Attack cooldown : 2.5/2/1.5
        
        Level 2 - Veil of Ordeal
            
*************************************************************************************

        */ requires /*
        
        */ MissileRecycler /*
        */ SpellEffectEvent /*
        */ GetUnitCollision /*
        */ MapBounds /*
        */ TimerUtils /*
        */ StunSystem /*
        */ RealEvent /*
        */ Table /*
        */ optional OrderEvent /*
        */ optional ZLibrary/*
        
*************************************************************************************/

    globals
        /*
        *   Timeout of the all timers in this spell
        */
        private constant real TIMEOUT = 0.03125
        /*
        *   Ability id of the Contract of Suffering
        */
        private constant integer ABIL_ID = 0
        /*
        *   All summoned units with respect to their levels
        */
        private constant integer EMB_LEVEL_1_UNIT = 'cos1'
        private constant integer EMB_LEVEL_2_UNIT = 'cos2'
        private constant integer EMB_LEVEL_3_UNIT = 'cos3'
        /*
        *   Damage required to summon the embodiment
        *   Note: The require amount is not necessarily
        *         the exact amount, instead it is the
        *         the total amount of damage received
        *         on the spell's duration
        */
        private constant real REQUIRED_DAMAGE = 600
        private constant real REQUIRED_DAMAGE_PER_LVL = -100
        /*
        *   Spell duration 
        */
        private constant real SPELL_DURATION = 10
        private constant real SPELL_DURATION_PER_LVL = 0
        /**************************************
        *
        *   Embodiment section
        *
        **************************************/
        /*
        *   Movespeed of the embodiment
        */
        private constant real MOVESPEED = 600
        private constant real MOVESPEED_PER_LVL = 0
        /*
        *   Timer period whether the embodiment is idle 
        */
        private constant real EMBODIMENT_IDLE_PERIOD = 1.0
        /*
        *   Max range for the embodiment from it's owner
        */
        private constant real ESCORT_RANGE = 800
        /*
        *   Period to order the embodiment to wander around it's owner
        */
        private constant real ESCORT_WANDER_PERIOD = 0.25
        /*
        *   Does the embodiment return to it's owner if it is
        *   outside the wander range?
        *   Note: Returns after the order has been fulfilled OR
        *         if it is cancelled
        */
        private constant boolean RETURN_IF_OUTRANGE = false
        /*
        *   Embodiment Timed Life
        */
        private constant real TIMED_LIFE = 30
        private constant real TIMED_LIFE_PER_LVL = 10
        /**************************************
        *
        *   Level 1 : Shadow Assault
        *
        **************************************/
        /*
        *   Level requirement
        */
        private constant integer ATTACK_LEVEL_REQ = 1
        /*
        *   Order id of Shadow Assault
        *   Note: the player can also cast this
        *         using this order 
        */
        private constant integer ATTACK_ORDER_ID = 851983 // "attack"
        /*
        *   Range to acquire targets
        */
        private constant real ACQUISITION_RANGE = 800
        /*
        *   If true, it uses the owner's position to search targets
        *   instead of using the embodiment's position
        */
        private constant boolean USE_OWNER_POSITION = false
        /*
        *   Attack cooldown of the embodiment
        */
        private constant real ATTACK_COOLDOWN = 2
        private constant real ATTACK_COOLDOWN_PER_LVL = -0.25
        /*
        *   Range of the attack
        *   Note: the actual range is :
        *   ATTACK_RANGE + target collision size + embodiment collision size
        */
        private constant real ATTACK_RANGE = 0
        /*
        *   Attack damage of the embodiment
        */
        private constant real ATTACK_DAMAGE = 25
        private constant real ATTACK_DAMAGE_PER_LVL = 25 
        private constant attacktype ATK_DMG_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype ATK_DMG_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Does the attack cooldown start if the embodiment has
        *   returned to it's owner?
        *   Note: if false, the attack cooldown starts after
        *         the attack has landed
        */
        private constant boolean START_ATK_COOLDOWN_IF_RETURNED = true
        /*
        *   Does the embodiment cancel it's attack if it is
        *   outside the escort range?
        *   Note: This is useless if CANCEL_ORDERS_IF_OUTRANGE
        *         is false.
        */
        private constant boolean CANCEL_ATK_IF_OUTRANGE = false
        /*
        *   Distance check of the difference of it's target's previous
        *   position to it's current position.
        *   If the difference is larger than ENEMY_ESCAPE_DISTANCE,
        *   the embodiment cancels it's attack
        */
        private constant real ENEMY_ESCAPE_DISTANCE = 1250
        /*
        *   Does the attack cooldown start if it escapes?
        */
        private constant boolean START_ATK_COOLDOWN_IF_ESCAPED = false
        /*
        *   Pass-through damage
        */
        private constant real PASS_DAMAGE = 10
        private constant real PASS_DAMAGE_PER_LVL = 10
        private constant real PASS_DAMAGE_RANGE = 64
        /**************************************
        *
        *   Level 2 : Veil of Ordeal
        *
        **************************************/
        /*
        *   Veil of Ordeal Level Requirement
        */
        private constant integer VEIL_LEVEL_REQ = 2
        /*
        *   Required receive damage to feed the embodiment
        */
        private constant real REQ_DAMAGE = 100
        private constant real REQ_DAMAGE_PER_LVL = 0
        /*
        *   Time to wait for the total damage
        */
        private constant real REQ_DMG_WAIT_TIME = 2
        private constant real REQ_DMG_WAIT_TIME_PER_LVL = 0
        /*
        *   Amount of timed life increased to the embodiment
        *   when fed.
        */
        private constant real TIMED_LIFE_INC = 0
        private constant real TIMED_LIFE_INC_PER_LVL = 1
        /**************************************
        *
        *   Level 3 : Roar of Pain
        *
        **************************************/
        private constant string EMB_MODEL = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
        private constant real EMB_SCALE = 1.5
        private constant integer EMB_RED = 255
        private constant integer EMB_GREEN = 255
        private constant integer EMB_BLUE = 255
        private constant integer EMB_ALPHA = 240
        
        private constant integer EMB_SEGMENT_LENGTH = 8
        private constant string EMB_SEGMENT_MODEL = EMB_MODEL
        private constant real MIN_SCALE = 0.25
        private constant integer EMB_SEGMENT_RED = 255
        private constant integer EMB_SEGMENT_GREEN = 255
        private constant integer EMB_SEGMENT_BLUE = 255
        private constant integer EMB_SEGMENT_ALPHA = 240
        private constant real EMB_SEGMENT_SEPERATION_DIST = 16
    endglobals
    
    private function ShadowAssaultValidTarget takes unit saTarget, player embOwner returns boolean
        /*
        *   Just put "//" before each statement to cancel those conditions
        */
        return     UnitAlive    (saTarget)                     and /*
            */     IsUnitEnemy  (saTarget, embOwner)           and /*
            */     IsUnitVisible(saTarget, embOwner)           and /*
            */ not IsUnitType   (saTarget, UNIT_TYPE_SLEEPING) and /*
            */ not IsUnitType   (saTarget, UNIT_TYPE_ETHEREAL) and /*
            */ not IsUnitType   (saTarget, UNIT_TYPE_STRUCTURE)
    endfunction
    
    private function RoarOfPainValidTarget takes unit ropTarget, player embOwner returns boolean
        return     UnitAlive    (ropTarget)                         and /*
            */     IsUnitEnemy  (ropTarget, embOwner)               and /*
            */     IsUnitVisible(ropTarget, embOwner)               and /*
            */ not IsUnitType   (ropTarget, UNIT_TYPE_MAGIC_IMMUNE) and /*
            */ not IsUnitType   (ropTarget, UNIT_TYPE_STRUCTURE)
    endfunction
    /**************************************
    *
    *   For getting the location surface z
    *
    **************************************/
    static if (not LIBRARY_ZLibrary) then
        globals
            private constant location p = Location(0.0, 0.0)
        endglobals
    
        function GetSurfaceZ takes real x, real y returns real
            call MoveLocation(p, x, y)
            return GetLocationZ(p)
        endfunction
    endif 
    
    /**************************************
    *
    *   List methods (Modules don't work
    *   as intended)
    *
    **************************************/
    //! textmacro COS_LIST
        private static thistype array prev
        private static thistype array next
        /*
        *   Insert node method
        */
        private method insert takes nothing returns nothing
            set next[this] = 0
            set prev[this] = prev[0]
            set next[prev[0]] = this
            set prev[0] = this
        endmethod
        /*
        *   Remove node method
        */
        private method remove takes nothing returns nothing
            set next[prev[this]] = next[this]
            set prev[next[this]] = prev[this]
        endmethod
        /*
        *   Check if list is empty
        */
        static method operator empty takes nothing returns boolean
            return next[0] == 0
        endmethod
        /*
        *   Check if list has only a single instance
        */
        method operator single takes nothing returns boolean
            return prev[this] == this
        endmethod
    //! endtextmacro
    /**************************************
    *
    *   Struct for segments to automatically
    *   adjust their facing.
    *
    **************************************/
    private struct SegmentFace
        /*
        *   u = the main unit
        *   nextU = the unit it's facing angle is based
        *   prevU = for destroying purposes
        */
        readonly unit u
        readonly unit nextU
        readonly unit prevU
        /*
        *   holds the object instance of the units
        */
        private static Table segList
        /*
        *   implement the list
        */
        //! runtextmacro COS_LIST()
        
        private static constant timer t = CreateTimer()
        
        method destroy takes nothing returns nothing
            /*
            *   remove node
            */
            call remove()
            /*
            *   if list is empty, pause timer
            */
            if empty then
                call PauseTimer(t)
            endif
            /*
            *   remove all data
            */
            set segList[GetHandleId(u)] = 0
            set u = null
            set nextU = null
            set prevU = null
        endmethod
        
        static method destroySegment takes unit du returns nothing
            /*
            *   this method is used for chain destruction 
            *   e.g. unit A is destroyed, destroy prev unit B
            */
            call thistype(segList[GetHandleId(du)]).destroy()
        endmethod
        
        private static method period takes nothing returns nothing
            local thistype this = next[0]
            local real ang = 0
            /*
            *   Iterate over instances
            */
            loop
                exitwhen 0 == this
                /*
                *   get the next unit's facing
                */
                set ang = GetUnitFacing(nextU)
                /*
                *   Set the unit's facing towards the facing
                *   of the next unit.
                */
                call SetUnitFacing(u, ang)
                /*
                *   Negate the given angle by rotating it
                *   180 degrees
                */
                set ang = ang*bj_DEGTORAD + bj_PI
                /*
                *   Move the unit to it's seperation distance
                */
                call SetUnitX(u, GetUnitX(nextU) + EMB_SEGMENT_SEPERATION_DIST*Cos(ang))
                call SetUnitY(u, GetUnitY(nextU) + EMB_SEGMENT_SEPERATION_DIST*Sin(ang))
                set this = next[this]
            endloop
        endmethod
        
        static method create takes unit tempU, unit prevLink, unit nextLink returns thistype
            local thistype this
            /*
            *   If the next unit is null, don't create an instance.
            */
            if nextLink == null then
                return thistype(0)
            endif
            /*
            *   allocate an isntance
            */
            set this = allocate()
            /*
            *   insert instance to the list
            */
            call insert()
            /*
            *   set the fields to the respective data
            */
            set u = tempU
            set nextU = nextLink
            set prevU = prevLink
            /*
            *   save the instace to the table
            */
            set segList[GetHandleId(u)] = this
            /*
            *   if the list has only one instance, start the timer.
            */
            if single then
                call TimerStart(t, TIMEOUT, true, function thistype.period)
            endif
            return this
        endmethod
        
        private static method onInit takes nothing returns nothing
            set segList = Table.create()
        endmethod
    endstruct
    
    private function LinearR takes real a, real b, real t returns real
        return a + (b - a)*t
    endfunction
    
    private function LinearInt takes integer a, integer b, real t returns integer
        return R2I(LinearR(I2R(a), I2R(b), t))
    endfunction
    
    private struct Embodiment
        private unit u
        private unit owner
        private effect mdl
        
        private boolean hasShadowAssault
        private boolean hasVeilOfOrdeal
        private boolean hasRoarOfTime
        
        private boolean isIdle
        
        private boolean returning
        
        private boolean attacking
        private unit target
        
        private timer attackTimer
        private timer castTimer
    endstruct
endlibrary

I love it when my spell's name describes the feeling when importing the spell
 
Level 19
Joined
Jul 2, 2011
Messages
2,161
If you've coded those 21 traps well it's still just two-three triggers.

what do you mean? is there a trig requirement of more than 3?

I may still decide to enter the competition...

it's just 21 log ins and outs of the world editor. then I might try and make the actual summoning more dramatic. I see a lot of cool summoning methods and it looks like I'll really have to invent something special to win

I think maybe I'll make the summons burrow? or hmm perhaps once my traps have captured a creature it creates a new monster from the corpse of the dead captured creature?

huh? that sounds kinda cool...
 
Right I've finalised the visuals in my mind and I've built the framework of my spell (about 100 lines sans configurable lines) so I've just code to code the "meat" of it - will edit this post when I've got an idea of how many configurable parts the spell has (aiming to keep it sub 50 but who knows)

Edit: Ok, I've managed to fit in pretty much everything I wanted into 31 configurables
 
Last edited:
Level 19
Joined
Jul 2, 2011
Messages
2,161
I think I've got it

hero summons trap (12 seconds of spell casting)
trap captures creature and borrows
captured creature dies
trap casts doom on self
demon summoned

that sounds awesome right?
ok the trap is summoned by 4 elements

the caster creates a ward and summons 4 creatures into existence who add their life energy into the ward.
thunder lizard
mud golem
water elemental
fire spawn
then the trap is made?

yep that's the plan ^^
 

Chaosy

Tutorial Reviewer
Level 41
Joined
Jun 9, 2011
Messages
13,239
I'm learning interfaces to make the user add features to the system easily without touching the core code. Or modify certain aspects of it.

I think I got the basic concept down, though I had to go to school so I could not finish it.
I will post another code WIP once I get that done.

yep that's the plan ^^
Sounds rather simple, unless the summoned ones are special in some way.
 
Right a quick Code WIP from about 3 hours of intermittently working on it


JASS:
////////////////////////////////////////////////////////////////////
//                       NANO PLAGUE V1.00                        //
//  Author: Tank-Commander                                        //
//  Purpose: Sacrafice yourself to decimate mechanical forces     //
//                                                                //
//  Requires:                                                     //
//    - Custom Stat System (Created by Doomlord)                  //
//      - SimError (Created by Vexorian)                          //
//      - BoundInt (Created by Magtheridon96)                     // 
//      The requirements of the CSS are in the map header         //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  Others mentioned in the requirements                     //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

constant function NP_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction

constant function NP_AbilityID takes nothing returns integer
    return 'A001'
endfunction

constant function NP_InfectAbilityID takes nothing returns integer
    return 'A002'
endfunction

constant function NP_DummyID takes nothing returns integer
    return 'u000'
endfunction

constant function NP_UseTeamColouredBuff takes nothing returns boolean
    return true
endfunction

constant function NP_TCBuffDummyID takes nothing returns integer
    return 'u001'
endfunction

constant function NP_BuffID takes nothing returns integer
    return 'A000'
endfunction

constant function NP_SummonedUnitID takes nothing returns integer
    return 'u002'
endfunction

constant function NP_BuffModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactory.mdl"
endfunction

constant function NP_ProjectileModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactoryMissle.mdl"
endfunction

constant function NP_SpawnBuff takes nothing returns string
    return "Objects\\Spawnmodels\\Other\\ToonBoom\\ToonBoom.mdl"
endfunction

constant function NP_InfectionLimit takes nothing returns integer
    return 3
endfunction

constant function NP_EndOnNoProduction takes nothing returns boolean
    return false
endfunction

constant function NP_BuffHeightOffset takes nothing returns real
    return 150.
endfunction

constant function NP_BuffOffset takes nothing returns real
    return 100.
endfunction

constant function NP_Gravity takes nothing returns real
    return 4.
endfunction

function NP_AOE takes real level returns real
    return 250 + (50 * level)
endfunction

function NP_BuffSize takes real level returns real
    return 0.3 + (0.05 * level)
endfunction

function NP_ProjectileSize takes real level returns real
    return 0.8 + (0.1 * level)
endfunction

function NP_InfectDuration takes real level returns real
    return 4 + (1 * level)
endfunction

function NP_InfectDPS takes real level returns real
    return 25 + (25 * level)
endfunction

function NP_InfectBonusDamageHealth takes real level returns real
    return 30 * level
endfunction

function NP_InfectBonusDamageMana takes real level returns real
    return 0.
endfunction

function NP_InstantDuplicationChance takes real level returns real
    return 0.05 * level
endfunction

function NP_ProductionPS takes real level returns real
    return 3 + (1 * level)
endfunction

function NP_FlightTime takes real level returns real
    return 2 + (-0.2 * level)
endfunction

function NP_SummonDuration takes real level returns real
    return 6 + (2 * level)
endfunction

function NP_SummonSize takes real level returns real
    return 0.4 + (0.1 * level)
endfunction

function NP_SummonHealthBonus takes real level returns integer
    return R2I(125 * level)
endfunction

function NP_SummonHealthRegenBonus takes real level returns integer
    return R2I(0.1 * level)
endfunction

function NP_SummonArmourBonus takes real level returns integer
    return R2I(-1 + (1 * level))
endfunction

function NP_SummonAttackDamageBonus takes real level returns integer
    return R2I(10 * level)
endfunction

function NP_summonAttackSpeedBonus takes real level returns integer
    return R2I(0)
endfunction

constant function NP_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction

constant function NP_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction

constant function NP_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  Function used to get the Z location height at a given point   //
////////////////////////////////////////////////////////////////////
function NP_GetLocationZ takes real x, real y returns real

    call MoveLocation(udg_NP_ZLoc, x, y)
    return GetLocationZ(udg_NP_ZLoc)
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to remove instances from the linked list once   //
//  they have expired                                             //
////////////////////////////////////////////////////////////////////
function NP_RecycleNode takes integer Node returns nothing
    set udg_NP_RecycleNodes[udg_NP_RecyclableNodes] = Node
    set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes + 1
    set udg_NP_NextNode[udg_NP_PrevNode[Node]] = udg_NP_NextNode[Node]
    set udg_NP_PrevNode[udg_NP_NextNode[Node]] = udg_NP_PrevNode[Node]
    
    if (udg_NP_PrevNode[0] == 0) then
        call PauseTimer(udg_NP_Timer)
    endif
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add new nodes to the linked list used by the //
//  system                                                        //
////////////////////////////////////////////////////////////////////
function NP_CreateNode takes nothing returns integer
    local integer Node

    if (udg_NP_RecyclableNodes == 0) then
        set udg_NP_NodeNumber = udg_NP_NodeNumber + 1
        set Node = udg_NP_NodeNumber
    else
        set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes - 1
        set Node = udg_NP_RecycleNodes[udg_NP_RecyclableNodes]
    endif
    
    set udg_NP_NextNode[Node] = 0
    set udg_NP_NextNode[udg_NP_PrevNode[0]] = Node
    set udg_NP_PrevNode[Node] = udg_NP_PrevNode[0]
    set udg_NP_PrevNode[0] = Node
    
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to run the main engine of the spell, handles    //
//  creation of new units, buff control and projectile control    //
////////////////////////////////////////////////////////////////////
function NP_Loop takes nothing returns nothing

endfunction

////////////////////////////////////////////////////////////////////
//  Function used to tell if the passed unit is infected with a   //
//  specific instance                                             //
////////////////////////////////////////////////////////////////////
function NP_IsInfected takes unit u, integer instance returns boolean
    local integer Node = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0 or udg_NP_Instance[Node] == instance
    endloop
    
    return udg_NP_Instance[Node] == instance
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to get the total amount of infections a given   //
//  unit currently has                                            //
////////////////////////////////////////////////////////////////////
function NP_GetInfections takes unit u returns integer
    local integer Node = 0
    local integer infections = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0
        
        if udg_NP_Unit[Node] == u then
            set infections = infections + 1
        endif
        
    endloop
    
    return infections 
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to attribute each infection to the original     //
//  unique cast of the ability, as each unit can be infected      //
//  multiple times, this is only used for getting summoned units  //
////////////////////////////////////////////////////////////////////
function NP_GetNode takes unit u returns integer
    local integer Node = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0 or udg_NP_Unit[Node] == u
    endloop
    
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to start an instance of the plague              //
////////////////////////////////////////////////////////////////////
function NP_StartPlague takes nothing returns boolean
    //Set up locals
    local integer Node
    local integer SpellID = GetSpellAbilityId()
    local integer instance
    local unit u
    local unit u2
    local boolean bNew = false
    
    //Check if something was infected
    if SpellID == NP_AbilityID() or SpellID == NP_InfectAbilityID() then
        set u = GetSpellTargetUnit()      
        
        set udg_NP_Unit[Node] = GetSpellTargetUnit()
        
        //Check if a the unit was infected by a summoned unit or by itself
        if SpellID == NP_InfectAbilityID() then
            set u2 = GetSpellAbilityUnit()
            
            set instance = NP_GetNode(u2)
            //Check if the unit has been infected from this instance before
            if NP_IsInfected(u, udg_NP_Instance[instance]) or (NP_GetInfections(u) >= NP_InfectionLimit()) then
                //Deal bonus damage when attempting to infect
                call UnitDamageTarget(u2, u, udg_NP_HealthDamage[instance], true, false, NP_AttackType(), NP_DamageType(), NP_WeaponType())
                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_NP_ManaDamage[instance])
            else
                //Remove the infecting unit
                set udg_NP_Instance[Node] = udg_NP_Instance[instance]
                call RemoveUnit(udg_NP_Unit[instance])
                call NP_RecycleNode(instance)
                set bNew = true
            endif
            
            set u2 = null
        elseif NP_GetInfections(u) < NP_InfectionLimit() then
            set bNew = true
        endif
        
        //Check if a new instance needs to be created
        if (bNew) then
            set Node = NP_CreateNode()
            
            if udg_NP_PrevNode[Node] == 0 then
                call TimerStart(udg_NP_Timer, NP_TimerSpeed(), true, function NP_Loop)
            endif  
            
            set udg_NP_Unit[Node] = u
            
            if SpellID == NP_InfectAbilityID() then
                set udg_NP_Instance[Node] = udg_NP_Instance[instance]
            else
                set udg_NP_Instance[Node] = Node
            endif
            
        endif
        
        set u = null
    endif
    
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to register the trigger of the ability          //
////////////////////////////////////////////////////////////////////
function InitTrig_Nano_Plague takes nothing returns nothing
    //Set up local variables
    local trigger t = CreateTrigger()

    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function NP_StartPlague))
    set udg_NP_ZLoc = Location(0., 0.)
    set t = null
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL                                                //
////////////////////////////////////////////////////////////////////


been working primarily on the infection logic in the NP_StartPlague function, currently nothing actually happens (noting that at most I just create an instance and do absolutely nothing with it) but once I set up the plague effect for the original caster it should automatically work for all infected units and automatically have the limits and other features (such as bonus damage when already infected or at the infection limit and infecting if not) also already implemented

Which leaves me with just summon creation and plague timeout and then I'll be done (which is admittedly most of the heavy lifting, but less brain work)

concept changes:
adding: Watch my 6 - You can target yourself instead of a location to lock production to follow your location instead of the target point
adding: Who did this?! - Nanobot infection buff has team colour
adding: Strength in numbers - Every Nanobot has additional attack points for every currently living Nanobot in the swarm (belonging to the same unique cast), production nanobots (infections) count
removing: Enemy override - There's little point to it since in most cases it makes no difference and adds a lot of needless complexity
 
Last edited:
leftover initTrig name, I convert the old spell map to the new spell map every time I make new abilities to save time with importing all the data and getting started (terrain is a pain and all that) - I clear those things up normally when I check for vanilla compatibility - if people checked closely enough you'd find I sometimes forget to update names on test maps since I get no errors

Edit: corrected it
 
Last edited:

Ardenian

A

Ardenian

Seems this is going to be a though contest.

Only have some code garbage right now, might post a WIP later the day.
 
bit of a teaser WIP since there's not much else to visually see at this point

I've finished making the "custom buff" part of my spell which permits me to team colour my buffs for each player (uses an extra dummy, but can be configured to be turned off removing the need for it) and make it clear how many infections are on one unit

on the left is 3 level 1 viruses infecting one unit, on the right is 1 level 3 virus infecting one unit (What model I use for the infection buff may change, but given they essentially turned things into nanobot factories pocket factory seemed appropriate), currently at 579 lines of code

naturally, they remain at the correct height regardless of the terrain angle you're stood on

Edit: Factories now produce nanobot-boxes (boxes are immobile at the moment) also set up for instant duplication completed
currently at 655 lines

here's a current Code WIP to go with it

JASS:
////////////////////////////////////////////////////////////////////
//                       NANO PLAGUE V1.00                        //
//  Author: Tank-Commander                                        //
//  Purpose: Sacrafice yourself to decimate mechanical forces     //
//                                                                //
//  Requires:                                                     //
//    - Custom Stat System (Created by Doomlord)                  //
//      - SimError (Created by Vexorian)                          //
//      - BoundInt (Created by Magtheridon96)                     // 
//      The requirements of the CSS are in the map header         //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  Others mentioned in the requirements                     //
//                                                                //
//  If you have used this spell in your map, you are required     //
//  to give credits to Tank-Commander for the creation of it      //
//  If you would like to use snippets of code from this for       //
//  whatever, getting permission and crediting the source/linking //
//  would be much appreciated.                                    //
////////////////////////////////////////////////////////////////////

constant function NP_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction

constant function NP_AbilityID takes nothing returns integer
    return 'A001'
endfunction

constant function NP_InfectAbilityID takes nothing returns integer
    return 'A002'
endfunction

constant function NP_DummyID takes nothing returns integer
    return 'u000'
endfunction

constant function NP_UseTeamColouredBuff takes nothing returns boolean
    return true
endfunction

constant function NP_TCBuffDummyID takes nothing returns integer
    return 'u001'
endfunction

constant function NP_BuffID takes nothing returns integer
    return 'A000'
endfunction

constant function NP_BuffFacing takes nothing returns real
    return 270.
endfunction

constant function NP_SummonedUnitID takes nothing returns integer
    return 'u002'
endfunction

constant function NP_DummyPlayerID takes nothing returns player
    return Player(14)
endfunction

constant function NP_AttachmentPoint takes nothing returns string
    return "origin"
endfunction

constant function NP_BuffModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactory.mdl"
endfunction

constant function NP_BuffSpawnModel takes nothing returns string
    return "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl"
endfunction

constant function NP_ProjectileModel takes nothing returns string
    return "Units\\Creeps\\HeroTinkerFactory\\HeroTinkerFactoryMissle.mdl"
endfunction

constant function NP_SpawnBuffModel takes nothing returns string
    return "Objects\\Spawnmodels\\Other\\ToonBoom\\ToonBoom.mdl"
endfunction

constant function NP_InfectionLimit takes nothing returns integer
    return 3
endfunction

constant function NP_EndOnNoProduction takes nothing returns boolean
    return false
endfunction

constant function NP_BuffHeightOffset takes nothing returns real
    return 150.
endfunction

constant function NP_BuffOffset takes nothing returns real
    return 70.
endfunction

constant function NP_Gravity takes nothing returns real
    return 4.
endfunction

function NP_AOE takes real level returns real
    return 250 + (50 * level)
endfunction

function NP_BuffSize takes real level returns real
    return 0.2 + (0.05 * level)
endfunction

function NP_ProjectileSize takes real level returns real
    return 0.8 + (0.1 * level)
endfunction

function NP_ProjectileFlightTime takes real level returns real
    return 2 + (-0.2 * level)
endfunction

function NP_InfectDuration takes real level returns real
    return 4 + (1 * level)
endfunction

function NP_InfectDPSHealth takes real level returns real
    return 25 + (25 * level)
endfunction

function NP_InfectDPSMana takes real level returns real
    return 0.
endfunction

function NP_InfectBonusDamageHealth takes real level returns real
    return 30 * level
endfunction

function NP_InfectBonusDamageMana takes real level returns real
    return 0.
endfunction

function NP_InstantDuplicationChance takes real level returns real
    return 0.05 * level
endfunction

function NP_ProductionPS takes real level returns real
    return 3 + (1 * level)
endfunction

function NP_SummonDuration takes real level returns real
    return 6 + (2 * level)
endfunction

function NP_SummonSize takes real level returns real
    return 0.4 + (0.1 * level)
endfunction

function NP_SummonHealthBonus takes integer level returns integer
    return 125 * level
endfunction

function NP_SummonHealthRegenBonus takes integer level returns integer
    return 1 * level
endfunction

function NP_SummonArmourBonus takes integer level returns integer
    return -1 + (1 * level)
endfunction

function NP_SummonAttackDamageBonus takes integer level returns integer
    return 10 * level
endfunction

function NP_SummonAttackSpeedBonus takes integer level returns integer
    return 0
endfunction

function NP_SwarmStrengthAttackBonus takes integer SwarmStrength returns integer
    return R2I(1.5 * SwarmStrength)
endfunction

constant function NP_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction

constant function NP_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction

constant function NP_WeaponType takes nothing returns weapontype
    return null
endfunction

constant function NP_InfectionStageID takes nothing returns integer
    return 1
endfunction

constant function NP_ProjectileStageID takes nothing returns integer
    return 2
endfunction

constant function NP_NanobotStageID takes nothing returns integer
    return 3
endfunction

constant function NP_BuffStageID takes nothing returns integer
    return 4
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  Function used to get the Z location height at a given point   //
////////////////////////////////////////////////////////////////////
function NP_GetZ takes real x, real y returns real

    call MoveLocation(udg_NP_ZLoc, x, y)
    return GetLocationZ(udg_NP_ZLoc)
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to remove instances from the linked list once   //
//  they have expired                                             //
////////////////////////////////////////////////////////////////////
function NP_RecycleNode takes integer Node returns nothing
    set udg_NP_RecycleNodes[udg_NP_RecyclableNodes] = Node
    set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes + 1
    set udg_NP_NextNode[udg_NP_PrevNode[Node]] = udg_NP_NextNode[Node]
    set udg_NP_PrevNode[udg_NP_NextNode[Node]] = udg_NP_PrevNode[Node]
    
    if (udg_NP_PrevNode[0] == 0) then
        call PauseTimer(udg_NP_Timer)
    endif
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add new nodes to the linked list used by the //
//  system                                                        //
////////////////////////////////////////////////////////////////////
function NP_CreateNode takes nothing returns integer
    local integer Node

    if (udg_NP_RecyclableNodes == 0) then
        set udg_NP_NodeNumber = udg_NP_NodeNumber + 1
        set Node = udg_NP_NodeNumber
    else
        set udg_NP_RecyclableNodes = udg_NP_RecyclableNodes - 1
        set Node = udg_NP_RecycleNodes[udg_NP_RecyclableNodes]
    endif
    
    set udg_NP_NextNode[Node] = 0
    set udg_NP_NextNode[udg_NP_PrevNode[0]] = Node
    set udg_NP_PrevNode[Node] = udg_NP_PrevNode[0]
    set udg_NP_PrevNode[0] = Node
    
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to tell if the passed unit is infected with a   //
//  specific instance                                             //
////////////////////////////////////////////////////////////////////
function NP_IsInfected takes unit u, integer instance returns boolean
    local integer Node = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0 or udg_NP_Instance[Node] == instance
    endloop
    
    return udg_NP_Instance[Node] == instance
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to get the total amount of infections a given   //
//  unit currently has                                            //
////////////////////////////////////////////////////////////////////
function NP_GetInfections takes unit u returns integer
    local integer Node = 0
    local integer infections = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0
        
        if udg_NP_Unit[Node] == u then
            set infections = infections + 1
        endif
        
    endloop
    
    return infections 
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to attribute each infection to the original     //
//  unique cast of the ability, as each unit can be infected      //
//  multiple times, this is only used for getting summoned units  //
////////////////////////////////////////////////////////////////////
function NP_GetNode takes unit u returns integer
    local integer Node = 0
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0 or udg_NP_Unit[Node] == u
    endloop
    
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to move all buffs on an infected unit when a    //
//  buff has either been added or removed from the unit           //
////////////////////////////////////////////////////////////////////
function NP_MoveBuffs takes unit u, integer buffCount returns nothing
    //Set up locals
    local integer Node = 0
    local real angle = 0.
    local real increment = (bj_PI * 2) / buffCount
    local integer tempInt = 0
    
    //Locate all buffs belonging to this unit
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen tempInt == buffCount
        
        if udg_NP_Caster[Node] == u and udg_NP_StageID[Node] == NP_BuffStageID() then
            
            //Set up their new position
            if buffCount > 1 then
                set udg_NP_Offset[Node] = NP_BuffOffset()
                set angle = angle + increment
                set udg_NP_BuffAngle[Node] = angle
            else
                set udg_NP_Offset[Node] = 0.
                set udg_NP_BuffAngle[Node] = 0.
            endif
            
            set tempInt = tempInt + 1
        endif
            
    endloop
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to remove the infection buff from units and     //
//  subsequently rearrange all others using the MoveBuff function //
////////////////////////////////////////////////////////////////////
function NP_DestroyBuff takes integer Node, unit u returns nothing
    local integer infections = NP_GetInfections(u)    
    
    //Remove the buff and recycle it
    call NP_RecycleNode(Node)
    call KillUnit(udg_NP_Unit[Node])
    
    if not(NP_UseTeamColouredBuff()) then
        call DestroyEffect(udg_NP_CurrentEffect[Node])
    endif
    
    //Rearrange remaining buffs
    if infections > 1 then
        call NP_MoveBuffs(u, infections - 1)
    endif
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to add the infection buff to units and          //
//  subsequently rearrange all others using the MoveBuff function //
////////////////////////////////////////////////////////////////////
function NP_CreateBuff takes unit u, integer buffCount, real level returns integer
    //set up locals
    local integer Node = NP_CreateNode()
    
    //Create new buff
    if NP_UseTeamColouredBuff() then
        set udg_NP_Unit[Node] = CreateUnit(NP_DummyPlayerID(), NP_TCBuffDummyID(), GetUnitX(u), GetUnitY(u), NP_BuffFacing())
        call SetUnitColor(udg_NP_Unit[Node], GetPlayerColor(GetOwningPlayer(u)))
    else
        set udg_NP_Unit[Node] = CreateUnit(NP_DummyPlayerID(), NP_DummyID(), GetUnitX(u), GetUnitY(u), NP_BuffFacing())
        set udg_NP_CurrentEffect[Node] = AddSpecialEffectTarget(NP_BuffModel(), udg_NP_Unit[Node], NP_AttachmentPoint())
    endif
    
    //Set up buff data
    if UnitAddAbility(udg_NP_Unit[Node], 'Amrf') and UnitRemoveAbility(udg_NP_Unit[Node], 'Amrf') then
    endif
    call SetUnitScale(udg_NP_Unit[Node], NP_BuffSize(level), 0., 0.)
    call SetUnitFlyHeight(udg_NP_Unit[Node], GetUnitFlyHeight(u) + NP_BuffHeightOffset(), 0.)
    set udg_NP_Caster[Node] = u
    set udg_NP_StageID[Node] = NP_BuffStageID()
    //Create spawn effect on the affected unit
    call DestroyEffect(AddSpecialEffectTarget(NP_BuffSpawnModel(), u, NP_AttachmentPoint()))
    
    //Move all Buffs to fit around the circle
    call NP_MoveBuffs(u, buffCount)
    
    //Return the newly created buff
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to produce new nanobots either from a virus     //
//  or from the instant duplication effect                        //
////////////////////////////////////////////////////////////////////
function NP_CreateProjectile takes real x, real y, real z, integer Node returns nothing
    local integer tempNode = NP_CreateNode()
    local real x2
    local real y2
    local real offset
    local real angle = GetRandomReal(-bj_PI, bj_PI)
    
    //Get target centre
    if udg_NP_TargetSelf[Node] then
        set x2 = GetUnitX(udg_NP_Unit[Node])
        set y2 = GetUnitY(udg_NP_Unit[Node])
    else
        set x2 = udg_NP_TargetX[Node]
        set y2 = udg_NP_TargetY[Node]
    endif
    
    //Get random location from the target point (Uniform distribution)
    set offset = GetRandomReal(0, udg_NP_AOE[Node]) +  GetRandomReal(0, udg_NP_AOE[Node])
    
    if offset > udg_NP_AOE[Node] then
        set offset = 2 * udg_NP_AOE[Node] - offset
    endif
    
    set x2 = x2 + offset * Cos(angle)
    set y2 = y2 + offset * Sin(angle)
    
    //Create projectile
    set udg_NP_StageID[tempNode] = NP_ProjectileStageID()
    set udg_NP_Unit[tempNode] = CreateUnit(NP_DummyPlayerID(), NP_DummyID(), x, y, angle * bj_RADTODEG)
    call SetUnitScale(udg_NP_Unit[tempNode], udg_NP_ProjectileSize[Node], 0., 0.)
    set udg_NP_CurrentEffect[tempNode] = AddSpecialEffectTarget(NP_ProjectileModel(), udg_NP_Unit[tempNode], NP_AttachmentPoint())
    if UnitAddAbility(udg_NP_Unit[tempNode], 'Amrf') and UnitRemoveAbility(udg_NP_Unit[tempNode], 'Amrf') then
    endif
    call DestroyEffect(AddSpecialEffectTarget(NP_SpawnBuffModel(), udg_NP_Unit[tempNode], NP_AttachmentPoint()))
    call SetUnitFlyHeight(udg_NP_Unit[tempNode], z, 0.)
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to run the main engine of the spell, handles    //
//  creation of new units, buff control and projectile control    //
////////////////////////////////////////////////////////////////////
function NP_Loop takes nothing returns nothing
    //Set up locals
    local integer Node = 0
    local integer tempNode = 0
    local real x
    local real y
    local real x2
    local real y2
    
    loop
        set Node = udg_NP_NextNode[Node]
        exitwhen Node == 0
        
        if udg_NP_StageID[Node] == NP_InfectionStageID() then
            //Check if the infection is over
            if udg_NP_InfectionDuration[Node] > 0. and not(IsUnitType(udg_NP_Unit[Node], UNIT_TYPE_DEAD)) then
                //Deal infection damage
                call UnitDamageTarget(udg_NP_Caster[Node], udg_NP_Unit[Node], udg_NP_InfectionDamageHealth[Node], true, false, NP_AttackType(), NP_DamageType(), NP_WeaponType())
                call SetUnitState(udg_NP_Unit[Node], UNIT_STATE_MANA, GetUnitState(udg_NP_Unit[Node], UNIT_STATE_MANA) - udg_NP_InfectionDamageMana[Node])
            
                set udg_NP_InfectionDuration[Node] = udg_NP_InfectionDuration[Node] - NP_TimerSpeed()
                
                //Check if it's time to produce a new nanobot
                if udg_NP_CurrentProductionDelay[Node] <= 0. then
                    set udg_NP_CurrentProductionDelay[Node] = udg_NP_ProductionDelay[Node]
                    
                    //Produce new nanobot
                    call NP_CreateProjectile(GetUnitX(udg_NP_Unit[udg_NP_BuffNode[Node]]), GetUnitY(udg_NP_Unit[udg_NP_BuffNode[Node]]), GetUnitFlyHeight(udg_NP_Unit[udg_NP_BuffNode[Node]]), Node)
                else
                    set udg_NP_CurrentProductionDelay[Node] = udg_NP_CurrentProductionDelay[Node] - NP_TimerSpeed()
                endif
                
            else
                call NP_DestroyBuff(udg_NP_BuffNode[Node], udg_NP_Unit[Node])
                call NP_RecycleNode(Node)
                
                //If the unit has no other infections, remove the buff
                if NP_GetInfections(udg_NP_Unit[Node]) == 0 then
                    call UnitRemoveAbility(udg_NP_Unit[Node], NP_BuffID())
                endif
                
            endif
            
        elseif udg_NP_StageID[Node] == NP_ProjectileStageID() then
            //Move projectiles
            
        elseif udg_NP_StageID[Node] == NP_NanobotStageID() then
            //Calculate bonuses
            
        else
            //Update location of Buffs
            set x = GetUnitX(udg_NP_Caster[Node])
            set y = GetUnitY(udg_NP_Caster[Node])
            set x2 = x + udg_NP_Offset[Node] * Cos(udg_NP_BuffAngle[Node])
            set y2 = y + udg_NP_Offset[Node] * Sin(udg_NP_BuffAngle[Node])
            call SetUnitX(udg_NP_Unit[Node], x2)
            call SetUnitY(udg_NP_Unit[Node], y2)
            call SetUnitFlyHeight(udg_NP_Unit[Node], GetUnitFlyHeight(udg_NP_Caster[Node]) + NP_BuffHeightOffset() + (NP_GetZ(x, y) - NP_GetZ(x2, y2)), 0.)
        endif
        
    endloop
    
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to start an instance of the plague              //
////////////////////////////////////////////////////////////////////
function NP_StartPlague takes nothing returns boolean
    //Set up locals
    local integer Node
    local integer SpellID = GetSpellAbilityId()
    local integer instance
    local integer infections
    local integer ilevel
    local real level
    local unit u
    local unit u2
    local boolean bNew = false
    local boolean bTargetSelf = false
    
    //Check if something was infected
    if SpellID == NP_AbilityID() or SpellID == NP_InfectAbilityID() then
        set u = GetSpellTargetUnit()      
        set u2 = GetTriggerUnit()
                
        //Check if a the unit was infected by a summoned unit or by itself
        if SpellID == NP_InfectAbilityID() then
            set infections = NP_GetInfections(u)
            set instance = NP_GetNode(u2)
            //Check if the unit has been infected from this instance before
            if NP_IsInfected(u, udg_NP_Instance[instance]) or (infections >= NP_InfectionLimit()) then
                //Deal bonus damage when attempting to infect
                call UnitDamageTarget(u2, u, udg_NP_HealthDamage[instance], true, false, NP_AttackType(), NP_DamageType(), NP_WeaponType())
                call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_NP_ManaDamage[instance])
                
                //Instantly create a clone
                if GetRandomReal(0, 1) <= udg_NP_DuplicationChance[udg_NP_Instance[instance]] then
                    call NP_CreateProjectile(GetUnitX(udg_NP_Unit[instance]), GetUnitY(udg_NP_Unit[instance]), GetUnitFlyHeight(udg_NP_Unit[instance]), udg_NP_Instance[instance])
                endif
                
            else
                //Remove the infecting unit
                set udg_NP_Instance[Node] = udg_NP_Instance[instance]
                set bTargetSelf = true
                call RemoveUnit(udg_NP_Unit[instance])
                call NP_RecycleNode(instance)
                set bNew = true
            endif
            
        else
        
            //Prevent targetting of other units
            if not(u == u2) then
                set u = u2
            else
                set bTargetSelf = true
            endif
            
            set infections = NP_GetInfections(u)
            
            if infections < NP_InfectionLimit() then
                set bNew = true
            endif
            
        endif
        
        //Check if a new instance needs to be created
        if (bNew) then
            //Initialise Noe
            set Node = NP_CreateNode()
            
            if udg_NP_PrevNode[Node] == 0 then
                call TimerStart(udg_NP_Timer, NP_TimerSpeed(), true, function NP_Loop)
            endif  
            
            //Pass instance along if the unit was infected by an existing plague
            if SpellID == NP_InfectAbilityID() then
                set udg_NP_Instance[Node] = udg_NP_Instance[instance]
                set udg_NP_Caster[Node] = udg_NP_Unit[instance]
            else
                set udg_NP_Instance[Node] = Node
                set udg_NP_Caster[Node] = u
                set udg_NP_SwarmStrength[Node] = 1
            endif

            //Add buff if this is the first infection
            if infections == 0 then
                call UnitAddAbility(udg_NP_Unit[Node], NP_BuffID())
            endif
            
            //Set up plague data
            set level = GetUnitAbilityLevel(u2, SpellID)
            set ilevel = R2I(level)
                        
            set udg_NP_Unit[Node] = u
            set udg_NP_BuffNode[Node] = NP_CreateBuff(u, infections + 1, level)
            set udg_NP_AOE[Node] = NP_AOE(level)
            set udg_NP_TargetSelf[Node] = bTargetSelf
            set udg_NP_TargetX[Node] = GetSpellTargetX()
            set udg_NP_TargetY[Node] = GetSpellTargetY()
            set udg_NP_ProjectileSize[Node] = NP_ProjectileSize(level)
            set udg_NP_ProjectileFlightTime[Node] = NP_ProjectileFlightTime(level)
            set udg_NP_ProductionDelay[Node] = 1 / NP_ProductionPS(level)
            set udg_NP_CurrentProductionDelay[Node] = udg_NP_ProductionDelay[Node]
            set udg_NP_InfectionDuration[Node] = NP_InfectDuration(level)
            set udg_NP_InfectionDamageHealth[Node] = NP_InfectDPSHealth(level) * NP_TimerSpeed()
            set udg_NP_InfectionDamageMana[Node] = NP_InfectDPSMana(level) * NP_TimerSpeed()
            set udg_NP_DuplicationChance[Node] = NP_InstantDuplicationChance(level)
            set udg_NP_HealthDamage[Node] = NP_InfectBonusDamageHealth(level)
            set udg_NP_ManaDamage[Node] = NP_InfectBonusDamageMana(level)
            set udg_NP_SummonDuration[Node] = NP_SummonDuration(level)
            set udg_NP_SummonSize[Node] = NP_SummonSize(level)
            set udg_NP_SummonHealth[Node] = NP_SummonHealthBonus(ilevel)
            set udg_NP_SummonHealthRegen[Node] = NP_SummonHealthRegenBonus(ilevel)
            set udg_NP_SummonArmour[Node] = NP_SummonArmourBonus(ilevel)
            set udg_NP_SummonAttackDamage[Node] = NP_SummonAttackDamageBonus(ilevel)
            set udg_NP_SummonAttackSpeed[Node] = NP_SummonAttackSpeedBonus(ilevel)
            set udg_NP_StageID[Node] = NP_InfectionStageID()
        endif
        
        set u2 = null
        set u = null
    endif
    
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to register the trigger of the ability          //
////////////////////////////////////////////////////////////////////
function InitTrig_Nano_Plague takes nothing returns nothing
    //Set up local variables
    local trigger t = CreateTrigger()

    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function NP_StartPlague))
    set udg_NP_ZLoc = Location(0., 0.)
    
    set t = null
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL                                                //
////////////////////////////////////////////////////////////////////
 

Attachments

  • WIP1.png
    WIP1.png
    187.9 KB · Views: 78
Last edited:

Ardenian

A

Ardenian

Not complete basic code to show, so here is my WIP tooltip:

View attachment 154281

To rephrase, hero channels, dummies pop up, player clicks dummies,
dependent on dummy type the hero gets additional stats, hero finishes channeling,
summons some units with percentage of additional hero stats.
 
Last edited by a moderator:

Chaosy

Tutorial Reviewer
Level 41
Joined
Jun 9, 2011
Messages
13,239
Not complete basic code to show, so here is my WIP tooltip:

View attachment 154281

To rephrase, hero channels, dummies pop up, player clicks dummies,
dependent on dummy type the hero gets additional stats, hero finishes channeling,
summons some units with percentage of additional hero stats.

I really like that.
Does that mean that if you click on all of them I will get a lot of bonuses, or 1 max?
 

Ardenian

A

Ardenian

Sounds epic, Ardenian. A lot of room for fancy effects :D

On a side note, I want to see some photos/GIFS of these spells. Screw these code WIPs, reading isn't fun!
Hehe, yes, it is going to be a long conf ^^

How do you create your awesome GIFs ?
I really like that.
Does that mean that if you click on all of them I will get a lot of bonuses, or 1 max?
Hm, I thought a lot of this point.
I am a bit indecisive, as static dummies to click is a bit boring.
Currently, once you click a unit, a reducing value gets added ( e.g. 4, 3.9, 3.8, ...) to a variable, for example damage, being added after the channel as additional stats during Metamorphosis.
One could think about adding a maximum of total units allowed to click or adding a maximum bonus per bonus type.
Another way could be to kill a dummy unit upon selecting, re-spawning and randomizing them newly.
 
Level 4
Joined
Sep 13, 2014
Messages
106
Well, if you choose to have in mind that the "player selects unit" event is kinda buggy if I recall.

I think if you hold in left click and select all units with the rectangle-tool (or whatever it's called) the event will fire for all units in that rectangle at once.

I'm not sure that is a bug, I think it is intended. Each unit gets added to the selection individually.

My concept is a spell which creates a ritual circle in which the more units die, the stronger the demon that is summoned.
 
Need more WIP animations you say? alright - I've finished nanobot production boxes movement (going to modify the scaling a little bit as to not obscure the buff quite so much while stationary - it's clearer while moving) infection effect (currently set to charm so I can clearly see when it's worked) will probably change

I used a uniform distribution for determining where the projectiles land as you can see in the GIF (not the normal amount of clustering you get around the center)
reminds me a bit of fireworks XD

WIP%20GIF.jpg


Edit: Boxes now summon my nanobots - just got to allow the nanobots to infect things and implement the strength in numbers mechanic and the spell is functionally completed (or where it all falls apart because some of my logic is missing), also going to need to run a prime on map initialisation just to make sure no lag occurs on the first cast of the spell

Edit2: Here's some images of a nanobot swarm created by a level 3 cast (note the low stats for each nanobot independently, the swarm bonus is giving +23.25 damage in that screenshot which is ceilinged to 24, each one by itself has 15 damage without the bonus)

Current nanobot stats:
Level: 1/2/3
HP: 50/100/150
Regen: 1/2/3
Armour: 0/1/2
Damage: 5/10/15
Attack Speed: 1/1/1
Duration: 8/10/12 seconds
 

Attachments

  • WIP2.png
    WIP2.png
    403 KB · Views: 47
  • WIP3.png
    WIP3.png
    421.9 KB · Views: 53
Last edited:
Level 11
Joined
Dec 19, 2012
Messages
411
WIP#2 from me :

Struggling for finding a nice sphere, and ends up using FarSeer missile effect...


giphy.gif



JASS:
scope ElementalChaos initializer Init

//================================================================================
//============================Configurable Part===================================
//================================================================================
	globals
		//The spell id of the spell
		private	constant integer				SPELL_ID 						= 'A000'
		
		//Dummy unit id
		private constant integer				DUMMY_UNIT_ID					= 'h000'
		
		//Timer updating time constant
		private constant real		 			TIMER_TIMEOUT					= 0.03125
		
		//Sphere effect model path
		private constant string					SPHERE_EFFECT_PATH				= "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
		
		//Sphere effect attachment point
		private constant string					SPHERE_EFFECT_ATTACHMENT_PATH	= "origin"
		
		//Color adjustment - red color component of sphere
		private	constant integer				SPHERE_COLOR_RED				= 70
		
		//Color adjustment - green color component of sphere
		private	constant integer				SPHERE_COLOR_GREEN				= 200
		
		//Color adjustment - blue color component of sphere
		private constant integer				SPHERE_COLOR_BLUE				= 255
		
		//Transparency adjustment - transparency of sphere
		private	constant integer				SPHERE_TRANSPARENCY				= 255
		
		//Initial scale of the sphere before generating start
		private	constant real					SPHERE_INITIAL_SCALE			= 0.
		
		//Sphere particle effect stop spawn timing (those little particles move toward the sphere)
		private constant real					SPHERE_PARTICLE_STOP_TIMING		= 1.
		
		//Sphere particle maximum scale
		private constant real					SPHERE_PARTICLE_MAX_SCALE		= 2.5
		
		//Sphere particle minimum scale
		private constant real					SPHERE_PARTICLE_MIN_SCALE		= 1.0
		
		//Sphere particle maximum distance from sphere
		private constant real					SPHERE_PARTICLE_MAX_DIST		= 300.
		
		//Sphere particle minimum distance from sphere
		private constant real					SPHERE_PARTICLE_MIN_DIST		= 200.
		
		//Sphere particle maximum height distance from shpere
		private constant real					SPHERE_PARTICLE_MAX_HEIGHT		= 200.
		
		//Sphere particle minimum height fistance from sphere
		private constant real					SPHERE_PARTICLE_MIN_HEIGHT		= 100.
		
		//Sphere particle maximum speed moving toward sphere (horizontal)
		//(verticle) speed is dependent on the (horizontal) speed and height (distance) between particle and sphere
		private constant real					SPHERE_PARTICLE_MAX_SPEED		= 32.
		
		//Sphere particle minimum speed moving toward sphere (horizontal)
		private constant real					SPHERE_PARTICLE_MIN_SPEED		= 16.
		
		//Sphere particle dissapear (remove) distance between sphere and sphere particle
		private constant real 					SPHERE_PARTICLE_END_DIST		= 50.
		
		//Sphere particle effect model path
		private constant string					SPHERE_PARTICLE_EFFECT_PATH		= "war3mapImported\\Stars_lightning_.MDX"
		
		//Sphere particle effect attachment point
		private constant string					SPHERE_PARTICLE_ATTACHMENT_PATH = "origin"
		
		//Sphere height (Configurable at function Init)
		private			 real		 array		SPHERE_HEIGHT
		
		//Sphere maximum scale (Configurable at function Init)
		private			 real		 array		SPHERE_MAX_SCALE
		
		//Sphere generates time (Configurable at function Init)
		private			 real		 array		SPHERE_GENERATE_TIME
		
	endglobals
	
	private function Init takes nothing returns nothing
		//[1] indicates Lv1, [2] indicates Lv2 and [3] indicates Lv3
		//Arrays are used instead of globals for preventing overwhelming globals as well as unnessasary coding.
		
		//Setting sphere height
		set SPHERE_HEIGHT[1] = 500.
		set SPHERE_HEIGHT[2] = 500.
		set SPHERE_HEIGHT[3] = 500.
		
		//Setting sphere maximum scale
		set SPHERE_MAX_SCALE[1] = 2.5
		set SPHERE_MAX_SCALE[2] = 3.0
		set SPHERE_MAX_SCALE[3] = 3.0
		
		//Setting sphere generates time
		set SPHERE_GENERATE_TIME[1] = 3.0
		set SPHERE_GENERATE_TIME[2] = 2.5
		set SPHERE_GENERATE_TIME[3] = 2.0
	endfunction
//================================================================================
//==========================End Configurable Part=================================
//================================================================================





//================================================================================
//===========================Non-Configurable Part================================
//================================================================================
	globals
	endglobals
	
	private function UnitAddRemoveCrowForm takes unit u returns nothing
		if UnitAddAbility(u, 'Amrf') then
			call UnitRemoveAbility(u, 'Amrf')
		endif
	endfunction
//================================================================================
//=========================End Non-Configurable Part==============================
//================================================================================


	//-----------------------------------------------------------------
	//-----------------------------------------------------------------
	//A linked list created by myself
	private module LinkedList
		static method create takes nothing returns thistype
			local thistype this = instanceCount + 1
			set instanceCount = this
			
			if thistype(0).next == 0 then
				set thistype(0).next = this
				set lastNode = this
			else
				set lastNode.next = this
				set this.prev = lastNode
				
				set lastNode = this
			endif
			
			set this.next = 0
			set thistype(0).prev = this
			
			return this
		endmethod
		
		method destroy takes nothing returns nothing
			set this.prev.next = this.next
			set this.next.prev = this.prev
			
			if this.next == 0 and this.prev == 0 then
				set instanceCount = 0
				
				//call instanceClear if exists
				static if thistype.instanceClear.exists then
					call thistype.instanceClear()
				endif
			elseif lastNode == this then
				set lastNode = this.prev
			endif
			
		endmethod
	endmodule
	//-----------------------------------------------------------------
	//-----------------------------------------------------------------
	
	
	
	private struct ElementalSummon extends array
		//============Linked List Globals=================
		
		//spell's instance count
		private 	static  thistype 	instanceCount = 0
		
		//last allocated instance
		private 	static	thistype 	lastNode
		
		private 			thistype	next
		private 			thistype	prev
		//================================================
		implement LinkedList
	endstruct
	
	
	
	
	
	private struct SphereParticleEffect extends array
		//==========Sphere Side Effect Globals============
					static	timer		timerSPE			= CreateTimer()
		
							unit		dummy
							
							effect		e //effect attached to particle
							
							real		x //y coordinate of particle
							real		y //x coordinate of particle
							real		distance //distance between sphere and particle
							real		height //height between sphere and particle
							real		speedh //speed horizontal
							real		speedv //speed verticle
							
							real		destinationHeight //sphere height
							real		destinationX //sphere x coordinate
							real		destinationY //sphere y coordinate
		//================================================
		
		//============Linked List Globals=================
		
		//spell's instance count
		private 	static  thistype 	instanceCount = 0
		
		//last allocated instance
		private 	static	thistype 	lastNode
		
		private 			thistype	next
		private 			thistype	prev
		//================================================
		
		private static method instanceClear takes nothing returns nothing
			call PauseTimer(timerSPE)
		endmethod
		
		implement LinkedList
		
		static method onPeriodic takes nothing returns nothing
			local thistype this = thistype(0).next
			local real angle
			local real x2
			
			loop
				exitwhen this == 0
				
				if this.distance >= SPHERE_PARTICLE_END_DIST then
					set this.distance = this.distance - this.speedh
					
					set x2 = GetUnitX(this.dummy)-this.destinationX
					set angle = Atan2(GetUnitY(this.dummy)-this.destinationY, x2)
					
					//move the particle toward sphere (verticle)
					set this.height = this.height - this.speedv
					call SetUnitFlyHeight(this.dummy, this.destinationHeight + this.height * Cos(Atan2(this.destinationHeight-this.height,x2)), 0.)
					
					//move the particle toward sphere (horizontal)
					set this.x = this.x - this.speedh * Cos(angle)
					set this.y = this.y - this.speedh * Sin(angle)
					
					call SetUnitX(this.dummy, this.x)
					call SetUnitY(this.dummy, this.y)
					
				else
					call DestroyEffect(this.e)
					call RemoveUnit(this.dummy)
					
					set this.e = null
					set this.dummy = null
					
					call this.destroy()
				endif
				set this = this.next
			endloop
		endmethod
	endstruct
	
	
	
	
	
	private struct ElementalChaos extends array
		//============Elemental Chaos Globals=============
		private		static	timer		timerEC					= CreateTimer() //Elemental Chaos Timer
		
		private				unit		triggerUnit
		private				unit		sphereDummy
		
		private				effect		sphereEffect
		
		private				player		owner
		
		private				integer		spellLv
		
		private				integer		phase //spell phase
		
		private				real		targetX //spell target X
		private				real		targetY //spell target Y
		private				real		spellDuration
		private				real		sphereScale
		private				real		sphereScaleConst //sphere scale increasement constant every TIMER_TIMEOUT seconds
		
		private				real		sphereGenerateTime
		private				real		sphereHeight
		//================================================
		
		
		//============Linked List Globals=================
		
		//spell's instance count
		private 	static  thistype 	instanceCount = 0
		
		//last allocated instance
		private 	static	thistype 	lastNode
		
		private 			thistype	next
		private 			thistype	prev
		//================================================
		//instanceClear will be called if no more instance is running
		private static method instanceClear takes nothing returns nothing
			call PauseTimer(timerEC)
		endmethod
		
		implement LinkedList
		
		private static method onPeriodic takes nothing returns nothing
			local thistype this = thistype(0).next
			local SphereParticleEffect particleEffect
			local real rand
			
			loop
				exitwhen this == 0
				
				set spellDuration = spellDuration + TIMER_TIMEOUT
				//phase 0 indicates sphere generating phase
				if this.phase == 0 then
					//=================================================================================
					//Sphere Generate Effect of the sphere (the effect shows when sphere is generating)
					//=================================================================================
					//if (sphere generate time - passed spell timing) >= SPHERE_PARTICLE_STOP_TIMING
					if (this.sphereGenerateTime-this.spellDuration) >= SPHERE_PARTICLE_STOP_TIMING then
						set particleEffect = SphereParticleEffect.create()
						
						//Setting up particle data
						set particleEffect.destinationHeight	= this.sphereHeight
						set particleEffect.destinationX			= this.targetX
						set particleEffect.destinationY			= this.targetY
						set particleEffect.distance				= GetRandomReal(SPHERE_PARTICLE_MIN_DIST, SPHERE_PARTICLE_MAX_DIST)
						set particleEffect.height				= GetRandomReal(SPHERE_PARTICLE_MIN_HEIGHT, SPHERE_PARTICLE_MAX_HEIGHT)
						set particleEffect.speedh				= GetRandomReal(SPHERE_PARTICLE_MIN_SPEED, SPHERE_PARTICLE_MAX_SPEED)
						//speedv = height / time
						//time = ditance / speedh
						set particleEffect.speedv				= particleEffect.height/(particleEffect.distance/particleEffect.speedh)
						set particleEffect.x					= this.targetX + particleEffect.distance * Cos(GetRandomReal(0., 2*bj_PI))
						set particleEffect.y					= this.targetY + particleEffect.distance * Sin(GetRandomReal(0., 2*bj_PI))
						set particleEffect.dummy				= CreateUnit(this.owner, DUMMY_UNIT_ID, particleEffect.x, particleEffect.y, 0)
						set particleEffect.e					= AddSpecialEffectTarget(SPHERE_PARTICLE_EFFECT_PATH, particleEffect.dummy, SPHERE_PARTICLE_ATTACHMENT_PATH)
						
						//Set unit scale randomly
						set rand = GetRandomReal(SPHERE_PARTICLE_MIN_SCALE, SPHERE_PARTICLE_MAX_SCALE)
						call SetUnitScale(particleEffect.dummy, rand, rand, rand)
						
						//add and remove crow form
						call UnitAddRemoveCrowForm(particleEffect.dummy)
						
						//rendomly obtain a number so that height can be appear lower or higher thn the sphere
						if GetRandomInt(0, 1) == 1 then
							set particleEffect.height = particleEffect.height * -1
						endif
						
						//set unit height
						call SetUnitFlyHeight(particleEffect.dummy, this.sphereHeight + particleEffect.height, 0)
					
						if particleEffect == 1 then
							call TimerStart(SphereParticleEffect.timerSPE, TIMER_TIMEOUT, true, function SphereParticleEffect.onPeriodic)
						endif
						
						//Setting sphere scale, reason it is inside the block is to prevent sphere increasing scale when no more particle effects
						//are generated.
						set this.sphereScale = this.sphereScale + this.sphereScaleConst
						call SetUnitScale(this.sphereDummy, this.sphereScale, this.sphereScale, this.sphereScale)
					endif
					//=================================================================================
					//===========================End Sphere Generate Effect============================
					//=================================================================================
					
					//Check if the spell duration >= sphere generate time
					if this.spellDuration >= this.sphereGenerateTime then
						//Sphere generate phase ends, switch spell phase to 1
						set this.phase = 1
					endif
				elseif this.phase == 1 then
					//--------------
				endif
				
				set this = this.next
			endloop
			
		endmethod
		
		private static method onCast takes nothing returns boolean
			local thistype this
			
			if GetSpellAbilityId() == SPELL_ID then
				set this = thistype.create() //create a new instance
				
				//Storing data
				set this.triggerUnit				= GetTriggerUnit()
				set this.owner						= GetOwningPlayer(this.triggerUnit)
				set this.targetX					= GetSpellTargetX()
				set this.targetY					= GetSpellTargetY()
				set this.spellLv					= GetUnitAbilityLevel(this.triggerUnit, SPELL_ID)
				set this.phase						= 0
				set this.sphereGenerateTime			= SPHERE_GENERATE_TIME[this.spellLv]
				set this.sphereHeight				= SPHERE_HEIGHT[this.spellLv]
				set this.sphereScale				= SPHERE_INITIAL_SCALE
													//sphereScaleConst is dependent on SPHERE_MAX_SCALE, SPHERE_GENERATE_TIME, TIMER_TIMEOUT and SHPERE_PARTICLE_STOP_TIMING
													//(this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING) : as sphere stop increase scale if no more particle is generating
													// divide by TIMER_TIMEOUT : to obtain the overall running times
													// SPHERE_MAX_SCALE[this.spellLv] : obtain the maximum scale of the sphere
													// maximum scale / running times : so that a constant is obtained for scale increasement every TIMER_TIMEOUT
				set this.sphereScaleConst 			= SPHERE_MAX_SCALE[this.spellLv]/((this.sphereGenerateTime-SPHERE_PARTICLE_STOP_TIMING)/TIMER_TIMEOUT)
				set this.spellDuration				= 0.
				set this.sphereDummy 				= CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX, this.targetY, 0.)
				
				//Add and remove crow form
				call UnitAddRemoveCrowForm(this.sphereDummy)
				
				//Set the unit height
				call SetUnitFlyHeight(this.sphereDummy, this.sphereHeight, 0.)
				
				//Setting the sphere to initial scale value
				call SetUnitScale(this.sphereDummy, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE)
				
				//Setting the sphere color
				call SetUnitVertexColor(this.sphereDummy, SPHERE_COLOR_RED, SPHERE_COLOR_GREEN, SPHERE_COLOR_BLUE, SPHERE_TRANSPARENCY)
				
				//Add the sphere effect to the unit
				set this.sphereEffect = AddSpecialEffectTarget(SPHERE_EFFECT_PATH, this.sphereDummy, SPHERE_EFFECT_ATTACHMENT_PATH)
				
				if this == 1 then
					call TimerStart(timerEC, TIMER_TIMEOUT, true, function thistype.onPeriodic)
				endif
			endif
			return false
		endmethod
		
		private static method onInit takes nothing returns nothing
			local trigger t = CreateTrigger()
			call TriggerAddCondition(t, Condition(function thistype.onCast))
			call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
			
			set t = null
		endmethod
	endstruct

endscope
 

Did anybody just notice that the boxes has caused a spinning-like illusion?

I mean all of the boxes create an illusion that it spins/orbits on the target point :V

Try using Mana Flare or Possession (Target) with 0,0,255 color :D

Possession is the best model for spheres and also easy to color with(due to it's white light)
 
Level 11
Joined
Dec 19, 2012
Messages
411
Almia said:
Try using Mana Flare or Possession (Target) with 0,0,255 color :D

Possession is the best model for spheres and also easy to color with(due to it's white light)
Thanks for your suggestions :) Mana Flare <target> seems to be the most appropriate model, but it doesn't looks like a whole sphere :/ (just partially). I personally think FarSeer missile effect would be better than Mana Flare <target> though.

Possession has certain height, so when unit get scaled, it height will also get scaled. End up inaccurate height from the ground, that's the main problem for me to struggle with the models.
 
@DD_LegionTN - I know your struggle all too well XD

@Almia - huh, didn't actually notice that though doesn't really look like it any longer since the boxes open the moment they touch the ground

Edit: Another WIP - The buffs linger on units (even if they die) so long as the unique cast persists, preventing them from being infected again by the same unique cast
 

Attachments

  • WIP4.png
    WIP4.png
    638.4 KB · Views: 74
Last edited:

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
Wow, so much entries. I would like to join but I hardly find a good idea lately. xD

- Anitarf (code)
- Bribe (visuals)
- PurgeandFire (concept)
Does it mean there will be only one review then?

Anyway, looking at some WIPs here, I think the theme has become a little bit fuzzy. I thought we are supposed to create living unit summoning spell. But now it looks like that launching missiles spell is also acceptable, isn't it? Like a meteor spell, the caster summons the meteor, so it's a summoning spell. I think it makes the theme lil bit less "new". But if it's indeed allowed, I think I have an idea here then. xD mwehehe
 
WIP #3

JASS:
library ContractOfSuffering/*
*************************************************************************************

    Contract of Suffering
    
        Signs a contract to call on your embodiment of suffering that escorts you in
        battle.
        
        Signs a contract. If the caster have received a certain amount of damage 
        in a given duration, it summons the embodiment. The Embodiment requires 20/25/30
        attacks to be killed, and has a timed life duration of 40/50/60 seconds.
        
        and has 3 abilities that it gains per level:
        Level 1 - Shadow Assault
            The Embodiment strikes a nearby random unit within 800 range, dealing 50/100/150
            damage to it's target and 15/30/45 damage to the units the embodiment passed
            through.
        
            Attack cooldown : 2.5/2/1.5
        
        Level 2 - Veil of Ordeal
            
*************************************************************************************

        */ requires /*
        
        */ MissileRecycler /*
        */ SpellEffectEvent /*
        */ GetUnitCollision /*
        */ MapBounds /*
        */ TimerUtils /*
        */ StunSystem /*
        */ RealEvent /*
        */ Table /*
        */ optional OrderEvent /*
        */ optional ZLibrary/*
        
*************************************************************************************/

    globals
        /*
        *   Timeout of the all timers in this spell
        */
        private constant real TIMEOUT = 0.03125
        /*
        *   Ability id of the Contract of Suffering
        */
        private constant integer ABIL_ID = 0
        /*
        *   All summoned units with respect to their levels
        */
        private constant integer EMB_LEVEL_1_UNIT = 'cos1'
        private constant integer EMB_LEVEL_2_UNIT = 'cos2'
        private constant integer EMB_LEVEL_3_UNIT = 'cos3'
        /*
        *   Damage required to summon the embodiment
        *   Note: The require amount is not necessarily
        *         the exact amount, instead it is the
        *         the total amount of damage received
        *         on the spell's duration
        */
        private constant real REQUIRED_DAMAGE = 600
        private constant real REQUIRED_DAMAGE_PER_LVL = -100
        /*
        *   Spell duration 
        */
        private constant real SPELL_DURATION = 10
        private constant real SPELL_DURATION_PER_LVL = 0
        /**************************************
        *
        *   Embodiment section
        *
        **************************************/
        /*
        *   Movespeed of the embodiment
        */
        private constant real MOVESPEED = 600
        private constant real MOVESPEED_PER_LVL = 0
        /*
        *   After the embodiment has received an order,
        *   idle check starts. If after a given period
        *   (in this case, the constant below) and 
        *   the embodiment has not received any order, 
        *   it is now considered as idle.
        */
        private constant real IDLE_PERIOD = 1.0
        /*
        *   Idle Period Order interval
        */
        private constant real IDLE_ORDER_PERIOD = 1.0
        /*
        *   Max range for the embodiment from it's owner
        */
        private constant real ESCORT_RANGE = 800
        /*
        *   Period to order the embodiment to wander around it's owner
        */
        private constant real ESCORT_WANDER_PERIOD = 0.25
        /*
        *   Does the embodiment return to it's owner if it is
        *   outside the wander range?
        *   Note: Returns after the order has been fulfilled OR
        *         if it is cancelled
        */
        private constant boolean RETURN_IF_OUTRANGE = false
        /*
        *   If the embodiment is ouside the escort range, do
        *   you want it to forcefully return to the owner?
        *   This is regardless of whether the embodiment is 
        *   casting, idle or being controlled.
        */
        private constant boolean FORCE_RETURN = false
        /*
        *   Embodiment Timed Life
        */
        private constant real TIMED_LIFE = 30
        private constant real TIMED_LIFE_PER_LVL = 10
        /*
        *   Attack counter check
        */
        private constant boolean ALLOW_HERO_DAMAGE = true
        private constant boolean ALLOW_UNIT_DAMAGE = true
        private constant boolean ALLOW_STRUCTURE_DAMAGE = true
        /*
        *   Range from the owner to consider the embodiment
        *   that is has returned
        */
        private constant real RETURN_RANGE = 128
        /**************************************
        *
        *   Level 1 : Shadow Assault
        *
        **************************************/
        /*
        *   Level requirement
        */
        private constant integer ATTACK_LEVEL_REQ = 1
        /*
        *   Order id of Shadow Assault
        *   Note: the player can also cast this
        *         using this order 
        */
        private constant integer ATTACK_ORDER_ID = 851983 // "attack"
        /*
        *   Range to acquire targets
        */
        private constant real ACQUISITION_RANGE = 800
        /*
        *   If true, it uses the owner's position to search targets
        *   instead of using the embodiment's position
        */
        private constant boolean USE_OWNER_POSITION = false
        /*
        *   Does the embodiment cancel it's return order to attack?
        */
        private constant boolean ATTACK_CANCEL_RETURN = false
        /*
        *   Attack cooldown of the embodiment
        */
        private constant real ATTACK_COOLDOWN = 2
        private constant real ATTACK_COOLDOWN_PER_LVL = -0.25
        /*
        *   Range of the attack
        *   Note: the actual range is :
        *   ATTACK_RANGE + target collision size + embodiment collision size
        */
        private constant real ATTACK_RANGE = 0
        /*
        *   Attack damage of the embodiment
        */
        private constant real ATTACK_DAMAGE = 25
        private constant real ATTACK_DAMAGE_PER_LVL = 25 
        private constant attacktype ATK_DMG_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype ATK_DMG_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Does the attack cooldown start if the embodiment has
        *   returned to it's owner?
        *   Note: if false, the attack cooldown starts after
        *         the attack has landed
        */
        private constant boolean START_ATK_COOLDOWN_IF_RETURNED = false
        /*
        *   Does the embodiment cancel it's attack if it is
        *   outside the escort range?
        *   Note: This is useless if CANCEL_ORDERS_IF_OUTRANGE
        *         is false.
        */
        private constant boolean CANCEL_ATK_IF_OUTRANGE = false
        /*
        *   Distance check of the difference of it's target's previous
        *   position to it's current position.
        *   If the difference is larger than ENEMY_ESCAPE_DISTANCE,
        *   the embodiment cancels it's attack
        */
        private constant real ENEMY_ESCAPE_DISTANCE = 1250
        /*
        *   Does the attack cooldown start if it escapes?
        */
        private constant boolean START_ATK_COOLDOWN_IF_ESCAPED = false
        /*
        *   Can the attack be stopped/cancelled by the player?
        *   Note : through the order "Stop", the attack is cancelled.
        */
        private constant boolean ATTACK_CANCELABLE = true
        /*
        *   Does the attack cooldown start if the attack is cancelled
        *   by the player?
        */
        private constant boolean START_ATK_COOLDOWN_IF_CANCEL = true
        /*
        *   Pass-through damage
        *   Pass-through damage uses the same attacktype 
        *   and damagetype to shadow assault
        */
        private constant real PASS_DAMAGE = 10
        private constant real PASS_DAMAGE_PER_LVL = 10
        private constant real PASS_DAMAGE_RANGE = 64
        private constant real PASS_DAMAGE_RANGE_PER_LVL = 0
        /**************************************
        *
        *   Level 2 : Veil of Ordeal
        *
        **************************************/
        /*
        *   Veil of Ordeal Level Requirement
        */
        private constant integer VEIL_LEVEL_REQ = 2
        /*
        *   Required receive damage to feed the embodiment
        */
        private constant real REQ_DAMAGE = 100
        private constant real REQ_DAMAGE_PER_LVL = 0
        /*
        *   Time to wait for the total damage
        */
        private constant real REQ_DMG_WAIT_TIME = 2
        private constant real REQ_DMG_WAIT_TIME_PER_LVL = 0
        /*
        *   Amount of timed life increased to the embodiment
        *   when fed.
        */
        private constant real TIMED_LIFE_INC = 0
        private constant real TIMED_LIFE_INC_PER_LVL = 1
        /**************************************
        *
        *   Level 3 : Roar of Pain
        *
        **************************************/
        /*
        *   Roar of Pain required level
        */
        private constant integer ROAR_LEVEL_REQ = 3
        /*
        *   Order Id of Roar of Pain
        */
        private constant integer ROAR_ORDER_ID = 851971 // smart
        /*
        *   Number of units allowed to proc the effect
        */
        private constant integer ROAR_UNIT_COUNT = 3
        /*
        *   Radius and offset for unit checking
        *   Note: offset is front of the embodiment
        */
        private constant real ROAR_UNIT_CHECK_RADIUS = 256
        private constant real ROAR_UNIT_CHECK_OFFSET = 256
        /*
        *   Cast time for Roar of Pain
        */
        private constant real ROAR_CAST_TIME = 1.5
        private constant real ROAR_CAST_TIME_PER_LVL = 0
        /*
        *   Speed of Roar of Pain
        */
        private constant real ROAR_SPEED = 500
        private constant real ROAR_SPEED_PER_LVL = 0
        /*
        *   Range of Roar of Pain
        */
        private constant real ROAR_TRAVEL_RANGE = 1000
        private constant real ROAR_TRAVEL_RANGE_PER_LVL = 0
        /*
        *   Roar of Pain radius
        */
        private constant real ROAR_RADIUS = 256
        private constant real ROAR_RADIUS_PER_LVL = 0
        /*
        *   Roar of Pain damage
        */
        private constant real ROAR_DAMAGE = 300
        private constant real ROAR_DAMAGE_PER_LVL = 0
        private constant attacktype ROAR_DAMAGE_ATKTYPE = ATTACK_TYPE_CHAOS
        private constant damagetype ROAR_DAMAGE_DMGTYPE = DAMAGE_TYPE_UNIVERSAL
        /*
        *   Roar stun duration
        */
        private constant real ROAR_STUN_DURATION = 2
        private constant real ROAR_STUN_DURATION_PER_LVL = 0
        /*
        *   Roar cooldown
        */
        private constant real ROAR_COOLDOWN = 15
        private constant real ROAR_COOLDOWN_PER_LVL = 0
        /*
        *   Does the embodiment cancel it's cast
        *   if outside the escort range?
        */
        private constant boolean CANCEL_ROAR_IF_OUTRANGE = false
        /*
        *   Can the player cancel the cast?
        */
        private constant boolean ROAR_CANCELABLE = true
        /*
        *   Does the roar cooldown start if cancelled?
        */
        private constant boolean START_ROAR_COOLDOWN_IF_CANCEL = false 
        /**************************************
        *
        *   SFX AND APPEARANCE SECTION
        *
        **************************************/
        /*
        *   Embodiment Appearance
        */
        private constant string EMB_MODEL = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
        private constant real EMB_SCALE = 1.5
        private constant integer EMB_RED = 255
        private constant integer EMB_GREEN = 255
        private constant integer EMB_BLUE = 255
        private constant integer EMB_ALPHA = 240
        /*
        *   Embodiment's segment appearances
        *   Take note that the MIN_SCALE refers to the
        *   model scale of the final unit.
        *   Starting from EMB_SCALE to MIN_SCALE,
        *   the calculated scale is linear to it.
        */
        private constant integer EMB_SEGMENT_LENGTH = 8 //defines the number of segments
        private constant string EMB_SEGMENT_MODEL = EMB_MODEL
        private constant real MIN_SCALE = 0.25
        private constant integer EMB_SEGMENT_RED = 255
        private constant integer EMB_SEGMENT_GREEN = 255
        private constant integer EMB_SEGMENT_BLUE = 255
        private constant integer EMB_SEGMENT_ALPHA = 240
        private constant real EMB_SEGMENT_SEPERATION_DIST = 16 // defines the distance between each segments
        /*
        *
        *   SPECIAL EFFECTS 
        *
        */
        private constant string COS_ON_CAST_SFX = "" //Effect when the spell is casted
        private constant string COS_CONTRACT_SFX = "" //Effect model of the contract
        private constant string COS_RELEASE_SFX = "" //Effect when the spell ends or when the embodiment is summoned 
        
        private constant string EMB_ON_SUMMON_SFX = "" //Effect when the embodiment is summoned
        private constant boolean ON_SUMMON_SFX_ON_SEGMENTS = true //Create the same effect on segments
        
        private constant string ACQUIRED_TARGET_SFX = "" //Effect made when acquiring a target
        private constant string ATTACK_RELEASE_SFX = "" //Effect when the attack is released
        private constant string ATTACK_DAMAGE_SFX = "" //Effect on the unit when damaged by attack .
        
        private constant string PASS_THROUGH_SFX = "" //Effect when passing on units.
        
        private constant string VEIL_SFX = "" //Model of veil
        private constant string VEIL_FEED_SFX = "" //Effect when releasing the received damage
        private constant string VEIL_HEAL_SFX = "" //Effect when the embodiment receives the feed
        
        private constant string ROAR_CHANNEL_SFX = "" //Effect when the embodiment starts channeling
        private constant string ROAR_RELEASE_SFX = "" //Effect when the channel succeeds
        private constant string ROAR_EXPLODE_SFX = "" //Effect of the explosions
        private constant real ROAR_SEPERATION = 250 //Explosion seperation
        
        private constant string EMB_DEATH_SFX = "" //Effect when embodiment dies
        private constant boolean ON_DEATH_SFX_ON_SEGMENTS = true //Create the same effect on segments
    endglobals
    
    private function ShadowAssaultValidTarget takes unit saTarget, player embOwner returns boolean
        /*
        *   Just put "//" before each statement to cancel those conditions
        */
        return     UnitAlive    (saTarget)                     and /*
            */     IsUnitEnemy  (saTarget, embOwner)           and /*
            */     IsUnitVisible(saTarget, embOwner)           and /*
            */ not IsUnitType   (saTarget, UNIT_TYPE_SLEEPING) and /*
            */ not IsUnitType   (saTarget, UNIT_TYPE_ETHEREAL) and /*
            */ not IsUnitType   (saTarget, UNIT_TYPE_STRUCTURE)
    endfunction
    
    private function RoarOfPainValidTarget takes unit ropTarget, player embOwner returns boolean
        /*
        *   Just put "//" before each statement to cancel those conditions
        */
        return     UnitAlive    (ropTarget)                         and /*
            */     IsUnitEnemy  (ropTarget, embOwner)               and /*
            */     IsUnitVisible(ropTarget, embOwner)               and /*
            */ not IsUnitType   (ropTarget, UNIT_TYPE_MAGIC_IMMUNE) and /*
            */ not IsUnitType   (ropTarget, UNIT_TYPE_STRUCTURE)
    endfunction
    
    private function ReceivedDamageCheck takes integer ddType, unit dSource returns boolean
        return IsUnitType(dSource, UNIT_TYPE_HERO)      == ALLOW_HERO_DAMAGE or /*
            */ IsUnitType(dSource, UNIT_TYPE_STRUCTURE) == ALLOW_STRUCTURE_DAMAGE or /*
            */ ALLOW_UNIT_DAMAGE
    endfunction
    
    private function GetLevelValue takes real base, real increment, integer level returns real
        return base + increment*level
    endfunction
    
    private function GetLevelIntValue takes integer base, integer increment, integer level returns integer
        return base + increment*level
    endfunction
    /**************************************
    *
    *   For getting the location surface z
    *
    **************************************/
    static if (not LIBRARY_ZLibrary) then
        globals
            private constant location p = Location(0.0, 0.0)
        endglobals
    
        function GetSurfaceZ takes real x, real y returns real
            call MoveLocation(p, x, y)
            return GetLocationZ(p)
        endfunction
    endif 
    
    /**************************************
    *
    *   List methods (Modules don't work
    *   as intended)
    *
    **************************************/
    //! textmacro COS_LIST
        private static thistype array prev
        private static thistype array next
        /*
        *   Insert node method
        */
        private method insert takes nothing returns nothing
            set next[this] = 0
            set prev[this] = prev[0]
            set next[prev[0]] = this
            set prev[0] = this
        endmethod
        /*
        *   Remove node method
        */
        private method remove takes nothing returns nothing
            set next[prev[this]] = next[this]
            set prev[next[this]] = prev[this]
        endmethod
        /*
        *   Check if list is empty
        */
        static method operator empty takes nothing returns boolean
            return next[0] == 0
        endmethod
        /*
        *   Check if list has only a single instance
        */
        method operator single takes nothing returns boolean
            return prev[this] == this
        endmethod
    //! endtextmacro
    /**************************************
    *
    *   Struct for segments to automatically
    *   adjust their facing.
    *
    **************************************/
    private struct SegmentFace
        /*
        *   u = the main unit
        *   nextU = the unit it's facing angle is based
        *   prevU = for destroying purposes
        */
        readonly unit u
        readonly unit nextU
        readonly unit prevU
        /*
        *   holds the object instance of the units
        */
        private static Table segList
        /*
        *   implement the list
        */
        //! runtextmacro COS_LIST()
        
        private static constant timer t = CreateTimer()
        
        method destroy takes nothing returns nothing
            /*
            *   remove all data
            */
            set segList[GetHandleId(u)] = 0
            set u = null
            set nextU = null
            set prevU = null
            /*
            *   remove node
            */
            call deallocate()
            call remove()
            /*
            *   if list is empty, pause timer
            */
            if empty then
                call PauseTimer(t)
            endif
        endmethod
        
        static method operator [] takes unit iu returns thistype
            return segList[GetHandleId(iu)]
        endmethod
        
        static method destroySegment takes unit du returns nothing
            /*
            *   this method is used for chain destruction 
            *   e.g. unit A is destroyed, destroy prev unit B
            */
            call thistype[du].destroy()
        endmethod
        
        private static method period takes nothing returns nothing
            local thistype this = next[0]
            local real ang = 0
            /*
            *   Iterate over instances
            */
            loop
                exitwhen 0 == this
                /*
                *   get the next unit's facing
                */
                set ang = GetUnitFacing(nextU)
                /*
                *   Set the unit's facing towards the facing
                *   of the next unit.
                */
                call SetUnitFacing(u, ang)
                /*
                *   Negate the given angle by rotating it
                *   180 degrees
                */
                set ang = ang*bj_DEGTORAD + bj_PI
                /*
                *   Move the unit to it's seperation distance
                */
                call SetUnitX(u, GetUnitX(nextU) + EMB_SEGMENT_SEPERATION_DIST*Cos(ang))
                call SetUnitY(u, GetUnitY(nextU) + EMB_SEGMENT_SEPERATION_DIST*Sin(ang))
                set this = next[this]
            endloop
        endmethod
        
        static method create takes unit tempU, unit prevLink, unit nextLink returns thistype
            local thistype this
            /*
            *   If the next unit is null, don't create an instance.
            */
            if nextLink == null then
                return thistype(0)
            endif
            /*
            *   allocate an isntance
            */
            set this = allocate()
            /*
            *   insert instance to the list
            */
            call insert()
            /*
            *   set the fields to the respective data
            */
            set u = tempU
            set nextU = nextLink
            set prevU = prevLink
            /*
            *   save the instace to the table
            */
            set segList[GetHandleId(u)] = this
            /*
            *   if the list has only one instance, start the timer.
            */
            if single then
                call TimerStart(t, TIMEOUT, true, function thistype.period)
            endif
            return this
        endmethod
        
        private static method onInit takes nothing returns nothing
            set segList = Table.create()
        endmethod
    endstruct
    
    private struct RoarOfPain
        private real totalDistance
        private real currentDistance
        private real effectDistance
        
        private real x
        private real y
        
        private real vectorX
        private real vectorY
        
        private real speed
        
        private unit source
        private real damage
        private real radius
        private real stunTime
        private player owner
        
        private group g = CreateGroup()
        
        //! runtextmacro COS_LIST()
        
        private static constant timer t = CreateTimer()
        
        method destroy takes nothing returns nothing
            /*
            *   Clean data
            */
            set currentDistance = 0
            set totalDistance = 0
            set x = 0
            set y = 0
            set vectorX = 0
            set vectorY = 0
            set speed = 0
            set radius = 0
            set damage = 0
            set stunTime = 0
            set source = null
            set owner = null
            /*
            *   remove node
            */
            call deallocate()
            call remove()
            /*
            *   if list is empty, pause timer
            */
            if empty then
                call PauseTimer(t)
            endif
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this = next[0]
            local unit u 
            loop
                exitwhen 0 == this
                /*
                *   Check if roar has not yet reached the destination
                */
                if currentDistance < totalDistance then
                    /*
                    *   Increment current distance with calculated speed
                    */
                    set currentDistance = currentDistance + speed
                    /*
                    *   Move coordinates
                    */
                    set x = x + vectorX
                    set y = y + vectorY
                    /*
                    *   Iterate on units
                    */
                    call GroupEnumUnitsInRange(g, x, y, radius, null)
                    loop
                        set u = FirstOfGroup(g)
                        exitwhen u == null
                        /*
                        *   Check if picked unit is valid
                        */
                        if RoarOfPainValidTarget(u, owner) then
                            /*
                            *   if true, damage unit
                            */
                            call UnitDamageTarget(source, u, damage, true, false, ROAR_DAMAGE_ATKTYPE, ROAR_DAMAGE_DMGTYPE, null)
                            /*
                            *   Stun the unit
                            */
                            call Stun.apply(u, stunTime, false)
                        endif
                        call GroupRemoveUnit(g, u)
                    endloop
                    /*
                    *   Check if effect distance has reached the effect seperation
                    */
                    if effectDistance < ROAR_SEPERATION then
                        set effectDistance = effectDistance + speed
                    else
                        set effectDistance = 0
                        if ROAR_EXPLODE_SFX != "" then
                            call DestroyEffect(AddSpecialEffect(ROAR_EXPLODE_SFX, x, y))
                        endif
                    endif
                else 
                    call destroy()
                endif
                set this = next[this]
            endloop
        endmethod
        
        static method create takes unit Source, real targetAngle, integer level returns thistype
            local thistype this
            /*
            *   Check first whether the Source is valid or the level is greater than 0
            */
            if Source == null or level == 0 then
                return thistype(0)
            endif
            /*
            *   Allocate new instance
            */
            set this = allocate()
            /*
            *   set source
            */
            set source = Source
            /*
            *   Get position of source
            */
            set x = GetUnitX(Source)
            set y = GetUnitY(Source)
            /*
            *   Get source owner
            */
            set owner = GetOwningPlayer(Source)
            /*
            *   Calculate speed
            */
            set speed = GetLevelValue(ROAR_SPEED, ROAR_SPEED_PER_LVL, level)*TIMEOUT
            /*
            *   Calculate vectors
            */
            set vectorX = speed*Cos(targetAngle)
            set vectorY = speed*Sin(targetAngle)
            /*
            *   Setup of distance data
            */
            set currentDistance = 0
            set effectDistance = 0
            set totalDistance = GetLevelValue(ROAR_TRAVEL_RANGE, ROAR_TRAVEL_RANGE_PER_LVL, level)
            /*
            *   Calculate effect radius
            */
            set radius = GetLevelValue(ROAR_RADIUS, ROAR_RADIUS_PER_LVL, level)
            /*
            *   Calculate stun duration
            */
            set stunTime = GetLevelValue(ROAR_STUN_DURATION, ROAR_STUN_DURATION_PER_LVL, level)
            /*
            *   insert node
            */
            call insert()
            /*
            *   if the list has only one instance, start timer 
            */
            if single then
                call TimerStart(t, TIMEOUT, true, function thistype.periodic)
            endif
            return this
        endmethod
    endstruct
    
    private function LinearR takes real a, real b, real t returns real
        return a + (b - a)*t
    endfunction
    
    private function LinearInt takes integer a, integer b, real t returns integer
        return R2I(LinearR(I2R(a), I2R(b), t))
    endfunction
    
    private struct Embodiment
        /*
        *   Embodiment data
        */
        
        private unit u                   // the embodiment
        
        private unit owner               // the owner
        
        private player pOwner            // the owning player 
        
        private effect mdl               // the model of the unit
        
        private unit prevU               // the link to the first segment 
        
        private Table segmentMDLs        // the segment's mdls
        private integer segmentCount
        
        private real speed               // the movespeed of the embodiment
        private real tx
        private real ty                  // the target points (movement, wander)
        
        private boolean returning        // states
        private boolean isIdle
        
        private integer level            // level of the ability
        
        private group targets            // for picking random targets
        
        private real timedLife           // amount of time
        private real incLife             // amount of time to increment
        private boolean hasVeilOfOrdeal 
        /*
        *   Shadow Assault variables
        */
        private boolean hasShadowAssault // indicates if the embodiment has "learned" the ability
        
        private real acqRange           // the acquisition range 
        
        private boolean attacking        // state of embodiment
        
        private real atkTime             // amount of attack cooldown
        private timer atkTimer           // the timer for the attack
        
        private real atkDamage           // amount of attack damage
        
        private real passDamage          // amount of damage dealt by passing through
        private real passRadius          // radius of pass-through
        
        private group passTargets        // for picking pass-through targets
        /*
        *   Roar of Pain
        */
        private boolean hasRoarOfPain    // indicates if the embodiment has "learned" the ability
        
        private real roarCastTime        // amount of time to cast
        private timer roarCastTimer      // timer for the cast
        
        private real roarCDTime          // amount of time for the roar's cooldown
        private timer roarCDTimer        // timer for the cooldown
        
        private group roarCheck          // check for valid casting
        
        private effect castSFX           // channeling sfx
        
        //! runtextmacro COS_LIST()
        
        private static constant timer t = CreateTimer()
        method destroy takes nothing returns nothing
            local unit temp
            local unit temp2
            /*
            *   Destroy all models
            */
            call DestroyEffect(mdl)
            set mdl = null
            loop
                exitwhen segmentCount == 0
                call DestroyEffect(segmentMDLs.effect[segmentCount])
                set segmentCount = segmentCount - 1
            endloop
            /*
            *   Destroy the segments
            */
            call RecycleMissile(u)
            set temp = prevU
            loop
                exitwhen temp == null
                set temp2 = SegmentFace[temp].prevU
                call SegmentFace.destroySegment(temp)
                call RecycleMissile(temp)
                set temp = temp2
            endloop
            set u = null
            set prevU = null
            /*
            *   Clear data
            */
            set owner = null
            set pOwner = null
            set speed = 0
            set tx = 0
            set ty = 0
            set returning = false
            set isIdle = false
            set timedLife = 0
            set incLife = 0
            set acqRange = 0
            set atkTime = 0
            set atkDamage = 0
            set passDamage = 0
            set passRadius = 0
            set roarCastTime = 0
            set roarCDTime = 0
            /*
            *   remove node
            */
            call deallocate()
            call remove()
            /*
            *   if list is empty, pause timer
            */
            if empty then
                call PauseTimer(t)
            endif
        endmethod
    endstruct
endlibrary

That's not even the main spell ;V
 
Status
Not open for further replies.
Top