• Check out the results of the Techtree Contest #19!
  • 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.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

[Spells] Need help please

Status
Not open for further replies.

Div

Div

Level 4
Joined
Oct 4, 2005
Messages
85
Hi, I am making a hero but I do not have the ability to trigger his skills on WE so I need help (All I can give is +Rep and be really glad :)

I'm gonna hide all the extra information anyone willing enough to edit the skills is gonna need in spoilers, so it doesn't looks like a giant wall of text

So this are the skills:

Confusion

Causes a target enemy unit to get confused, attacking the nearest target to it (enemy or ally) in a frenzy, granting extra attack speed but taking part of the damage back to himself. If the target has no units in his area of sight to attack he will attack himself/herself¹. Lasts 1.75/2.5/3.25/4 seconds.

Level 1 - 10% extra attack speed, 15% damage return.
Level 2 - 20% extra attack speed, 20% damage return.
Level 3 - 30% extra attack speed, 25% damage return.
Level 4 - 40% extra attack speed, 30% damage return.

Mana Cost: 45/60/75/90
Cooldown: 20
Skill type: Active/Target Unit (700 cast range)

*Automaticaly applies Confusion to a Mind Controlled unit, making it deal self-damage whenever it deals damage to a unit (as of regret) but still retaining Ty's ability to fully control it.
*If casted twice (Body and Mind) the second cast will deal damage to the target (100/150/200/250).
*If Confused enemy misses any attack (no damage dealt on attack), it will take full damage itself from the last attack damage it has dealt.

1 - "Attack himself" means that the enemy will take damage based on the last hit it gave to a target unit. For that, if a target is not within range of the confused unit, a dummy would be created at his position forcing it to attack it, them that damage inflicted to the dummy would be the damage the confused unit would take through out the duration of Confusion.

Body and Mind

Gives a 15% chance to separate Ty's mind from his body whenever he casts a spell, manifesting it as a copy of himself. The mind is uncontrolable¹, can only be damaged by spells or magical attacks and will do everything the body is ordered to do (does not attack).

Level 1 - The mind can take up to 100 damage and will last 5 seconds or 1 cast².
Level 2 - The mind can take up to 200 damage and will last 10 seconds or 2 casts².
Level 3 - The mind can take up to 300 damage and will last 15 seconds or 3 casts².
Level 4 - The mind can take up to 400 damage and will last 20 seconds or 4 casts².

Mana cost: -
Cooldown: 40/35/30/25
Spell Type: Passive.

*It basically creates "another Ty" but more bluish/purplish and with some transparency to look like "it's astral-like"
*If Mind Control is used while Ty's mind is separated from his body the enemy units will have to stop both of them in order to interrupt the channel (stun both of them, kill both of them, etc)
*If Body and Mind effect ends while Ty is casting Mind Control, it won't interrupt the Ultimate's effect (unless both of the parts have stopped channeling it).
*Mind has 0 collision and will disappear when getting too far away from the Body (max 2000 range).
*If a damaging item is used (Dagon, Shivas, etc), the Mind will use it as well² at the targeted unit/point, but will only deal 40% of that item's damage (Body deals 800 with Dagon level 5, Mind will deal 320 = 1120 damage total).

1 - Has a sub-skill which allows you to target where you want the Mind manifestation to go to (as a moving command) but has a standard passive follow whenever Ty's body moves.
2 - Item using counts as a normal cast.

Curse of the Blind

Steals a target unit's knowledge, making it lose vision sight while Ty gains the same amount of extra vision sight. Affected units have the ability to resist Mind Control lowered by 2% and 5% chance of missing attacks for every 100 area of sight lost. If the unit reaches 0 vision sight it'll miss all attacks and have 0% chance of resisting Mind Control. Stacks up to 5 times and lasts 12 seconds each.

Level 1 - Steals 5%¹ total² area of sight of unit and adds 50% of it to Ty's vision.
Level 2 - Steals 10%¹ total² area of sight of unit and adds 50% of it to Ty's vision.
Level 3 - Steals 15%¹ total² area of sight of unit and adds 50% of it to Ty's vision.
Level 4 - Steals 20%¹ total² area of sight of unit and adds 50% of it to Ty's vision.

Mana cost: 25/45/65/85
Cooldown: 3.5 sec.
Spell Type: Active/Target Unit (900 cast range)

