• 🏆 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!

Creation of Hero Spells

Status
Not open for further replies.
Level 14
Joined
Nov 18, 2007
Messages
1,084
I made spells for Jaret and Xerex, though that doesn't mean I finished all of their spells. :p

Once I move on with their spells, I'll probably look for other spells that I think I can code.

Edit: I'll post the spells after the preloading issue is resolved.

Edit 2:
It's been several days without any mention of the preloading issue. Might as well as post what I had.
JASS:
//==========================================================================================
//Soul Harvest (Xerex) v1.02 by watermelon_1234
//__________________________________________________________________________________________
//Description:
//  Corpses within 500 range of Dark Lord will bolster his offensive power.
//******************************************************************************************
//Libraries required:
//  - BonusMod
//  - GroupUtils
//  - TimedLoop
//##########################################################################################
//Importing:
//  1. Copy the ability, Soul Harvest, and its buff.
//  2. Copy this trigger.
//  3. Implement required libraries.
//  4. Configure the spell.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Notes:
//  The level check may be inefficient as it always update to that manually. The nice thing 
//  is that this spell supports Tome of Retraining.
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
//Changelog:
//  v1.02: Made it reduce the time it would check, adds a special effect if the hero has a
//          bonus.
//  v1.01: Made it use TimedLoop
//==========================================================================================
scope SoulHarvest

    native UnitAlive takes unit id returns boolean  //A better way to check if a unit is alive.
    
    globals
        private constant integer    SPELL_ID    = 'A005'    //Raw id of the Soul Harvest ability.   
        private constant real       TIMER_LOOP  = 0.5       //How often the timer would check the corpses  
        private constant string     BONUS_SFX           = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"  //The SFX that would be attached to the hero if it has a damage bonus
        private constant string     BONUS_SFX_ATTACH    = "hand,left"                                               //The attachment point for the BONUS_SFX
    endglobals
    
    //The range of the ability which enumerates corpses.
    private constant function Range takes integer lvl returns real
        return 500. + 0*lvl
    endfunction
    
    //The damage bonus gained for each corpse.
    private constant function DamageBonus takes integer lvl returns integer
        return 2 + 2*lvl
    endfunction
    
    //The max damage bonus that one can get from this ability.
    private constant function MaxDamageBonus takes integer lvl returns integer
        return 70 + 0*lvl
    endfunction    
    
