HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Passive Spell that Actively Works

12-04-2008, 10:44 PM#1
Zerzax
I'm hoping to create an ability that will use a passive icon to actively store corpses in the caster. What I mean is, there will be a passive spell icon displaying text, while the owner of the hero need not click any icons to facilitate the corpse storage. I want to accomplish this using only 1 icon / space. I have successfully made a spell that is actively used to store corpses, however I think it will be a burden on the hero if the user has to be constantly clicking.

I've also tried using a spellbook trick that uses raise dead to target the corpses, and is triggered such that when the maximum number of corpses has been stored, autocast is turned on, and when corpses are used, autocast is turned off. However, disabling the spellbook ability seems to also disable the raise dead ability stored within it.

The other option you may think of is of course the actual corpse cargo load ability, however it severely interferes with the hero interface and also makes it very difficult to remove the stored unit, which I want to do immediately after the spell is cast. Doing it a conventional way of ordering the unit to just drop the corpse once it's picked up requires a second icon unfortunately.

Anyone have some ideas? I'm scrambling here.
12-04-2008, 10:49 PM#2
Veev
Trigger it completely? Detect if a corpse is in range (maybe based on an order string?), remove it, increase some variable. If the maximum is reached, loop and create a skeleton and subtract the corpse count until you've used every corpse.
12-04-2008, 11:04 PM#3
Zerzax
I've already made a spell identical to that is used actively. Do you mean a passive version that just checks every once in a while and carries out the removal action? I want it to be done via casting a spell such that the hero is required to walk over to and "pick up" the corpse, which would be a pain to code. Plus I think having a timer run like that throughout the game might be expensive, though I have seen it done before. I guess the best option now is using a permanent timer.
12-05-2008, 12:30 AM#4
ShadowWolf
Base it off of cannabalism. :P
12-05-2008, 01:02 AM#5
Zerzax
That's a good idea, and it limits to one icon. But it still requires active use of the spell :(.
12-05-2008, 01:21 AM#6
Pyrogasm
GUI or JASS? This would be a bit complicated in GUI, but less complicated using JASS.

Another thing that might help is: if you disable an ability for a player, does it retain its current cooldown? Because if so, then this wouldn't be too complicated in GUI.
12-05-2008, 01:27 AM#7
Zerzax
JASS all the way. I'm pretty sure the ability and therefore its cooldown is still there if you disable it, you just can't do squat with it. It wouldn't need a cooldown or manacost though. Do you have a plan? If so, lay it out for me.
12-05-2008, 01:34 AM#8
Pyrogasm
You give the units a passive ability that simply displays the number of corpses and then an active ability based on silence that does absolutely nothing except play the animation you want to play when the unit picks up a corpse. This spell is disabled for all players at map init.
  • Periodic Timer runs every 0.15 second or so
  • Enables the 2nd ability
  • Picks every unit with your passive ability and then checks to see if there are any nearby corpses
    • If a corpse is found, the unit is ordered to use the active ability on the position of the corpse
  • The ability is disabled for all players
Then, when a unit starts the effect of your active ability, simply increase some counter variable by 1 and change the level of the passive to display the current number of corpses.

I'll write up some JASS in a second that does this.

