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

Esdo's "Demonic Pentagram" spell v1.1a

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
JASS:
scope DemonicPentagram initializer DemonicPentagramInit
    
    globals
        //  ___                     _      ___         _                            
        // |   \ ___ _ __  ___ _ _ (_)__  | _ \___ _ _| |_ __ _ __ _ _ _ __ _ _ __  
        // | |) / -_) '  \/ _ \ ' \| / _| |  _/ -_) ' \  _/ _` / _` | '_/ _` | '  \ 
        // |___/\___|_|_|_\___/_||_|_\__| |_| \___|_||_\__\__,_\__, |_| \__,_|_|_|_|
        //                                                     |___/
        //                                 Made by "esdo" ( HiveWorkshop.com ) v 1.1a
        //
        // AUTHOR'S NOTE:
        // My first vJass spell uploaded on hive... Hope you guys enjoy this efficient,
        // leakless spell... Please give me credit if you ever use this!!!
        // AUTHOR'S NOTE END
        //
        // DESCRIPTION:
        // Curses an area, slowing and disabling all enemies inside, preventing spellcasting
        // and any kind of attack. A pentagram is slowly drawn inside the area and, once its
        // complete, the whole area will violently explode, dealing damage based on the caster's
        // selected stat.
        // DESCRIPTION END
        //
        // CREDITS:
        // - Me, "esdo", for creating this spell.
        // - "Deathismyfriend" and many other members of the Hive, that helped me learn Jass
        // and fix my code.
        // CREDITS END
        //
        // CONFIGURATION:
        // "Size" defines the radius of the cursed area. Remember to change the radius effect
        // to match this one in the "Pentagram [Effect 1]" spell!
        private constant real SIZE = 300
        //
        // "ATTRIBUTE_USED" defines the used attribute for damage. You should edit only the latter
        // part of the var, that is, the "_" and the last three letters. Use _STR for strenght,
        // _AGI for agility and _INT for intelligence.
        private constant integer ATTRIBUTE_USED = bj_HEROSTAT_STR
         //
    endglobals
        //
        // This function allows the user to configure it's own desired spell damage. It takes
        // the hero's attribute and skill level as parameters.
        
    private function GetDamageDealt takes real attribute, real level returns real
        return attribute * level // <== This will be the damage done.
    endfunction
    
    globals
        //
        // "ATTACK_TYPE" defines the type of the attack dealt when the pentagram explodes.
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS
        //
        // "DAMAGE_TYPE" is the type of damage dealt by the pentagram explosion.
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
        //
        // "EFFECT_PATH" is the \\path of the special effect shown upon pentagram explosion.
        private constant string EFFECT_PATH = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl"
        //
        // "DUMMY_ID" is the integer ID of the "Dummy" unit.
        private constant integer DUMMY_ID = 'e000'
        //
        // "EFFECT_ID" is the inreger ID of the "Pentagram Dummy" unit.
        private constant integer EFFECT_ID = 'e001'
        //
        // "SPELL1_ID" is the integer ID of the "Demonic Pentagram" spell.
        private constant integer SPELL1_ID = 'A000'
        //
        // "SPELL2_ID" is the integer ID of the "Pentagram [Effect1]" spell.
        private constant integer SPELL2_ID = 'A001'
        //
        // CONFIGURATION END
    endglobals
    
    private struct Pentagram
        unit Caster
        unit Draw
        unit Dummy
        group Effects
        real Distance
        integer SidesDrawn
        real Facing
    endstruct
    
    globals
        private timer Periodic = CreateTimer()
        private integer Index = 0
        private real TriSideSize = ( 0.95 * SIZE ) * 2
        private Pentagram array Data
        private integer Expire
    endglobals
    
    private function DemonicPentagramDraw takes nothing returns nothing
        local effect s
        local unit u
        local integer i = 1
        local real ox
        local real oy
        local real nx
        local real ny
        local real a
        local player p
        local group g = CreateGroup()
        loop
            exitwhen i > Index
            set p = GetOwningPlayer( Data[i].Caster )
            set ox = GetUnitX( Data[i].Draw )
            set oy = GetUnitY( Data[i].Draw )
            set nx = ox + 10 * Cos( Data[i].Facing * bj_DEGTORAD)
            set ny = oy + 10 * Sin( Data[i].Facing * bj_DEGTORAD)
            call SetUnitPosition( Data[i].Draw, nx, ny )
            call GroupAddUnit( Data[i].Effects, CreateUnit( p, EFFECT_ID, nx, ny, 0 ))
            set Data[i].Distance = Data[i].Distance + 10
            if ( Data[i].Distance >= TriSideSize  ) then
                set Data[i].Distance = 0
                set Data[i].Facing = Data[i].Facing - 144
                set Data[i].SidesDrawn = Data[i].SidesDrawn + 1
                if ( Data[i].SidesDrawn == 5 ) then
                    set ox = GetUnitX(Data[i].Dummy)
                    set oy = GetUnitY(Data[i].Dummy)
                    call KillUnit( Data[i].Draw )
                    call KillUnit( Data[i].Dummy ) 
                    set Expire = i
                    call GroupEnumUnitsInRange( g, ox, oy, SIZE, null )
                    loop
                        set u = FirstOfGroup( g )
                        exitwhen u == null
                        if ( IsPlayerAlly( GetOwningPlayer( Data[i].Caster ), GetOwningPlayer(u)) == false ) then
                            call UnitDamageTarget( Data[i].Caster, u, GetDamageDealt( GetHeroStatBJ( ATTRIBUTE_USED, Data[i].Caster, true ),IncUnitAbilityLevel( Data[i].Caster, SPELL1_ID )) , true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS)
                        endif
                        call GroupRemoveUnit( g, u )
                    endloop
                    loop
                        set u = FirstOfGroup( Data[i].Effects )
                        exitwhen u == null
                        set ox = GetUnitX( u )
                        set oy = GetUnitY( u )
                        set s = AddSpecialEffect( EFFECT_PATH, ox, oy )
                        call DestroyEffect( s )
                        call KillUnit(u)
                        call GroupRemoveUnit( Data[i].Effects, u )
                    endloop
                    set Data[i] = Data[Index]
                    call Data[Index].destroy()
                    set Index = Index - 1
                    if ( Index == 0 ) then
                        call PauseTimer( Periodic )
                    endif
                    set i = i - 1
                endif
            endif
            set i = i + 1
        endloop
        call DestroyGroup(g)
        set s = null
        set u = null
        set g = null
    endfunction
    
    private function Trig_DemonicPentagram_Actions takes nothing returns nothing
        local integer i = 0
        local player p
        local real x = GetSpellTargetX()
        local real y = GetSpellTargetY()
        local real dx
        local real dy
        set Index = Index + 1
        set Data[Index] = Pentagram.create()
        set Data[Index].SidesDrawn = 0
        set Data[Index].Caster = GetTriggerUnit()
        set Data[Index].Facing = 108
        set p = GetOwningPlayer( Data[Index].Caster )
        set Data[Index].Dummy = CreateUnit( p, DUMMY_ID, x, y , 0 )
        call UnitAddAbility( Data[Index].Dummy, SPELL2_ID)
        call IssuePointOrder( Data[Index].Dummy, "cloudoffog", x, y )
        set Data[Index].Draw = CreateUnit( p, DUMMY_ID, x, (y-SIZE), 108 )
        set x = GetUnitX( Data[Index].Dummy )
        set y = GetUnitY( Data[Index].Dummy )
        set Data[Index].Distance = 0
        call DestroyGroup( Data[Index].Effects )
        set Data[Index].Effects = CreateGroup()
        loop
            exitwhen i == 360
            set dx = x + SIZE * Cos( i * bj_DEGTORAD)
            set dy = y + SIZE * Sin( i * bj_DEGTORAD)
            call GroupAddUnit( Data[Index].Effects, CreateUnit( p, EFFECT_ID, dx, dy, 0 ))
            set i = i + 2
        endloop
        if ( Index == 1 ) then
            call TimerStart( Periodic, 0.03, true, function DemonicPentagramDraw )
        endif
        set p = null
    endfunction

    private function Trig_DemonicPentagram_Conditions takes nothing returns boolean
        if ( GetSpellAbilityId() == SPELL1_ID ) then
            call Trig_DemonicPentagram_Actions()
        endif
        return false
    endfunction