*A totaly blind unit will have vision of only itself (example: Techies' mines)
*the unit will still be able to see through other units "eyes" (staying near creeps, other heroes), but will still miss attacks.
*If Mind Controlled, the Cursed unit will have all it's vision back to normal (debuff will be dispelled).

1 - I made it as a percentage because area of sight varies too much from hero to hero (the earlier concept was that the targeted unit would lose 250/300/350/400 AoS and half of theses values at night), so I'm hoping you guys can help deciding on which one is better.
2 - Total, not current (meaning if the enemy as 1800 original area of sight, it will always steal from that value).

Mind Control

Gains control over a targeted weaker mind, allowing Ty to manipulate it in anyway he wants. The target has a 20% chance of resisting Mind Control and additional chance for every level it has if it is a Hero. Unable to target stronger units than Ty himself³.

Level 1 - 3% additional resist chance per hero level, lasts 10 seconds.
Level 2 - 2% additional resist chance per hero level, lasts 15 seconds.
Level 3 - 1% additional resist chance per hero level, lasts 20 seconds.

Mana cost: 200/300/400
Cooldown: 180²
Spell type: Active/Target Unit/Channeling (700 cast range)
Max unit distance range: 50%/60%/70% of Ty's vision sight² (if the controlled unit gets further away from Ty it breaks Mind Control)

*When under control, a hero may not drop/destroy/sell/buy/pick items, but may use the ones he/she already has
*The player in control of the target may use all of its abilities, including spells, move, attack, anything¹
*If the controlled unit dies/kills a hero, the credit for the kill goes to Ty (exp as well)
*The controlled unit has a 10/7/5% chance of regaining conscience on each attack it takes.

1 - Ty loses control over the unit if any ultimate is used (loses control after the ultimate's effect has been activated)
2 - Aghanim's Scepter Upgrade - 60%/70%/80% of Ty's total area of sight vision allowed as distance; 140 cooldown (suggestion).
3 - If Ty is level 6 and targets an enemy level 7, the enemy will have 100% chance of resisting Mind Control, BUT, if the enemy has Curse of the Blind fully stacked on it (ONLY when fully stacked) Ty will be able to control it with ease. If the enemy has a higher level than Ty, and it's affected by Curse of the Blind, it won't have its resisting chance reduced unless completly blind (Won't go from 100% chance of resisting to 96/48/12/etc, it is either 100% or 0%).




That's it, I know it looks very complicated... and maybe it is
But I really need it for something I'm doing

Please, anyone willing enough just let me know!
Thank you very much :)
 
Last edited:
Thanks, I tried [/Spoiler] but it didn't work :)
Anyone can help me with the spells??
 
I can do them. Most likely. Let me read them.

Okay, first question. What exactly did you have in mind when you said "the unit attacks itself".

For the second ability, I don't follow what you want to happen at all. Also, you don't explain what "Ty" is.

...before I continue, could you please just post the raw layout of each spell (rather than these weird tool-tip-like descriptions)? All of your descriptions simply give an abstract explanation of what is happening without giving any detail to what is happening behind the scenes.
 
I can do them. Most likely. Let me read them.

Okay, first question. What exactly did you have in mind when you said "the unit attacks itself".

For the second ability, I don't follow what you want to happen at all. Also, you don't explain what "Ty" is.

...before I continue, could you please just post the raw layout of each spell (rather than these weird tool-tip-like descriptions)? All of your descriptions simply give an abstract explanation of what is happening without giving any detail to what is happening behind the scenes.

Ok so:

First of all, Ty is the name of the hero I have in mind

SPELL ONE: is a basic Confusion spell, where you target an enemy unit/hero and that unit will attack the nearest target from it for the duration with increased attack speed BUT taking part of the damage dealt to itself.

Attacking itself means that if the units has no targets to attack in its vision sight (no one to run to and attack), it will "attack itself"
"it's impossible to make a unit attack itself", I know, but as I suggested, creating a dummy right next to the affected unit could allow that unit to attack the dummy, then you can take the damage taken from the dummy and apply to the confused unit (would be the same as "attacking itself")

Also, it is important to notice that when the ultimate (Mind Control) is applied to a unit, Confusion will be applied to that unit, meaning the controlled unit will be given x% extra attack speed (depending on Confusion's level) and also take extra damage (But Ty will be able to fully control it, not having it attacking random units)

The range of the "detection system" used to the detect any possible units the confused unit can attack should be the same as its vision sight

SPELL TWO: Do you know Tauren Chieftain from DotA? Do you know his spell Ancestral Spirit? This would work in a similar way, except this one is passive
How does it works?
Whenever Ty (the hero) casts a spell, he has x% chance of creating a "spirit" form of himself where he targeted the spell
This "spirit" is uncontrollable and will perform ANY action Ty does (if Ty casts Confusion to a target, the spirit will do that as well - remember that if confusion is casted twice on an enemy the second one will deal damage, as said before)
If Ty is ordered to move to a target point, the unit will face and head x units towards the same direction
Also, a sub skill that allows Ty to order the "Spirit" to move to a point would be added
Notice that Ty's spirit is not completly like him, the Spirit only has 100/200/300/400 hitpoints, no regen, not selectable, infinite mana (in order for it to be able to copy all Ty's casts) and may only be killed by spells

SPELL THREE:do you have any questions here? I guess it is pretty simple
The only thing here is: When a unit is mind controlled, Curse of the Blind debuff is removed from it

SPELL FOUR: The only "complicated part" from here is the "resistance" chance which can be increased by level or decreased by Curse of the Blind
Notice that if a unit is stronger than Ty (Level wise) it can only be dominated if it has Curse of the Blind fully stacked on (meaning 0% chance of resisting)




Thank you for giving attention to this, hope you can help me! :)
 
Alright, it seems you've cleared quite a bit up. I must say these are some really really complex spells, but it shouldn't be too much of a problem.

That's great, if you have any doubts just let me know :)

I know they are complicated, and the main goal here is having a hero which fully synergys with himself... so, can I count with you on the hero making?
 
Alright, that's awesome, I'm really excited
Any news you have let me know please

PS: 3 hour shift? That's the best job in the world haha
 
Alright things didn't go as expected, but I'm back now. I'll get to work. Do you mind if I use some public resources so I don't have to make new ones specifically for you? If necessary, of course.

Also, in your Confusion spell, can the affected unit attack the caster?

Here's a status update:
Implemented a generic Damage Detection system from scratch.

Confusion -
  • Requires implementation of the BonusMod library for attack speed modifiers.
  • Damage return - Done
  • Primary spell structuring - Done
  • Target of Confusion disabled - Done
  • Implemented a stack to easily reference existing instances of Confusion - Done
Body and Mind - Pending
Curse of the Blind - Pending
Mind Control - Pending

I could just use abilities instead of using BonusMod, but using that library will make it easier to change the values (much easier).
 
Last edited:
Then it is the best job in the world lol

No, I don't mind it at all :)

Yeap, he can, but if there is any other unit in it's vision sight that other unit is the priority for an attack
Works like this:

Closest Unit in sight>Any other unit besides Ty (the hero here) in sight>Ty (the hero here)>self attack (creates dummy at Confused unit's position, confused unit attacks the dummy, attack received is applied back to the confused unit)
 
As I have it setup right now the Confused unit will receive a percentage of the damage he deals back to himself. The damage percent that is used is declared in a fairly large table of arrays which I give you adequate reference to in the code (and it is as easy as changing the values).

I'll go ahead and implement BonusMod right now, and finalize the first spell. The way I have it setup makes it really easy for me to re-apply the effects of Confusion whenever necessary, and I also included (as I say above) a really easy method of determining whether a unit is under the influence of Confusion (in which case it will deal damage which is defined in the table I mentioned earlier).

Here's the code so far:
JASS:
scope ConfusionSpell initializer init

    //################################################################################################
    globals
    
        public      constant integer    ABIL_ID             = 'A001'
        public      constant integer    BUFF_ID             = 'B000'
        
        //the radius will act as a "maximum" search area because if there are units covered by the
        //fog of war the affected "confused" unit will not be issued to attack it.
        public      constant real       RADIUS              = 1600      
        
        public      real array          DURATION            
        public      real array          RETURN_PERCENT      
        public      integer array       ATTACK_SPEED
        public      real array          DAMAGE 
        
        //used to detect nearby targets for the affected unit to attack.
        private     group               core__enumGroup     = CreateGroup()
        private     confusionspell      core__temp         
        
        //used to return damage to confused units when they attack
        private     trigger             core__onDamage      = CreateTrigger()
        
        private     hashtable           core__table         = InitHashtable()
        
    endglobals
    
    //################################################################################################
    struct confusionspell
    
        public      unit        caster      = null
        public      unit        victim      = null
        private     unit        target      = null
        public      integer     level
        
        private     trigger     disable     = CreateTrigger()
        private     trigger     targetDeath = CreateTrigger()
        private     trigger     victimDeath = CreateTrigger()
        
        private     timer       duration    = CreateTimer()
        
        
        static method operator [] takes unit victim returns thistype
            return LoadInteger(core__table, GetHandleId(victim), 1)
        endmethod
        
        method onDestroy takes nothing returns nothing
            call RemoveSavedInteger(core__table, GetHandleId(duration), 1)
            call RemoveSavedInteger(core__table, GetHandleId(targetDeath), 1)
            call RemoveSavedInteger(core__table, GetHandleId(victim), 1)
            call RemoveSavedInteger(core__table, GetHandleId(victimDeath), 1)
            
            call PauseTimer(duration)
            call DestroyTrigger(disable)
            call DestroyTrigger(targetDeath)
            call DestroyTrigger(victimDeath)
            call DestroyTimer(duration)
            
            //remove bonuses from the targeted unit
            call SetUnitBonus(victim, BONUS_ATTACK_SPEED, GetUnitBonus(victim, BONUS_ATTACK_SPEED)-ATTACK_SPEED[level])
        endmethod
        
        //################################################################################################
        private static method disableOrders takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetTriggerUnit()), 1)
            
            call DisableTrigger(data.disable)
            call IssueTargetOrder(data.victim, "attack", data.target)
            call EnableTrigger(data.disable)
        endmethod
        
        //################################################################################################
        private static method searchFilter takes nothing returns boolean
            return IsUnitVisible(GetFilterUnit(), GetOwningPlayer(core__temp.caster))/*
                                            */and(GetFilterUnit()!=core__temp.victim)
        endmethod
        
        private static method search takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetTriggeringTrigger()), 1)
            
            set core__temp=data
            call GroupEnumUnitsInRange(core__enumGroup, GetUnitX(data.victim), GetUnitY(data.victim),/*
                                                        */RADIUS, Filter(function thistype.searchFilter))
            set data.target=FirstOfGroup(core__enumGroup)
            
            call TriggerRegisterDeathEvent(data.targetDeath, data.target)
        endmethod
        
        //################################################################################################
        private static method finish takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetExpiredTimer()), 1)
            if(data!=0) then
                call data.destroy()
            endif
        endmethod
        private static method finishEx takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetTriggeringTrigger()), 1)
            if(data!=0) then
                call data.destroy()
            endif
        endmethod
        
        //################################################################################################
        static method create takes unit caster, unit victim, integer level returns thistype
            local thistype data
            if(thistype[victim]!=0) then
                return 0
            endif
            set data=allocate()     
            set data.caster=caster       
            set data.victim=victim
            set data.level=level-1       
            
            //add the necessary bonus to the spell target and then store the instance of "confusion" 
            //to that unit for reference.
            call SetUnitBonus(victim, BONUS_ATTACK_SPEED, GetUnitBonus(victim, BONUS_ATTACK_SPEED)+ATTACK_SPEED[data.level])
            call SaveInteger(core__table, GetHandleId(data.victim), 1, data)
            
            //if the victim dies, abort
            call TriggerRegisterDeathEvent(data.victimDeath, data.victim)
            call TriggerAddAction(data.victimDeath, function thistype.finishEx)
            
            //if the victim is issued any orders, revert it's order to attack it's target
            call TriggerRegisterUnitEvent(data.disable, data.victim, EVENT_UNIT_ISSUED_ORDER)
            call TriggerRegisterUnitEvent(data.disable, data.victim, EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(data.disable, data.victim, EVENT_UNIT_ISSUED_POINT_ORDER)
            call TriggerAddAction(data.disable, function thistype.disableOrders)
            
            //setup the duration timer
            call TimerStart(data.duration, DURATION[data.level], false, function thistype.finish)
            call SaveInteger(core__table, GetHandleId(data.duration), 1, data)
            
            //search for nearby target to attack
            set core__temp=data
            call GroupEnumUnitsInRange(core__enumGroup, GetUnitX(data.caster), GetUnitY(data.caster),/*
                                                        */RADIUS, Filter(function thistype.searchFilter))
                                                        
            set data.target=FirstOfGroup(core__enumGroup)
            
            call DisableTrigger(data.disable)
            call IssueTargetOrder(data.victim, "attack", data.target)
            call EnableTrigger(data.disable)
            
            //if the target should die, a new target must be selected
            call TriggerRegisterDeathEvent(data.targetDeath, data.target)
            call TriggerAddAction(data.targetDeath, function thistype.search)
            call SaveInteger(core__table, GetHandleId(data.targetDeath), 1, data)
            
            return data
        endmethod
    endstruct
    
    //################################################################################################
    public function checkId takes nothing returns boolean
        return GetSpellAbilityId()==ABIL_ID
    endfunction
    
    public function run takes nothing returns nothing
        //the spell is supposed to disable a target enemy unit and have it attack nearby targets. if
        //targets are no where around, then the affected unit is to attack a dummy created nearby.
        local confusionspell spell=confusionspell.create/*
                */(GetTriggerUnit(), GetSpellTargetUnit(), GetUnitAbilityLevel(GetTriggerUnit(), ABIL_ID))
        
        if(spell==0) then
            //if the unit is already confused, this spell-cast will merely deal damage to it.
            call UnitDamageTarget(GetTriggerUnit(), GetSpellTargetUnit(),/*
                                */DAMAGE[GetUnitAbilityLevel(GetTriggerUnit(), ABIL_ID)-1],/*
                                */false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
        endif
    endfunction

    //################################################################################################
    public function onDamageFilter takes nothing returns boolean
        return (GetUnitAbilityLevel(GetEventDamageSource(), BUFF_ID)>0)
    endfunction
    
    public function onDamage takes nothing returns nothing
        local confusionspell data
        call EnableDamageEvents(false)
        
        set data=LoadInteger(core__table, GetHandleId(GetEventDamageSource()), 1)
        call UnitDamageTarget(data.caster, data.victim, GetEventDamage()*RETURN_PERCENT[data.level],/*
                                    */false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
        
        call EnableDamageEvents(true)
    endfunction
    
    //################################################################################################
    public function init takes nothing returns nothing
        local trigger t=CreateTrigger()
        local integer i=0
        loop
            exitwhen(i==16)
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set i=i+1
        endloop
        call TriggerAddCondition(t, Filter(function checkId))
        call TriggerAddAction(t, function run)
        
        //--------------------------------------------------------------------------------------------
        // Ability Data Table
        //--------------------------------------------------------------------------------------------
        set DURATION[0]         =10.0       //the duration that the spell "Confusion" will last on  
        set DURATION[1]         =2.50       //an affected unit.
        set DURATION[2]         =3.25
        set DURATION[4]         =4.00
        //--------------------------------------------------------------------------------------------
        set RETURN_PERCENT[0]   =0.75       //the target of the "Confusion" spell deals a percentage 
        set RETURN_PERCENT[1]   =0.20       //it's damage (per attack) back to itself.
        set RETURN_PERCENT[2]   =0.25
        set RETURN_PERCENT[3]   =0.30
        //--------------------------------------------------------------------------------------------
        set ATTACK_SPEED[0]     =100        //the target of the "Confusion" spell gains an attack
        set ATTACK_SPEED[1]     =20         //speed bonus modifier. 
        set ATTACK_SPEED[2]     =30
        set ATTACK_SPEED[3]     =40
        //--------------------------------------------------------------------------------------------
        set DAMAGE[0]           =100        //the damage that is recorded here will be dealt if a
        set DAMAGE[1]           =200        //confused unit has "Confusion" casted on it while still
        set DAMAGE[2]           =300        //under the effects of the spell.
        set DAMAGE[3]           =400
        //--------------------------------------------------------------------------------------------

        call OnDamageEvent(core__onDamage, true)
        call TriggerAddCondition(core__onDamage, Filter(function onDamageFilter))
        call TriggerAddAction(core__onDamage, function onDamage)
    endfunction

endscope

I do have a question though. What happens if the affected unit is attacking the caster of the spell and then another unit becomes an available target, that was not available before. Should it switch targets (if so then I need to apply a timer that constantly searches for targets)?

I removed the stack, I completely overlooked the fact that I could do an even quicker hash-table look-up. I'll post the new code. I've finished implementing BonusMod too, but some of the values currently are not what you specified them to be because I wanted to test extremes to see if it worked properly.

The first spell, Confusion, has been completed. I am updating a test-map. Currently the spell is capable of targeting allies (so you can see how the unit disabling works).

Berbanog said:
I do have a question though. What happens if the affected unit is attacking the caster of the spell and then another unit becomes an available target, that was not available before. Should it switch targets (if so then I need to apply a timer that constantly searches for targets)?

Please answer this question though, as I have not yet fully implemented the priorities on what the target unit will attack. It's getting a little late now though (damn spell took almost two hours to finalize).
 

Attachments

Last edited:
Wow, It looks great, you completly got the idea :)

Just a couple things:

One: Can you cause the second/third/fourth cast on the same unit deal damage?
I mean, if Confusion is cast on an unit that already has the Confusion debuff (an unit that is already under the effects of Confusion), then it deals damage (100/150/200/250 per level), that way Confusion Synergys with "Body and Mind" (the second skill)

For your question, I do not want to cause this to get too complicated for you, but I'll explain exactly what I had in mind:

Confusion would prioritise Closest hero in sight (besides Caster and affected unit)> Closest unit in sight (Besides Caster and affected unit)> Caster> affected unit
Ok, that I told you already
then: the confused unit would act almost as a neutral creep would = if the target the unit is attacking gets too far, then it redirects its aggro to another unit

Is the last one possible to make? if not, no worries

And that's basically the answer to your question, the confused unit would change targets if the unit it is attacking moves, lets say, 400 units away and the confused unit has SOME OTHER units to attack in range (cos if it does not, it will keep following the only one it is able to attack)

Does that answer your question?
 
Div said:
One: Can you cause the second/third/fourth cast on the same unit deal damage?
I mean, if Confusion is cast on an unit that already has the Confusion debuff (an unit that is already under the effects of Confusion), then it deals damage (100/150/200/250 per level), that way Confusion Synergys with "Body and Mind" (the second skill)

It does. Try it again. Currently (unless you've modified the cooldown, or used the "thedudeabides" cheat) it is impossible for this to go off since the cooldown of the spell exceeds the duration time. Either modify the cooldown to make it easier on you, or type thedudeabides after you've casted it, and cast it again on the same unit. It should be fairly obvious. By the way, that's what the DAMAGE[ ] values are for.

Div said:
then: the confused unit would act almost as a neutral creep would = if the target the unit is attacking gets too far, then it redirects its aggro to another unit

Well I'll have to use a timer to update it's "aggro". I have it setup so that it's quite easy for me to focus it on a single unit, so we'll see how complicated it is. :D

Div said:
And that's basically the answer to your question, the confused unit would change targets if the unit it is attacking moves, lets say, 400 units away and the confused unit has SOME OTHER units to attack in range (cos if it does not, it will keep following the only one it is able to attack)

Okay so I'll constantly check for the closest hero, then the closest unit, then the caster, and then if not make the unit attack itself. I'm going to use a pretty simple method of detecting this though, so it's not like it's going to have an entire AI built into the spell, that would be a little over complicated. Though now that I'm using a timer I can remove my trigger that detects the death of the target.

Oh, I do actually have another question. All of your numbers seem really small and ineffective.

Dev said:
Lasts 1.75/2.5/3.25/4 seconds.

I mean, with a 10% attack speed bonus and a 1.75 duration this spell really won't do much of anything, as it will probably take more than 1.75 seconds for the affected unit to even attack once. I highly suggest you buff up your numbers to more realistic values so it doesn't seem like all this code is a complete waste.

God you really know how to make things complicated eh? This is my code currently for the priority checking:

JASS:
            if(IsUnitType(filtunit, UNIT_TYPE_HERO)) and(filtunit!=core__temp.caster) then
                if(priority__targetHero) then
                    if(range<priority__targetRange) or(priority__target==null) then
                        set priority__target=filtunit
                        set priority__targetHero=true
                        set priority__targetUnit=false
                        set priority__targetCaster=false
                        set priority__targetRange=range
                    endif
                endif
            endif
            if not(priority__targetHero) then
                if not(IsUnitType(filtunit, UNIT_TYPE_HERO)) and(filtunit!=core__temp.caster) then
                    if(range<priority__targetRange) or(priority__target==null) then
                        set priority__target=filtunit
                        set priority__targetHero=false
                        set priority__targetUnit=true
                        set priority__targetCaster=false
                        set priority__targetRange=range
                    endif
                endif
            endif

Okay, here's another test-map with some priorities 'n stuff. I still haven't finished the self-targeting mechanism, but the priorities seem to be working really well. This spell is taking more code than my damn Projectiles library. This is the priority code that I have laid out so far:

JASS:
            if(result) then
                set range=SquareRoot((GetUnitX(filtunit)-GetUnitX(core__temp.victim))*/*
                                        */(GetUnitX(filtunit)-GetUnitX(core__temp.victim))+/*
                                        */(GetUnitY(filtunit)-GetUnitY(core__temp.victim))*/*
                                        */(GetUnitY(filtunit)-GetUnitY(core__temp.victim)))
                                        
                if(IsUnitType(filtunit, UNIT_TYPE_HERO)) and(filtunit!=core__temp.caster)/*
                                                                        */and(filtunit!=core__temp.victim) then
                    if(priority__targetHero) then
                        if(range<priority__targetRange) or(priority__target==null) then
                            set priority__target=filtunit
                            set priority__targetRange=range
                        endif
                    else
                        set priority__target=filtunit
                        set priority__targetHero=true
                        set priority__targetUnit=false
                        set priority__targetCaster=false
                        set priority__targetRange=range
                    endif
                    
                elseif not(IsUnitType(filtunit, UNIT_TYPE_HERO)) and(filtunit!=core__temp.caster)/*
                                                                        */and(filtunit!=core__temp.victim) then
                    if(priority__targetUnit) then
                        if(range<priority__targetRange) or(priority__target==null) then
                            set priority__target=filtunit
                            set priority__targetRange=range
                        endif
                    else
                        if not(priority__targetHero) then
                            set priority__target=filtunit
                            set priority__targetHero=false 
                            set priority__targetUnit=true
                            set priority__targetCaster=false
                            set priority__targetRange=range
                        endif
                    endif
                    
                endif
                
                if not(priority__targetHero) and not(priority__targetUnit) and(filtunit==core__temp.caster) then
                    set priority__target=filtunit
                    set priority__targetHero=false
                    set priority__targetUnit=false
                    set priority__targetCaster=true
                    set priority__targetRange=0
                endif
            endif

It's a beast.

Okay, I've updated the uploaded test-map with a fully completed version. The self-attack works, priorities work. Everything is cleaned up properly, too, there should be no leaks. From the looks of it the other requests are a lot more simple in concept, this one seems to have 8 different affects all intertwined. Yeesh.

I'm going to take a little break from these requests, perhaps for a couple of hours. Maybe I'll start work on them again tonight.

Here's the full spell code:
JASS:
scope ConfusionSpell initializer init

    //################################################################################################
    globals
    
        public      constant integer    ABIL_ID                 = 'A001'
        public      constant integer    BUFF_ID                 = 'B000'
        
        public      constant integer    DUMMY_TARGET_ID         = 'dumm'
        
        //the radius will act as a "maximum" search area because if there are units covered by the
        //fog of war the affected "confused" unit will not be issued to attack it.
        public      constant real       RADIUS                  = 1500      
        
        //this is the period between unit-priority target updating. if the unit is to switch targets,
        //this will be the minimum amount of time it will wait before it actually does.
        public      constant real       REFRESH_PERIOD          = 2
        
        public      real array          DURATION            
        public      real array          RETURN_PERCENT      
        public      integer array       ATTACK_SPEED
        public      real array          DAMAGE 
        
        //used to detect nearby targets for the affected unit to attack.
        private     group               core__enumGroup         = CreateGroup()
        private     confusionspell      core__temp         
        private     boolexpr            core__searchFilter     
        
        //used to return damage to confused units when they attack
        private     trigger             core__onDamage          = CreateTrigger()
        
        private     hashtable           core__table             = InitHashtable()
        
        //the following are related to target priority for the affected unit. in a case where there
        //are ample available targets, this is the priority list:
        //      > closest hero
        //      > closest unit
        //      > caster
        //      > itself
        private     boolean             priority__targetHero    = false
        private     boolean             priority__targetUnit    = false
        private     boolean             priority__targetCaster  = false
        
        private     real                priority__targetRange   = 0
        private     unit                priority__target        = null
        
    endglobals
    
    //################################################################################################
    struct confusionspell
    
        public      unit        caster      = null
        public      unit        victim      = null
        private     unit        target      = null
        private     unit        targetDummy = null
        public      integer     level
        
        private     trigger     disable     = CreateTrigger()
        private     trigger     victimDeath = CreateTrigger()
        
        private     timer       duration    = CreateTimer()
        private     timer       refresh     = CreateTimer()
        
        
        static method operator [] takes unit victim returns thistype
            return LoadInteger(core__table, GetHandleId(victim), 1)
        endmethod
        
        method onDestroy takes nothing returns nothing
            call RemoveSavedInteger(core__table, GetHandleId(duration), 1)
            call RemoveSavedInteger(core__table, GetHandleId(victim), 1)
            call RemoveSavedInteger(core__table, GetHandleId(victimDeath), 1)
            call RemoveSavedInteger(core__table, GetHandleId(refresh), 1)
            
            call PauseTimer(duration)
            call PauseTimer(refresh)
            call DestroyTimer(refresh)
            call DestroyTimer(duration)
            
            call DestroyTrigger(disable)
            call DestroyTrigger(victimDeath)
            
            //remove bonuses from the targeted unit and make unit stop
            call SetUnitBonus(victim, BONUS_ATTACK_SPEED, GetUnitBonus(victim, BONUS_ATTACK_SPEED)-ATTACK_SPEED[level])
            call IssueImmediateOrder(victim, "stop")  
            if(targetDummy!=null) then
                call RemoveUnit(targetDummy)
            endif
        endmethod
        
        //################################################################################################
        private static method disableOrders takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetTriggerUnit()), 1)
            
            call DisableTrigger(data.disable)
            call IssueTargetOrder(data.victim, "attack", data.target)
            call EnableTrigger(data.disable)
        endmethod
        
        //################################################################################################
        private static method searchFilter takes nothing returns boolean
            local unit filtunit=GetFilterUnit()
            local real range
            local boolean result=IsUnitVisible(filtunit, GetOwningPlayer(core__temp.victim))/*
                                            */and not(IsUnitType(filtunit, UNIT_TYPE_DEAD))/*
                                            */and not(IsUnitType(filtunit, UNIT_TYPE_FLYING))/*
                                            */and(filtunit!=core__temp.victim)/*
                                            */and(GetUnitTypeId(filtunit)!=DUMMY_TARGET_ID)
            if(result) then
                set range=SquareRoot((GetUnitX(filtunit)-GetUnitX(core__temp.victim))*/*
                                        */(GetUnitX(filtunit)-GetUnitX(core__temp.victim))+/*
                                        */(GetUnitY(filtunit)-GetUnitY(core__temp.victim))*/*
                                        */(GetUnitY(filtunit)-GetUnitY(core__temp.victim)))
                                        
                if(IsUnitType(filtunit, UNIT_TYPE_HERO)) and(filtunit!=core__temp.caster)/*
                                                                        */and(filtunit!=core__temp.victim) then
                    if(priority__targetHero) then
                        if(range<priority__targetRange) or(priority__target==null) then
                            set priority__target=filtunit
                            set priority__targetRange=range
                        endif
                    else
                        set priority__target=filtunit
                        set priority__targetHero=true
                        set priority__targetUnit=false
                        set priority__targetCaster=false
                        set priority__targetRange=range
                    endif
                elseif not(IsUnitType(filtunit, UNIT_TYPE_HERO)) and(filtunit!=core__temp.caster)/*
                                                                        */and(filtunit!=core__temp.victim) then
                    if(priority__targetUnit) then
                        if(range<priority__targetRange) or(priority__target==null) then
                            set priority__target=filtunit
                            set priority__targetRange=range
                        endif
                    else
                        if not(priority__targetHero) then
                            set priority__target=filtunit
                            set priority__targetHero=false 
                            set priority__targetUnit=true
                            set priority__targetCaster=false
                            set priority__targetRange=range
                        endif
                    endif
                endif
                if not(priority__targetHero) and not(priority__targetUnit) and(filtunit==core__temp.caster) then
                    set priority__target=filtunit
                    set priority__targetHero=false
                    set priority__targetUnit=false
                    set priority__targetCaster=true
                    set priority__targetRange=0
                endif
            endif
                                
            set filtunit=null
            return result
        endmethod
        
        //################################################################################################
        private static method refreshOrders takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetExpiredTimer()), 1)
            
            set core__temp=data
            call GroupEnumUnitsInRange(core__enumGroup, GetUnitX(data.victim), GetUnitY(data.victim),/*
                                                        */RADIUS, core__searchFilter)
            set data.target=priority__target
            set priority__target=null
            set priority__targetHero=false
            set priority__targetUnit=false
            set priority__targetCaster=false
            set priority__targetRange=0
            
            if(data.target==null) then
                if(data.targetDummy==null) then
                    //we want the self-attack mechanism to come into play
                    set data.targetDummy=CreateUnit(GetOwningPlayer(data.victim), DUMMY_TARGET_ID, GetUnitX(data.victim), GetUnitY(data.victim), 0)
                    call UnitRemoveAbility(data.targetDummy, 'Amov')
                endif
                set data.target=data.targetDummy
            else
                if(data.targetDummy!=null) then
                    call RemoveUnit(data.targetDummy)
                    set data.targetDummy=null
                endif
            endif
            call DisableTrigger(data.disable)
            call IssueTargetOrder(data.victim, "attack", data.target)
            call EnableTrigger(data.disable)
        endmethod
        
        //################################################################################################
        private static method finish takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetExpiredTimer()), 1)
            if(data!=0) then
                call data.destroy()
            endif
        endmethod
        private static method finishEx takes nothing returns nothing
            local thistype data=LoadInteger(core__table, GetHandleId(GetTriggeringTrigger()), 1)
            if(data!=0) then
                call data.destroy()
            endif
        endmethod
        
        //################################################################################################
        static method create takes unit caster, unit victim, integer level returns thistype
            local thistype data
            if(thistype[victim]!=0) then        //units that are already under the effects of confusion
                return 0                        //are not able to be "confused" again, instead this
            endif                               //constructor method will simply return 0
            set data=allocate()     
            set data.caster=caster       
            set data.victim=victim
            set data.level=level-1       
            
            //add the necessary bonus to the spell target and then store the instance of "confusion" 
            //to that unit for reference.
            call SetUnitBonus(victim, BONUS_ATTACK_SPEED, GetUnitBonus(victim, BONUS_ATTACK_SPEED)+ATTACK_SPEED[data.level])
            call SaveInteger(core__table, GetHandleId(data.victim), 1, data)
            
            //if the victim dies, abort
            call TriggerRegisterDeathEvent(data.victimDeath, data.victim)
            call TriggerAddAction(data.victimDeath, function thistype.finishEx)
            
            //if the victim is issued any orders, revert it's order to attack it's target
            call TriggerRegisterUnitEvent(data.disable, data.victim, EVENT_UNIT_ISSUED_ORDER)
            call TriggerRegisterUnitEvent(data.disable, data.victim, EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(data.disable, data.victim, EVENT_UNIT_ISSUED_POINT_ORDER)
            call TriggerAddAction(data.disable, function thistype.disableOrders)
            
            //setup the duration timer
            call TimerStart(data.duration, DURATION[data.level], false, function thistype.finish)
            call SaveInteger(core__table, GetHandleId(data.duration), 1, data)
            
            //in addition to the duration timer, there is a timer that will force a priority on the 
            //affected unit's attack target
            call TimerStart(data.refresh, REFRESH_PERIOD, true, function thistype.refreshOrders)
            call SaveInteger(core__table, GetHandleId(data.refresh), 1, data)
            
            //search for nearby target to attack - similar actions are performed in refreshOrders
            set core__temp=data
            set priority__targetRange=-1
            call GroupEnumUnitsInRange(core__enumGroup, GetUnitX(data.victim), GetUnitY(data.victim),/*
                                                        */RADIUS, core__searchFilter)
                                                        
            //at the moment, the FirstOfGroup() is used to determine the appropriate target. it should
            //be narrowed down with priorities, only attacking certain units if absolutely necessary
            set data.target=priority__target
            set priority__target=null
            set priority__targetHero=false
            set priority__targetUnit=false
            set priority__targetCaster=false
            set priority__targetRange=0
            
            if(data.target==null) then
                //we want the self-attack mechanism to come into play
                set data.targetDummy=CreateUnit(GetOwningPlayer(data.victim), DUMMY_TARGET_ID, GetUnitX(data.victim), GetUnitY(data.victim), 0)
                call UnitRemoveAbility(data.targetDummy, 'Amov')
                
                set data.target=data.targetDummy
            endif
            call DisableTrigger(data.disable)
            call IssueTargetOrder(data.victim, "attack", data.target)
            call EnableTrigger(data.disable)
            
            return data
        endmethod
        
        private static method onInit takes nothing returns nothing
            set core__searchFilter=Filter(function thistype.searchFilter)
        endmethod
    endstruct
    
    //################################################################################################
    public function checkId takes nothing returns boolean
        return GetSpellAbilityId()==ABIL_ID
    endfunction
    
    public function run takes nothing returns nothing
        //the spell is supposed to disable a target enemy unit and have it attack nearby targets. if
        //targets are no where around, then the affected unit is to attack a dummy created nearby.
        local confusionspell spell=confusionspell.create/*
                */(GetTriggerUnit(), GetSpellTargetUnit(), GetUnitAbilityLevel(GetTriggerUnit(), ABIL_ID))
        
        if(spell==0) then
            //if the unit is already confused, this spell-cast will merely deal damage to it.
            call UnitDamageTarget(GetTriggerUnit(), GetSpellTargetUnit(),/*
                                */DAMAGE[GetUnitAbilityLevel(GetTriggerUnit(), ABIL_ID)-1],/*
                                */false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
        endif
    endfunction

    //################################################################################################
    public function onDamageFilter takes nothing returns boolean
        return (GetUnitAbilityLevel(GetEventDamageSource(), BUFF_ID)>0)
    endfunction
    
    public function onDamage takes nothing returns nothing
        local confusionspell data
        call EnableDamageEvents(false)
        
        set data=LoadInteger(core__table, GetHandleId(GetEventDamageSource()), 1)
        call UnitDamageTarget(data.caster, data.victim, GetEventDamage()*RETURN_PERCENT[data.level],/*
                                    */false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
        
        call EnableDamageEvents(true)
    endfunction
    
    //################################################################################################
    public function init takes nothing returns nothing
        local trigger t=CreateTrigger()
        local integer i=0
        loop
            exitwhen(i==16)
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set i=i+1
        endloop
        call TriggerAddCondition(t, Filter(function checkId))
        call TriggerAddAction(t, function run)
        
        //--------------------------------------------------------------------------------------------
        // Ability Data Table
        //--------------------------------------------------------------------------------------------
        set DURATION[0]         =10.0       //the duration that the spell "Confusion" will last on  
        set DURATION[1]         =2.50       //an affected unit.
        set DURATION[2]         =3.25
        set DURATION[4]         =4.00
        //--------------------------------------------------------------------------------------------
        set RETURN_PERCENT[0]   =0.75       //the target of the "Confusion" spell deals a percentage 
        set RETURN_PERCENT[1]   =0.20       //it's damage (per attack) back to itself.
        set RETURN_PERCENT[2]   =0.25
        set RETURN_PERCENT[3]   =0.30
        //--------------------------------------------------------------------------------------------
        set ATTACK_SPEED[0]     =100        //the target of the "Confusion" spell gains an attack
        set ATTACK_SPEED[1]     =20         //speed bonus modifier. 
        set ATTACK_SPEED[2]     =30
        set ATTACK_SPEED[3]     =40
        //--------------------------------------------------------------------------------------------
        set DAMAGE[0]           =100        //the damage that is recorded here will be dealt if a
        set DAMAGE[1]           =200        //confused unit has "Confusion" casted on it while still
        set DAMAGE[2]           =300        //under the effects of the spell.
        set DAMAGE[3]           =400
        //--------------------------------------------------------------------------------------------

        call OnDamageEvent(core__onDamage, true)
        call TriggerAddCondition(core__onDamage, Filter(function onDamageFilter))
        call TriggerAddAction(core__onDamage, function onDamage)
    endfunction

endscope
 

Attachments

Last edited:
You sir, you are a genious
I garantee you this is the hardest spell to code
The second one is just creating another unit that mirrors the casters actions (except attack)
the third one may be complicated for the "stacking" reason and for passing vision of sight from the target to the caster
the ultimate has one complicated thing in my opinion: checking the "resistance" percentage the target has, but that's all also

Again, thank you so much for everything, I can't express through here how excited I am for having such a capable person helping me
Thank you very very much my friend :)

Hope you don't give up on me lol haha
 
Div said:
the third one may be complicated for the "stacking" reason and for passing vision of sight from the target to the caster

Actually, using BonusMod it will actually be pretty easy.

Div said:
Hope you don't give up on me lol haha

Let's hope. Haha.

Okay, I've come across another problem. In your descriptions you frequently refer to the "maximum sight area" for each specific unit. This value cannot be referenced by any function in the JASS API. The only way to achieve this effect would be to trigger each unit's sight radius and go from there. Either that or record the sight radius of each unit and use a value from that table.

I almost have Curse of the Blind done (using constant values, rather than percentages), here is the code so far;

JASS:
scope CurseOfTheBlind initializer init

    //################################################################################################
    globals
    
        public      constant integer        ABIL_ID         = 'A000'
        public      constant integer        BUFF_ID         = 'B001'
    
        public      integer array           SIGHT_PER_LEVEL
        public      real array              DURATION   
        public      constant integer        STACK_AMOUNT    = 5
    
    endglobals

    //################################################################################################
    private struct spelldata
    
        public          unit                caster                  = null
        public          unit                target                  = null
    
        public          integer             sightModifier
        public          integer             level
        
        method onDestroy takes nothing returns nothing
            call SetUnitBonus(target, BONUS_SIGHT_RANGE, GetUnitBonus(target, BONUS_SIGHT_RANGE)+sightModifier)
            call SetUnitBonus(caster, BONUS_SIGHT_RANGE, GetUnitBonus(target, BONUS_SIGHT_RANGE)-sightModifier)
        endmethod
        
        static method create takes nothing returns thistype
            local thistype data=allocate()
            set data.caster=GetTriggerUnit()
            set data.target=GetSpellTargetUnit()
            set data.level=GetUnitAbilityLevel(data.caster, ABIL_ID)
            set data.sightModifier=data.level*SIGHT_PER_LEVEL[data.level]
            
            call SetUnitBonus(data.caster, BONUS_SIGHT_RANGE, GetUnitBonus(data.caster, BONUS_SIGHT_RANGE)/*
                                                                            */+data.sightModifier)
            call SetUnitBonus(data.target, BONUS_SIGHT_RANGE, GetUnitBonus(data.target, BONUS_SIGHT_RANGE)/*        
                                                                            */-data.sightModifier)
            return data
        endmethod
    endstruct
    
    //################################################################################################
    struct curseoftheblind
        
        private         spelldata array     ref__entry              [STACK_AMOUNT]
        private         integer             ref__entrysize          = 0
        
        private         real                ref__timeLeft  
        
        private static  timer               core__looptmr           = CreateTimer()
        private static  constant real       core__looptmrRef        = 0.2
        
        private static  thistype array      core__activeStack
        private static  integer             core__activeStackSize   = 0
        private         integer             core__activeStackIndex  
        private         boolean             core__active            = false
        
        method onDestroy takes nothing returns nothing
            local integer i=0
            loop
                exitwhen(i==ref__entrysize)
                call ref__entry[i].destroy()
                set i=i+1
            endloop
        endmethod
        
        //################################################################################################
        private static method loopFunc takes nothing returns nothing
            local integer i=core__activeStackSize-1
            local integer j
            loop
                exitwhen(i<0)
                if(core__activeStack[i]!=0) then
                    set core__activeStack[i].ref__timeLeft=core__activeStack[i].ref__timeLeft-core__looptmrRef
                    if(core__activeStack[i].ref__timeLeft<=0) then
                        set core__activeStack[i].core__active=false
                        set core__activeStackSize=core__activeStackSize-1
                        set core__activeStack[i]=core__activeStack[core__activeStackSize]
                        if(core__activeStackSize==0) then
                            call PauseTimer(core__looptmr)
                        endif
                        set j=0
                        loop
                            exitwhen(j==core__activeStack[i].ref__entrysize)
                            call core__activeStack[i].ref__entry[j].destroy()
                            set j=j+1
                        endloop
                        set core__activeStack[i].ref__entrysize=0
                    endif
                endif
                set i=i-1
            endloop
        endmethod
        
        //################################################################################################
        method addStack takes spelldata entry returns nothing   
            local integer i
            if not(core__active) then
                if(thistype.core__activeStackSize==0) then
                    call TimerStart(thistype.core__looptmr, thistype.core__looptmrRef, true, function thistype.loopFunc)
                endif
                set thistype.core__activeStack[thistype.core__activeStackSize]=this
                set thistype.core__activeStackSize=thistype.core__activeStackSize+1
                set core__active=true
            endif
            if(ref__entrysize<STACK_AMOUNT) then
                set ref__entry[ref__entrysize]=entry
                set ref__entrysize=ref__entrysize+1
            else
                call ref__entry[0].destroy()
                set i=0
                loop
                    exitwhen(i==ref__entrysize-1)
                    set ref__entry[i]=ref__entry[i+1]
                    set i=i+1
                endloop
                set ref__entry[STACK_AMOUNT]=entry
            endif
            set ref__timeLeft=DURATION[ref__entry[STACK_AMOUNT]]
        endmethod
        
        implement   AutoCreate
        implement   AutoDestroy
    endstruct
    
    //################################################################################################
    public function checkId takes nothing returns boolean
        return GetSpellAbilityId()==ABIL_ID
    endfunction
    
    public function run takes nothing returns nothing
        local spelldata sdat=spelldata.create()
        
        call curseoftheblind[GetSpellTargetUnit()].addStack(sdat)
    endfunction

    //################################################################################################
    public function init takes nothing returns nothing
        local trigger t=CreateTrigger()
        local integer i=0
        loop
            exitwhen(i==16)
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            set i=i+1
        endloop
        call TriggerAddCondition(t, Filter(function checkId))
        call TriggerAddAction(t, function run)
        
        //---------------------------------------------------------------------------------------------
        set SIGHT_PER_LEVEL[0]      =100
        set SIGHT_PER_LEVEL[1]      =200
        set SIGHT_PER_LEVEL[2]      =300
        set SIGHT_PER_LEVEL[3]      =400
        //---------------------------------------------------------------------------------------------
        set DURATION[0]             =10
        set DURATION[1]             =10
        set DURATION[2]             =10
        set DURATION[3]             =10
        //---------------------------------------------------------------------------------------------
    endfunction

endscope

Alright, finished up this one too. I am going to wait for you to determine how we're going to go about this sight-percentage problem.

The stacking is setup such that it will store a variable amount (default is set at 5) of stacks for each unit. Each stack of casts will be nulled after the duration runs out (and no other instances have been added to the stack). It keeps a constant record of the most recent 5 instances in each stack, though the duration is set by using the value of the duration at the level of the most recent instance of the spell added to the stack.

Let me give you an example. Say you had two levels of the ability, level 2 had a duration of 20 seconds and level 1 had a duration of 10 seconds. Now you cast level 2 on a unit, which will apply the 20-second timer from the duration. Now you cast the level 1 version on the same unit; this will apply the 10-second timer and it will not run for the extra 10 seconds that were previously available. If you want me to change this, just say so. I wasn't sure exactly how you wanted it so I just put whatever. It's all fairly modular and easy to change for me.

I'll upload a test-map so you can see how it works on an allied unit. Also I haven't even done any of the Object Editor stuff for the spell yet so it's just called Drunken Haze. hope you don't mind :P

Alright, few things to clear up on second spell, Curse of the Blind. Without being able to grab any information on a unit's sight-range it's nearly impossible to know exactly when a unit can no longer see itself. I could check to see if the unit is visible to it's owner and if not try to setup some way of disabling it's vision from depleting any further. If there is anything else that modifies vision though it's going to screw this up right away.

Since it's going to be tough to acknowledge when a unit no longer has ample sight range judging when it is supposed to miss it's attack will also be hard. Again, I could try to implement some sort of "no-vision-zone" detection by checking the owning player's visibility of the unit, which would let me know when a unit has no vision or not (for giving him a 100% chance to miss attacks 'n stuff) but even after that point knowing how much vision he needs in order to see is undetectable (unless vision is 100% hard-coded).
 

Attachments

Last edited:
Don't use as percentage then, just do a fix value like:

Steas x vision sight from a unit and gives 50% of that value to Ty
Level 1 steals 150/Level 2 steals 200/Level 3 Steals 250/Level 4 Steals 350 for 12 seconds.

Also, for the "unit not being able to see itself", don't worry about that, my idea is pretty simple
Whenever a Unit gets 0 vision of sight from having it removed by Curse of the Blind, it actually stays with 50 or i don't know, any value that allows it to see at least itself (like landmines have vision of only themselves)

And the missing chance, I don't know if it is hard, but for every 100 area of sight lost by the target, it would have 2% less resistance chance from Mind Control and 5% missing chance on attacks... is that possible?
 
Don't use as percentage then, just do a fix value like:

Steas x vision sight from a unit and gives 50% of that value to Ty
Level 1 steals 150/Level 2 steals 200/Level 3 Steals 250/Level 4 Steals 350 for 12 seconds.

Also, for the "unit not being able to see itself", don't worry about that, my idea is pretty simple
Whenever a Unit gets 0 vision of sight from having it removed by Curse of the Blind, it actually stays with 50 or i don't know, any value that allows it to see at least itself (like landmines have vision of only themselves)

And the missing chance, I don't know if it is hard, but for every 100 area of sight lost by the target, it would have 2% less resistance chance from Mind Control and 5% missing chance on attacks... is that possible?

PS: I don't mind at all you just leaving it as Drunken Haze, your doing a lot for me already
The only problem is, everytime I try to open your map and save something It appears that I can't :( why's that?
 
Div said:
Whenever a Unit gets 0 vision of sight from having it removed by Curse of the Blind, it actually stays with 50 or i don't know, any value that allows it to see at least itself (like landmines have vision of only themselves)

There is no such thing as "0 vision of sight". You can't determine how much sight a specific unit has.

Div said:
And the missing chance, I don't know if it is hard, but for every 100 area of sight lost by the target, it would have 2% less resistance chance from Mind Control and 5% missing chance on attacks... is that possible?

Yea, recording how much sight has been stolen from the unit is easy.

Div said:
The only problem is, everytime I try to open your map and save something It appears that I can't :( why's that?

Do you use JNGP with the latest JassHelper?
 
Last edited:
Well if you don't have it then you won't be able to save these spells... I thought I was clear that I used JassHelper with JNGP. It's a publicly available resource, so you're free to go download it.
 
I've been quite busy.

I finished a Camera system for a friend, Midnighters, that he requested some time ago. Also I have been doing some basic requests for a Warsong Gulch map or something like that. I wanted to hear back from you and make sure you got JNGP installed and everything.
 
Okay, so then could you review some of the problems (above) that I was having with some of your specifications for the Curse of the Blind spell?

Div said:
Also, for the "unit not being able to see itself", don't worry about that, my idea is pretty simple
Whenever a Unit gets 0 vision of sight from having it removed by Curse of the Blind, it actually stays with 50 or i don't know, any value that allows it to see at least itself (like landmines have vision of only themselves)

Berb said:
There is no such thing as "0 vision of sight". You can't determine how much sight a specific unit has.

I'm thinking that there may be a way around this, using BonusMod, but it would require the fog fade-time to be completely instant. I don't know if this will affect any of your game mechanics, but it should allow me to determine the maximum amount of sight that can be deducted from a unit before it can no longer see itself.

No, it doesn't look like there's an option available that would allow me to do this, after checking.

Another possibility would be to hard-code values for each unit type, which would easily allow you to determine the maximum amount of visibility that can be reduced before the unit is "blind".
 
Damn... I guess Curse of the Blind ended up being the hardest one after all :(
Well, since you're the genious among us, anything you decide to be the better way will be the better way to do this, so I totally trust you :)
 
Okay, well in that case I think I'll do the hard-coded values. If you need more help setting this up in your own map then I'll help you out with that too, since it's going not going to your average GUI solution.

On a good note, once Curse of the Blind is finished I have a good feeling that Mind Control and Body and Mind will be relatively simple. I've already got lots of the leg-work laid out for Mind Control simply within Curse of the Blind spell, so once I add a couple of things to enable specific player control it will be a walk in the park. I hope I am not overlooking anything. Anyways, I'll start finishing up this spell so we can move on.
 
Okay, Curse of the Blind is pretty well finished. I setup a table that lets me reference a unit's sight using their recorded day/night sight radius in combination with BonusMod. I've got the "blind" thing all worked out, and now that we have table values for unit-sight it would be possible to have the spell steal a percentage of the unit's sight.
 
Jesus Christ man, I'm not kidding, you gotta be the best editor out there :o
You finish things so quickly and so perfectly... If I had a well known map I would pay you to help me code things! haha

I'll be waiting for news :)
 
Okay, well I had to digress a little since I found some problems with the way I was doing Blind which would cause the spell to break a unit's visibility. It is directly related to my methods of having a unit with a minimum sight radius so removing what I had added for that the spell works perfectly up until this point.

Nothing major, I just have to re-code some of the Blind mechanisms.

Okay, actually it was just me forgetting to reset a variable to 0. It's working now. I still don't have the percentages setup, but that won't be much more difficult (I don't think). Have a test-map. I still need to implement a hash-table to store the values of unit-type-specific default sight visibilities so that they can be referenced really quickly, rather than having to loop through an entire stack of registered unit-types.

Okay, so the code that is in the test-map is going to look different than this (I'll upload a new test-map soon enough), but this is the new table (using a hash-table).

JASS:
    //################################################################################################
    public function GetUnitSight takes unit u, integer sightType returns integer
        local integer id = GetUnitTypeId(u)
        if HaveSavedInteger(table__hash, id, sightType) then
            return LoadInteger(table__hash, id, sightType)+GetUnitBonus(u, BONUS_SIGHT_RANGE)
        else
            if(sightType == table__type__DAY) then
                return table__sightDayDefault+GetUnitBonus(u, BONUS_SIGHT_RANGE)
            elseif(sightType == table__type__NIGHT) then
                return table__sightNightDefault+GetUnitBonus(u, BONUS_SIGHT_RANGE)
            endif
        endif
        return 0
    endfunction
    
    //The setupTable() function is called on initialization to setup the values for the arrays 
    //defined above. This will allow us to use the GetUnitSight() to reference a unit-type's 
    //default sight modifier values.
    private function setupTable takes nothing returns nothing
    
        call SaveInteger(table__hash, 'hfoo', table__type__DAY, 1400)
        call SaveInteger(table__hash, 'hfoo', table__type__NIGHT, 800)
    
        call SaveInteger(table__hash, 'Hamg', table__type__DAY, 1800)
        call SaveInteger(table__hash, 'Hamg', table__type__NIGHT, 800)
    
    endfunction

This speeds things up tremendously, especially if you're going to have a lot of unit-types with unique values. I have it set up so that if there is a unit that is come across that has not been registered, it will automatically assume a default (which is defined in the constants). Anyway, try out the test-map (functionality-wise there is no change, only the performance of the spell has been improved).

Okay I've got a suggestion. What you could do is take the table out of the spell entirely so that this can be used by everything in your map. Obviously it would still be available to the spell, but you would also be able to reference a unit's sight-range in other spells and shit. Eh, well, I went ahead and did it anyways. Now you've got this library too.

JASS:
library UnitSight initializer setupTable requires BonusMod

    //################################################################################################
    globals
    
        public      constant integer        TYPE_DAY                    = 0
        public      constant integer        TYPE_NIGHT                  = 1
    
        private     constant integer        table__type__DAY            = 0
        private     constant integer        table__type__NIGHT          = 1
        
        private     constant integer        table__sightDayDefault      = 1800
        private     constant integer        table__sightNightDefault    = 800
        
    //------------------------------------------------------------------------------------------------
        private     hashtable               table__hash                 = InitHashtable()
    //------------------------------------------------------------------------------------------------
    endglobals
    
    //################################################################################################
    function GetUnitSight takes unit u, integer sightType returns integer
        local integer id = GetUnitTypeId(u)
        if HaveSavedInteger(table__hash, id, sightType) then
            return LoadInteger(table__hash, id, sightType)+GetUnitBonus(u, BONUS_SIGHT_RANGE)
        else
            if(sightType == table__type__DAY) then
                return table__sightDayDefault+GetUnitBonus(u, BONUS_SIGHT_RANGE)
            elseif(sightType == table__type__NIGHT) then
                return table__sightNightDefault+GetUnitBonus(u, BONUS_SIGHT_RANGE)
            endif
        endif
        return 0
    endfunction
    
    //The setupTable() function is called on initialization to setup the values for the arrays 
    //defined above. This will allow us to use the GetUnitSight() to reference a unit-type's 
    //default sight modifier values.
    private function setupTable takes nothing returns nothing
    
        call SaveInteger(table__hash, 'hfoo', table__type__DAY, 1400)
        call SaveInteger(table__hash, 'hfoo', table__type__NIGHT, 800)
    
        call SaveInteger(table__hash, 'Hamg', table__type__DAY, 1800)
        call SaveInteger(table__hash, 'Hamg', table__type__NIGHT, 800)
    
    endfunction

endlibrary

So you have access to the function GetUnitSight, which could serve useful later on. Right now it only has the necessary features for Curse of the Blind to function properly.
 

Attachments

Last edited:
The spell is perfect! It works properly in every kind of way, and that doesn't not surprise me anymore comming from you! :)

there's just one thing: whenever I open the map using the tools you told me to download, I'm getting an error that does not let me save or test the map, which is:

Line 1651: Unable to find textmacro: "optional"
 
now a new error ocurred: whenever I try to initialize the map this appears:

wehack.lua:437:attempt to index global 'rtc_enabled' (a nil value)

Also, when I download the Grimoire update, the editor does not even open anymore after that... :S
 
Okay well that "rtc_enabled" thing is because you've got Reinventing the Craft enabled (or an anti-virus that is blocking it, in which case you need to allow it as an exception and then disable it). What Reinventing the Craft does is allow certain, non-BNet natives such as GetMousePositionX(). It probably won't be of use to you.

For Grimoire, sift through the pages and see if you can't find any help on the issue. If you can open your editor after undoing some of your last changes then get it open, and see if it works without - in which case you can go through the steps for updating Grimoire later.
 
Got everything working out properly now, thanks for the tips (and sorry for bothering you in so many different ways haha)
:)
 
Alright, I'm starting up on Body and Mind. Could you clarify a few things for me? I'll start by explaining what I understand from your descriptions.

The hero has a chance when it casts a spell to create a "spirit" form of itself. The spirit-form has a few Object Editor specifications such that it only has 100 life and is ethereal (can only be damaged by magic & spells). When the "spirit" has casted its spell limit it will be destroyed.

The spirit has no collision size so it moves through units and if it leaves a certain range of the caster it will be destroyed.

*If a damaging item is used (Dagon, Shivas, etc), the Mind will use it as well² at the targeted unit/point, but will only deal 40% of that item's damage (Body deals 800 with Dagon level 5, Mind will deal 320 = 1120 damage total).

It isn't possible to change the amount of damage dealt by an ability unless the ability is coded. Also you can't possibly determine whether a spell is "damaging" or not unless you've got a data-base of the items that you want specifically to work.

You really should have though about these things when you were making your map. Every spell I've made has required serious unit-manipulation that should have been controlled by your map's systems.

1 - Has a sub-skill which allows you to target where you want the Mind manifestation to go to (as a moving command) but has a standard passive follow whenever Ty's body moves.

Do orders issued directly to the spirit-unit get over ridden by the caster's orders?

If Ty is ordered to move to a target point, the unit will face and head x units towards the same direction

What do you mean, the "same direction"? What happens if you cast this at the bottom of a cliff (to the top of the cliff) and then order the hero that casted it to move away from the cliff? This will completely screw with the spirit's issued order, because he will have to move all the way around the cliff, unless of course it moves over cliffs. If that is true, though, then the only way for the spirit/caster to leave a certain range is if one of them is moved instantly to another location.

I also found a really rare bug that occurred in Curse of the Blind that would make all ethereal units in the center of the map (where the dummy is created) to be blinded permanently. It's fixed now though.

While I'm at it, I might as well review the Mind Control specifics too.

*The controlled unit has a 10/7/5% chance of regaining conscience on each attack it takes.

What is an "attack" defined by? An actual instance of damage, or just an attack event?

*If the controlled unit dies/kills a hero, the credit for the kill goes to Ty (exp as well)

If you want all of that stuff to be applied to the player who is casting Mind Control then you're pretty much limited to the Mind Control changing owners. The original player can still be given vision of the unit, but enemies of the Mind Control target will not attack it (since it will actually be owned by the casting player). The player that originally owned the unit will have absolutely no control over it, and the selection circle will appear as "red" instead of "green". Pretty much all the typical alliance issues, because it is not possible to change unit-specific alliances.

1 - Ty loses control over the unit if any ultimate is used (loses control after the ultimate's effect has been activated)

There is no way of knowing whether an ability is an ultimate, unless, as before, the ultimate abilities are all added to a data-base that I can reference.
 
Deep down I enjoy it.

Don't think I've quit, either, I'm quite busy. Besides, who the hell would quit after making the first two hell-raisers.

Hahahaha maan, you're awesome
You're just like me!

Alright, I'm starting up on Body and Mind. Could you clarify a few things for me? I'll start by explaining what I understand from your descriptions.

The hero has a chance when it casts a spell to create a "spirit" form of itself. The spirit-form has a few Object Editor specifications such that it only has 100 life and is ethereal (can only be damaged by magic & spells). When the "spirit" has casted its spell limit it will be destroyed.

The spirit has no collision size so it moves through units and if it leaves a certain range of the caster it will be destroyed.



It isn't possible to change the amount of damage dealt by an ability unless the ability is coded. Also you can't possibly determine whether a spell is "damaging" or not unless you've got a data-base of the items that you want specifically to work.

You really should have though about these things when you were making your map. Every spell I've made has required serious unit-manipulation that should have been controlled by your map's systems.



Do orders issued directly to the spirit-unit get over ridden by the caster's orders?



What do you mean, the "same direction"? What happens if you cast this at the bottom of a cliff (to the top of the cliff) and then order the hero that casted it to move away from the cliff? This will completely screw with the spirit's issued order, because he will have to move all the way around the cliff, unless of course it moves over cliffs. If that is true, though, then the only way for the spirit/caster to leave a certain range is if one of them is moved instantly to another location.

I also found a really rare bug that occurred in Curse of the Blind that would make all ethereal units in the center of the map (where the dummy is created) to be blinded permanently. It's fixed now though.

Alright, you got the first part, that's correct
The life of the "spirit" also scales by level (100/200/300/400)
Don't worry about "deals 40% of item damage", that's not necessary

Also, about the moving order, the Spirit is UNSELECTABLE, it works like a "locust", it will just automatically do whatever Ty does... but Ty will have a subskill added whenever body and Mind triggers that allows Ty to "order the spirit" to move to a target location

What I mean with that is, did you ever play DotA? Do you know the hero Tauren Chieftain?
He has a similar spell called Ancestral Spirit, which does exactly what the hero does, BUT, moves on opposite directions
Body and Mind would work like that, but not in opposite directions
What I mean is, if ty is ordered to move to a position 600 units away, the Spirit faces the same direction as Ty and moves 600 units that way...
But if that is complicated, just order the spirit to move to the point where ty is moving

For Mind Control:

It would actually work on a damage instance caused by an ATTACK only, not spells
but if that is also too complicated, you can just base it on an attack event, although that could allow some exploits

For the "exp and gold bounty for the kill", do what you think would be the best, cause I have no idea on that one :P
I juts thought you could manipulate the difference of the Exp gained by the controlled unit from the point it got controlled to the end of the spell, and give it to Ty

And finally, for the ultimate, don't worry about it as well, it is not necessary!

Hope I answered it all :)
 
Also, it would be nice to remind you the Synergy between Mind Control and Body and Mind

If Ty casts Mind Control and succeeds in controlling the unit, if the spirit has been triggered it will cast Mind Control as well
AND, if Ty is stunned/interrupted from his channeling of Mind Control, as long as the Spirit is still casting, the unit will remain controlled, and vice-versa
 
Div said:
Don't worry about "deals 40% of item damage", that's not necessary

Actually, I can use the Damage Detection system to ensure that all of the spirit's damage is reduced by 40%, though detecting which spell it came from would be a little more complicated.

Div said:
Also, about the moving order, the Spirit is UNSELECTABLE, it works like a "locust", it will just automatically do whatever Ty does... but Ty will have a subskill added whenever body and Mind triggers that allows Ty to "order the spirit" to move to a target location

Oh I see, so it's one of the caster's abilities. I see.

Div said:
What I mean is, if ty is ordered to move to a position 600 units away, the Spirit faces the same direction as Ty and moves 600 units that way...
But if that is complicated, just order the spirit to move to the point where ty is moving

This part isn't complicated, it's merely a question of what the spirit will do if the caster uses the special spirit-movement ability and then issues an order. Also, what happens if the spirit is not in range to cast a spell that is casted by the main caster.

Div said:
AND, if Ty is stunned/interrupted from his channeling of Mind Control, as long as the Spirit is still casting, the unit will remain controlled, and vice-versa

Seeing as how Mind Control is his only channeling ability this probably won't be very difficult. If you were to give an ability like the default "Blizzard" to this unit it won't prevent that from stopping channeling. In other words, this would have to be triggered into each channeling spell that you want this to happen with.

Div said:
I juts thought you could manipulate the difference of the Exp gained by the controlled unit from the point it got controlled to the end of the spell, and give it to Ty

Well yea, you could easily add any experience gained by the unit to the caster but the "killing player" would still be the original owner, and therefore he would get credit for the kill, including bounty. There is also no way to determine where bounty came from, so this would only be possible if you're triggering all of your bounty.

I'm going to look into Mind Control a little more though. I'm pretty sure that something like this has been done without impairing either of the player's control. I'll do some research.

Okay, so; are you using all 16 players? I can give the unit to Neutral Victim or Neutral Extra and then allow both users to control those units. This is only multi-player capable though, you could not have this ability twice on the same player (though the spirit could very easily have a dummy ability that simply maintains the channeling). Actually, by using the Damage Detection you may be able to "transfer" the kill to the caster of the spell by attaching that data to the affected unit. It really depends how Warcraft III would handle that kind of thread manipulation.
 
Last edited:
Actually, I can use the Damage Detection system to ensure that all of the spirit's damage is reduced by 40%, though detecting which spell it came from would be a little more complicated.


Oh I see, so it's one of the caster's abilities. I see.



This part isn't complicated, it's merely a question of what the spirit will do if the caster uses the special spirit-movement ability and then issues an order. Also, what happens if the spirit is not in range to cast a spell that is casted by the main caster.



Seeing as how Mind Control is his only channeling ability this probably won't be very difficult. If you were to give an ability like the default "Blizzard" to this unit it won't prevent that from stopping channeling. In other words, this would have to be triggered into each channeling spell that you want this to happen with.



Well yea, you could easily add any experience gained by the unit to the caster but the "killing player" would still be the original owner, and therefore he would get credit for the kill, including bounty. There is also no way to determine where bounty came from, so this would only be possible if you're triggering all of your bounty.

I'm going to look into Mind Control a little more though. I'm pretty sure that something like this has been done without impairing either of the player's control. I'll do some research.

Okay, so; are you using all 16 players? I can give the unit to Neutral Victim or Neutral Extra and then allow both users to control those units. This is only multi-player capable though, you could not have this ability twice on the same player (though the spirit could very easily have a dummy ability that simply maintains the channeling). Actually, by using the Damage Detection you may be able to "transfer" the kill to the caster of the spell by attaching that data to the affected unit. It really depends how Warcraft III would handle that kind of thread manipulation.


If the spirit is not in range, them the spirit will run to a position where he is able to cast that ability
Also, if possible, make the spirit faster than normal units

Don't worry about the "killing credits", as long as only ty gets the experience, and not the killer, that's ok

And no, I'm not using all 16 players, only 12, so that's not a problem :)
 
That's cool bro, no worries or hurries :)
Glad you're even doing this for me! haha
 
Damn, hope you get better man! Lots of water and resting should do well haha
But seriously, wish you get well soon!
 
Okay, so I've got a few questions for you regarding Body and Mind. The way I have it set up now will destroy the "mind spirit" if it tries to cast another spell when it has already reached its limit. This is to ensure that the unit isn't killed while in the middle of any spells (channeling, for example).

Second, what is supposed to happen if two of these are triggered within the same period of time? I've got it set up now such that you can have many Body and Mind spirits following your orders and stuff so if you want me to just keep it like that it's fine.

I think that's about it for now. It's coming along nicely, I've given the unit Ethereal so that it cannot attack or be attacked, but it is capable of being damaged by magic.

Alright, it still needs a little bit of work but here's a test-map.
 

Attachments

Status
Not open for further replies.
Back
Top