//-------------END OF CONFIGURATION-------------------

    globals
        private boolexpr e //Used for counting corpses
    endglobals
    
    private struct Data
        unit    hero            //The hero that learned the ability.
        integer lvl = 1         //The level of the ability for the hero. Is updated at the end of every loop as it stores the past level.
        integer count           //Counts how many corpses there are near the hero.
        integer oldCount = 0    //Stores the count from the previous time the loop was run. Used to update the attack damage bonus correctly.
        integer totalBonus = 0  //The total attack damage bonus the hero has from this ability.
        real    timeCounter = 0 //Counts how often the struct should be looped.
        effect  sfx = null      //The effect for the bonus.
        private static thistype temp    //Used for the group filter so that we can set count of a struct instance correctly. 
        
        static method create takes unit h returns thistype
            local thistype this = thistype.allocate()
            set .hero = h
            call .startTimedLoop()
            return this
        endmethod
        
        //Note that this is a filter. Used only to count dead units for the bonus.
        static method countCorpses takes nothing returns boolean
            if not UnitAlive(GetFilterUnit()) then //Corpses count as units that are dead.
                set temp.count = temp.count + 1
            endif
            return false
        endmethod
        
        //Used so many times that I just made a method for this. Basically removes any bonus the hero gained from this spell.
        method resetBonus takes nothing returns nothing
            call AddUnitBonus(.hero,BONUS_DAMAGE,-.totalBonus) 
        endmethod
        
        method removeEffect takes nothing returns nothing
            call DestroyEffect(.sfx)
            set .sfx = null
        endmethod
        
        method onTimedLoop takes nothing returns boolean
            local integer add   //Considers what attack damage bonus the spell needs to add 
            local integer curLvl = GetUnitAbilityLevel(.hero,SPELL_ID)
            //If the level is 0 that means the hero no longer has the ability. That means the spell should stop.
            if curLvl == 0 then
                //Only reset the bonus if .totalBonus is greater than 0.
                if .totalBonus > 0 then
                    call .resetBonus()
                    call .removeEffect()
                endif
                return false
            endif
            if .timeCounter == 0 then
                if UnitAlive(.hero) then  
                    set .count = 0 //Set to 0 first or else counting the corpses will be messed up.
                    set temp = this
                    call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(.hero),GetUnitY(.hero),Range(.lvl),e)
                    //No corpses but oldCount has a different number. Remove any bonus given to the hero and correctly set oldCount to 0.
                    if .count == 0 and .oldCount != 0 then
                        call .resetBonus()
                        call .removeEffect()
                        set .totalBonus = 0
                        set .oldCount = 0
                    //Previous level is not updated with the current level. 
                    //In this case, ignore any bonus the hero has received from last loop by resetting it and manually give the correct bonus. 
                    elseif .lvl != curLvl then
                        call .resetBonus()       
                        //Make sure the bonus will not exceed MaxDamageBonus. 
                        //Remember: The bonus given to the hero is the # of corpses times the damage bonus each corpse gives but the bonus is capped.
                        if .count*DamageBonus(curLvl) > MaxDamageBonus(curLvl) then
                            set .totalBonus = MaxDamageBonus(curLvl)
                        else
                            set .totalBonus = .count*DamageBonus(curLvl)
                        endif
                        call AddUnitBonus(.hero,BONUS_DAMAGE,.totalBonus)
                        set .lvl = curLvl
                    elseif .count != .oldCount then //The counts of the corpses aren't the same so change the bonus. Otherwise, don't do anything after this as all is well.             
                        //If the supposed bonus is bigger than MaxDamageBonus, limit it.
                        //add is the difference between the bonus the hero should have from the spell and the current bonus it has.
                        if .count*DamageBonus(.lvl) > MaxDamageBonus(.lvl) then 
                            set add = MaxDamageBonus(.lvl) - .totalBonus
                        else
                            set add = .count*DamageBonus(.lvl) - .totalBonus
                        endif
                        call AddUnitBonus(.hero,BONUS_DAMAGE,add)
                        //Update some variables to work for the next loop
                        set .oldCount = .count 
                        set .totalBonus = .totalBonus + add
                    endif        
                    if .totalBonus > 0 and .sfx == null then
                        set .sfx = AddSpecialEffectTarget(BONUS_SFX,.hero,BONUS_SFX_ATTACH)
                    endif
                endif
            endif
            set .timeCounter = .timeCounter + TimedLoop_PERIOD
            if .timeCounter >= TIMER_LOOP then
                set .timeCounter = 0
            endif
            return true
        endmethod
        
        implement TimedLoop
        
        static method learnSkill takes nothing returns boolean
            if GetLearnedSkill() == SPELL_ID and GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID) == 1 then
                call thistype.create(GetTriggerUnit())
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_HERO_SKILL)
            call TriggerAddCondition(t,Condition(function thistype.learnSkill))
            set e = Filter(function thistype.countCorpses)
        endmethod
    endstruct    
endscope
Posted code only for Soul Harvest since the other spells' code was barely touched.
 

Attachments

  • Pack2.w3x
    133.2 KB · Views: 145
Last edited:
Level 25
Joined
Jun 5, 2008
Messages
2,572
I came to a standstill while making my spells.

The issue is i don't know how to detect what kind of damage the unit is taking with DamageEvent.

Thus my 3 skills might work even for magic damage, or items that deal damage instead of just physical attacks.

Some insight about this would be nice...
 
Level 13
Joined
May 11, 2008
Messages
1,198
hmm...i admit i haven't used that system yet, i'm not sure if it allows for that or not. at any rate, it would help to know more about the map in general. in my map, i can detect what type of attack/damage it should be by checking various things about the source of the damage(the unit dealing the damage). you or i could write something like that if the system doesn't support it.

if i'm not mistaken, you're trying to figure out for example how to make your hero deal amplified damage. but you want to perhaps prevent magical attacks to be amplified.

if we allow dummy units to deal damage, then we can check if source is hero and if not, then we can prevent the amplification, but if we only let the hero itself deal the damage, then we cannot use that method.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I want to create the spells for Dentothor, is that OK?

I use GUI if that is alright.
I'm pretty sure only vJass is accepted, sorry. However, things may change if there's a serious lack of coders which appears to be happening right now...


Anyway, I attached the map with the second set of spells I made to my last post in case no one saw that yet.
 
Level 13
Joined
May 11, 2008
Messages
1,198
i noticed that in dota the corpses disappear too fast to do anything with them. just mentioning it so that if we're using spells like soul harvest we need to make sure that corpses last longer than they do in dota. i don't know what the value will be, but perhaps we should assume it will be similiar to the default value. also, i'm curious...what is up with that line...
native UnitAlive takes unit id returns boolean //A better way to check if a unit is alive.
is that from a system that the spell is using? if so, which one?
i haven't seen that sort of thing before.
 
Status
Not open for further replies.
Top