//===========================================================================

    private function DemonicPentagramInit takes nothing returns nothing
        set gg_trg_DemonicPentagram = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( gg_trg_DemonicPentagram, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( gg_trg_DemonicPentagram, Condition( function Trig_DemonicPentagram_Conditions ) )
    endfunction

endscope

Keywords:
demon, pentagram, demonic, curse, area, aoe, disable, hell, hellish
Contents

Just another Warcraft III map (Map)

Reviews
IcemanBo: Too long as NeedsFix. Rejected. 22:35, 5th May 2014 BPower: http://www.hiveworkshop.com/forums/spells-569/esdos-demonic-pentagram-spell-v1-1a-250990/#post2522746

Moderator

M

Moderator

IcemanBo: Too long as NeedsFix. Rejected.

22:35, 5th May 2014
BPower:
http://www.hiveworkshop.com/forums/spells-569/esdos-demonic-pentagram-spell-v1-1a-250990/#post2522746
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
  • those globals inside configuration must be constant like private constant integer SOME_VARIABLE and check carefully how I write the variable name. it's a standart vJass writing convention
  • use FoG looping instead of ForGroup
    JASS:
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        // do actions here
        call GroupRemoveUnit(g, u)
    endloop
  • not a big deal, but you may start the private integer Index = 0 from -1
  • JASS:
            local location l = GetSpellTargetLoc()
    
            set x = GetLocationX( l )
            set y = GetLocationY( l )
    please, dont use location on Jass.. use this native: GetSpellTargetX() and GetSpellTargetY()
  • call TimerStart( Periodic, 0.03, true, function DemonicPentagramDraw )
    that periodic timer (0.03) can be included in the configuration
    private constant real PERIODIC = 0.03
  • JASS:
                call DisplayTextToPlayer( GetLocalPlayer(),0,0, "TimerFired")
            endif
            call DisplayTextToPlayer( GetLocalPlayer(),0,0, "Aesd bacchannalia")
    I think you forgot to remove that debug message?
  • you should merge Trig_DemonicPentagram_Actions into Trig_DemonicPentagram_Conditions
    and that's just a bad function name
  • set a = GetUnitFacing( Data[i].Draw )
    instead of getting the face on each loop isn't that better to store it into a variable array like (yup, I'm speed freaky):
    JASS:
                    call KillUnit( Data[i].Draw )
                    set Data[i].Draw = CreateUnit( p, DummyID , nx, ny, Facing[i] )
                    set Facing[i] = Facing[i] - 144
  • local effect s = AddSpecialEffect( "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", x, y )
    that effect path should be configurable
  • if ( IsPlayerAlly( GetOwningPlayer( Data[Expire].Caster ), GetOwningPlayer(u)) == false ) then
    you could add a configurable filtering function
  • private real TriSideSize = ( 0.95 * Size ) * 2
    it should be a constant
    private constant real TRI_SIDE_SIZE = ( 0.95 * Size ) * 2
  • JASS:
            //
            // "AttributeUsed" defines the used attribute for damage. You should edit only the latter
            // part of the var, that is, the "_" and the last three letters. Use _STR for strenght,
            // _AGI for agility and _INT for intelligence.
            private integer AttributeUsed = bj_HEROSTAT_STR
            //
            // "Damage" defines the damage dealt by the spell. This value is multiplied
            // by the casters "AttributeUsed".
            private real Damage = 4
            //
            // "DmgLvl" multiplies total damage done by the level of the spell fo the caster.
            // The final damage equasion is:
            // Hero Attribute * (( Damage var ) + ( Level of ability * DmgLvl var ))
            private real DmgLvl = 1
    it can be improved:
    JASS:
    // that private real DmgLvl is not neccesary
    // remove global Damage..
    // use this function so that user could calculate the damage by themselves
    
    private function getDamage takes integer level, integer att returns real
        // see that the function takes integer att which it's caster's used attribute
        // and imo it's better to improve the damage as we got higher ability level, isn't it?
        return 4 * att * level
    endfunction
    then you may call it on damage event:
    JASS:
    call UnitDamageTarget( Data[Expire].Caster, u, getDamage(abilityLevel, USED_ATTRIBUTE), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
    and weapon/damage/attack type should be configurable though..
  • local effect s = AddSpecialEffect( "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl", x, y )
    afaik you should null it

I'm sorry for unreadable review.. I'm in hurry

it's edited. I'm sorry if there is maybe some mistakes because I typed them manually I'm not on my PC atm
and hey, you got a good start here :grin: keep going
 
Last edited:
Level 29
Joined
Oct 24, 2012
Messages
6,543
You should inline this. GetHeroStatBJ

Since you are clearing the group every time it is used make it a global group. Create it once. You should also use FirstOfGroup loop rather than ForGroup loop. FirstOfGroup is faster and more efficient than forgroup. It will also allow you to get rid of a function call.

Don't use locations.
GetSpellTargetLoc()
Use these instead.
GetSpellTargetX() and this GetSpellTargetY()
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
JASS:
        //
        // "SPELL_DAMAGE" defines the damage dealt by the spell. This value is multiplied
        // by the casters "ATTRIBUTE_USED".
        private constant real SPELL_DAMAGE = 5
        //
        // "DAMAGE_LEVEL" multiplies total damage done by the level of the spell fo the caster.
        // The final damage equasion is:
        // ( Hero Attribute * SPELL_DAMAGE var ) * ( Level of ability * DAMAGE_LEVEL var )
        private constant real DAMAGE_LEVEL = 2
you forgot to remove it

merge those two configurable global declarations. and write damage function right after global declaration..

JASS:
    private function GetDamageDealt takes real attribute, real level returns real
        return attribute * level // <== This will be the damage done.
    endfunction
level must be integer, idk about the attribut but as far as I remember it's an integer too..

some haven't fixed yet..
 
Level 5
Joined
Oct 2, 2013
Messages
95
Okay now it's fixed. I think.

The att/level can be reals. If I change them to Integer the function's (return real) wont work, so I'll just leave them like that.

Removed unnecessary vars and locations.

I think that merging the global blocks would make the config page look abit messy.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
- set p = GetOwningPlayer( Data[Index].Caster ) --> set p = GetTriggerPlayer()
p could be a member of the struct, label it with "owner"
- call DestroyGroup( Data[Index].Effects ) This group does not exist at that point.
- You could use SetUnitX/Y instead of SetUnitPosition, because it is superior in terms of speed.
- Instead of creating multiple local groups, you could use one global group for the FoG enumeration.
- The BJ could be replaced.
- in IsUnitAlly you don't have to compare the condition with the boolean false --> if IsUnitAlly(...) then is enough
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
- in IsUnitAlly you don't have to compare the condition with the boolean false --> if IsUnitAlly(...) then is enough
This.
if ( IsPlayerAlly( GetOwningPlayer( Data[i].Caster ), GetOwningPlayer(u)) == false ) then
Should be this for a false boolean.
if not IsPlayerAlly( GetOwningPlayer( Data[i].Caster ), GetOwningPlayer(u)) then
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
1. Remove all generated variables from tactical dragon fire.

2. gg_trg_DemonicPentagram = CreateTrigger() --> local trigger t = CreateTrigger()

3. As you are using vJass, your function names should follow the JPAG. This also means that function names like Trig_DemonicPentagram_Conditions should be for instance Check or AbilityCondition.

4. SPELL1 and SPELL2 are not really suitable variable names.

5. call DestroyGroup( Data[Index].Effects ) might not exist in the first place.

6. In the loop you create a lot of local groups, which are not used at all.

7. if ( IsPlayerAlly( GetOwningPlayer( Data.Caster ), GetOwningPlayer(u)) == false ) then should be configurable. Furthermore GetOwningPlayer( Data[i].Caster ) is already set to local player variable p.
You also don't have to do a boolean comparison == false, just if ( IsPlayerAlly( p, GetOwningPlayer(u))) then is enough.

8. Struct members should be nulled properly.

9. Remove the BJ.
 
Top