Collapse JASS:
scope CorpseSpell initializer Init
    globals
        private constant integer PASSIVE_ID = 'A000'
        private constant integer ACTIVE_ID = 'A001'
        private constant real TIMER_INTERVAL = 0.15
        private constant real SEARCH_RADIUS = 200.00
        private constant string ORDER_STRING = "silence"

        private timer T = null
        private trigger SpellCast = null
        private boolexpr B = null
        private boolexpr B2 = null
        private group G = null
        private group G2 = null
    endglobals

    private function HasPassive takes nothing returns boolean
        return GetUnitAbilityLevel(GetFilterUnit(), PASSIVE_ID) > 0
    endfunction

    private function IsCorpse takes nothing returns boolean
        return GetWidgetLife(U) < 0.406
    endfunction

    private function Periodic takes nothing returns nothing
        local integer J = 0
        local unit U
        local unit U2
        local unit C
        local real CX
        local real CY
        local real Dist = SEARCH_RADIUS*SEARCH_RADIUS+32.00
        local integer O

        local real U2D
        local real U2X
        local real U2Y
        local real X
        local real Y

        loop
            call SetPlayerAbilityAvailable(Player(J), ACTIVE_ID, true)
            set J = J+1
            exitwhen J >= 12
        endloop

        call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea, B)
        loop
            set U = FirstOfGroup(G)
            exitwhen U == null

            set X = GetUnitX(U)
            set Y = GetUnitY(U)
            call GroupEnumUnitsInRange(G2, X, Y, SEARCH_RADIUS, B2)

            loop
                set U2 = FirstOfGroup(G)
                exitwhen U2 == null

                set U2X = GetUnitX(U)
                set U2Y = GetUnitY(U)
                set U2D = (U2X-X)*(U2X-X) + (U2Y-Y)*(U2Y-U)

                if U2D < Dist then
                    set C = U2
                    set Dist = U2D
                    set CX = U2X
                    set CY = U2Y
                endif
            endloop

            if C != null then
                set O = GetUnitCurrentOrder(U)
                if O == Order("smart") or O == Order("stop") then
                    call IssueTargetOrder(U, ORDER_STRING, CX, CY)
                endif

                set C == null
            endif
        endloop

        set J = 0
        loop
            call SetPlayerAbilityAvailable(Player(J), ACTIVE_ID, false)
            set J = J+1
            exitwhen J >= 12
        endloop
    endfunction

    private function ActiveCastConditions takes nothing returns boolean
        return GetSpellAbilityId() == ACTIVE_ID
    endfunction

    private function ActiveCast takes nothing returns nothing
        //Do your stuff here
    endfunction

    private function Init takes nothing returns nothing
        local integer J = 0
        loop
            call SetPlayerAbilityAvailable(Player(J), ACTIVE_ID, false)
            set J = J+1
            exitwhen J >= 12
        endloop

        set G = CreateGroup()
        set G2 = CreateGroup()
        set B = Condition(function HasPassive)
        set B2 = Condition(function IsCorpse)

        set SpellCast = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(SpellCast, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(SpellCast, Condition(function ActiveCastConditions))
        call TriggerAddAction(SpellCast, function ActiveCast)

        call TimerStart(T, TIMER_INTERVAL, true, function Periodic)
    endfunction
endscope
12-05-2008, 01:41 AM#9
Zerzax
Dude that's an awesome idea... I already have the corpse removal / variable increase coded, I just need to implement the new ability and the enabling / disabling trick. So yeah I guess it does need a constant timer... Don't sweat going through all this, I'm sure you have enough on your hands.

Oh I see. I'm rather glad you went through with it Pyro, thank you. That seems like a fairly efficient way of doing it. I was considering using an individual instanciation of a structure per creation of the hero: that is, individual timer, conditions, corpse counter, disabling / enabling for only that player. Probably gets much more expensive with more and more people using the hero...

One question before I say g'night: At what point is each unit who has the spell and available space, ordered to cast silence? In the Periodic?
12-05-2008, 02:21 AM#10
Pyrogasm
I hadn't finished when I saved that edit. Now it's finished.

I'm not entirely sure if the enabling/disabling thing will work, though.
12-05-2008, 05:15 PM#11
Zerzax
I read through the script Pyro, and I definitely get the gist of it... Though I have yet to test it and see whether the spell can fire in time before the disable. I can see how using those locals is a useful way to find the dead unit, though I usually use a static group, boolexpr, and simply sort through each unit in the boolexpr, and check it with static constants that I set right before the enum. Then instead of comparison of squares you can use IsUnitInRangeXY though that is a function call. I'm not really sure which is more efficient. Thank you for writing the script.
12-05-2008, 11:37 PM#12
Pyrogasm
I don't honestly know how IsUnitInRangeXY() compares to GetUnitX() + GetUnitY() + Math + if-comparison.

My guess is that it's probably better :P So yeah, use that.
12-06-2008, 12:30 AM#13
Bobo_The_Kodo
And it takes their collision size into account
12-07-2008, 10:32 PM#14
Zerzax
I've made my decision: The caster will not go to the dead units; they will come to him :D. I'll say no more because I want it a secret until the Hero Contest Poll.