HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Web

07-24-2007, 06:11 AM#1
Av3n
Screenie:
Zoom (requires log in)

Foreword:
This is my second Jass spell made for a request. Hopefully you (users) do post good or bad comments,
because it will make me happy in knowing that people are looking at this thread.
Plus Don't talk about Dota thanks.

The Spell:
What this spell does is creates a Web when within the web you'll gain a regeneration bonus and permenant invisibility. This spell follows the JESP format.
Web Description:
Zoom (requires log in)Creates an Web which provides the caster Permenant Invisibility and regen bonus, within the web.
Level 1 - 2hp regen bonus. Lasts for 30 seconds.
Level 2 - 4hp regen bonus. Lasts for 60 seconds.
Level 3 - 6hp regen bonus. Lasts for 90 seconds.

Dota and this
The main differences between this and Dota is that this is open source. This spell does not cast permenant webs yet they create timed ones instead. This is coded with vJass and yet again it follows the JESP standard. Implementing this spell is easy for JASS and GUI users alike as there is a implemention readme to help you out, since it is JESP its even easier. And the ability the add un-limited spells through a simple setup trigger which is commented to help you out when adding some of your own abilities, this is the closest you can get for the Dota's "Web" spell.

Requries:
- JASSHelper easiest way to get this is through JASSNewGenPack

Credits:
- Vexorian for JASSHelper
- Anitarf for his pointers and how to fix stuff
- moyack, cohadar, Rising_Dusk for their help in some way

Version History

v1.00 First Release
v2.00 Second Release
CHANGELOG
- Now uses a combo of vJass, CSSafety and HandleVars
- More flexible in changing values
- Doesn't relies on an aura now to check
v2.01 Patch up
CHANGELOG
- Just changed some constants to globals
- No longer uses Polled wait
- Uses GetWidgetLife(d) <= 0.405 instead of GetUnitState(d,UNIT_STATE_LIFE) == 0
- The Timer function kills off the struct now...
v2.02 Patch up
CHANGELOG
- The Web is now more intellingent... It allows the use of double Webs lol.
- Runs on 1 timer
- No longer needs CSSafety nor HandleVars
- Credits to Anitarf for assistance!
- Purely vJASS!
- Since its a scope i removed all of the Web prefixes within the scope
0.2a
- The timer now stops
- added more comments!
v3.00
- Test period
v4.00 Final Version
- Code is more effcient
- Can use multiple webs
- Uses Collections for storing structs
- Uses bj_globals for temp stuff instead of locals!
v5.00 Update on the Final Version
- Fully indepent and no longer requires Collections
- No longer uses bj vars as temp vars, instead it uses a created global var instead
- Updated the confusing readme

Expand Web code:

-Av3n
Attached Images
File type: jpgWeb.jpg (87.8 KB)
File type: jpgBTNWeb.JPG (5.4 KB)
Attached Files
File type: w3xWeb - Demo.w3x (33.7 KB)
07-24-2007, 12:59 PM#2
moyack
I didn't have downloaded your spell, but checking the code, I think we can improve some stuff.

We can improve the constant value usage replacing those long functions with private globals. Something like this:

Collapse Improved constant definition:
globals
    private constant integer Web_id        = 'A001' //Web spell id based on Channel
    private constant integer Web_dummy     = 'e000' //Web's dummy unit
    private constant integer Web_timerbuff = 'BTLF' //Web's timer buff which is showed when clicking on the web Example:
                                                    // '|||Water Elemantal|  |' will be showed the UI when Water Elemental clicked on          
    private constant integer Web_permhide  = 'A003' //Web Permenant Invisiblity id. Also this could be any ability.
    private constant integer Web_regen     = 'A000' //Web Regeneration id. Also this could be any ability.
    private constant integer Web_looprate  = .1     //Loop rate for checking if the caster within the web
endglobals

As you can see, it takes less written space and is more readable.

You are using a loop with PolledWait, why use this when you have something more accurate which is the timer itself?? I suggest to add a conditional at the end of the timer loop code that checks if the dummy is alive.

Other thing that I noticed is that you use GetUnitState(d,UNIT_STATE_LIFE) == 0 by GetWidgetLife(d) <= 0.405, it's faster.

I'll do a complete review later, but you can do this changes meanwhile
07-25-2007, 04:44 AM#3
Av3n
I'll go and change it then and reformat the thread a little bit.

-Av3n

UPDATE: I done abit of things to the code so that. The Timer function kills off the trigger. Hopefully Im disposing the struct correctly though...
07-28-2007, 05:57 AM#4
Av3n
Bump?

-Av3n
07-28-2007, 09:25 PM#5
blu_da_noob
Steps:
Summon more than one web
Stand at a point out of range of any one (or more) of your webs, but inside another web
Watch your hero not become invisible

Bug fix :)
07-28-2007, 09:37 PM#6
Av3n
Ok, I'll fix that when I've time (Going out today... when I've have homework), I might have to do a few scaling stuff as well and such to fit the radius.
Thanks for pointing things out. Multiple Web problems? Ok thats something new...

-Av3n
07-29-2007, 11:08 AM#7
blu_da_noob
It's because each web checks if the hero is in range and removes the ability if it is not. So you're in range of one, it adds the ability, then the second web runs and sees that you aren't so it removes the ability.
08-05-2007, 04:42 AM#8
Av3n
Well BUMP! and it isn't over a week so it still ok...
But straight to the point I've fixed it thanks to Anitarf that is. There's one more bug. Im gonna check it a few more times, it says it hit the limit op or something, its happens when the web dies. So this version is very unstable. Any ideas how to fix it?

-Av3n

UPDATE: Don't worry fixed it. Just trying to pause the timer correctly now

UPDATE: All fixed!
08-07-2007, 01:33 AM#9
moyack
Ok Av3n, I've checked your spell, and...well, I think that in this moment it has too many stuff that is unnecessary to the spell purpose and it can be done in a simpler way.

Let's review the spell idea. In general you propose a spell which gives some abilities to the caster if it is in the range of the web (aka dummy unit with a kind of triggered aura.)

Now we should see how the web should behave:
  • should it affect allied units?? no, only the caster
  • should it be visible for the enemy?? yes
  • should it be invulnerable?? no
  • should it have expiration time?? yes
I put this answers according to the test that I made with your map. Because this spell affects only the caster, we can develop a struct which will define the data as follows:

Collapse JASS:
struct Web
   unit caster // it saves the unit which will be beneficed by the aura
   unit dummy // it stores the dummy unit which will center the triggered aura
endstruct

With this we can ensure that this spell will be MUI because we're linking the caster with the dummy unit. Now because this spell summons a dummy unit and gives a benefit to the caster, we should select an appropriate base spell to develop it, in this case Healing Ward would be the perfect base spell. Why?? let's see:
  1. It cast an unmovable unit. Ok
  2. it shows an AOE indicator. Great!!
  3. it has expiration time. More than perfect LOL!!!
  4. if an AI player controls the caster, the spell will be correctly used without triggering the AI. Definitely, perfect!!

Now we should make the trigger activates when the caster summon the web, so the init_trigger function should be in this way:

Collapse JASS:
function InitTrig_Web takes nothing returns nothing //Not part of the scope!
    local trigger t //Creating an local trigger creating it then nulling it. No global trigger 
    //varibles are needed
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SUMMON )
    call TriggerAddCondition(t, Condition( function Web_Conditions ) )
    call TriggerAddAction(t, function Web_Actions )
    set t = null
endfunction

The Web_Conditions function should detect if the kind of unit is the required, something like this:

Collapse JASS:
public function Conditions takes nothing returns boolean
    return GetUnitTypeId(GetSummonedUnit()) == SummonedID
endfunction

And the Web_Actions function should be in this way:

Collapse JASS:
public function Actions takes nothing returns nothing
    // all the highlighted vars are private globals
    local timer t = NewTimer()
    local Web W = Web.create()
    set W.caster = GetSummoningUnit()
    set W.dummy = GetSummonedUnit()
    call SetHandleInt(t, "Webdata", W)
    call TimerStart(t, dt, true, function webloop)
    set t = null
endfunction

now we need to know how to make the periodic evaluation. First we need that the spell detects if the caster is inside the web, if it's true, then add the abilities (if the caster doesn't have them), else, remove them (if the caster has the abilities). One suggestion in this part: I'd add one ability based on spellbook, so you spell can be customized in this part by editing the data in the dummy ability. Doing this in that way, we only have to check if the caster has the spellbook ability only.

Now let's see how the loop code will look.

Collapse JASS:
// the hightlited parts are private globals to add
function webloop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local Web W = Web( GetHandleInt(t, "Webdata") )
    if IsUnitInRange(W.caster, W.dummy, AOE) and GetUnitAbilityLevel(W.caster, SpellBookAbil) < 1 then
        call UnitAddAbility(W.caster, SpellBookAbil)
    endif
    if not IsUnitInRange(W.caster, W.dummy, AOE) and GetUnitAbilityLevel(W.caster, SpellBookAbil) > 0 then
        call UnitRemoveAbility(W.caster, SpellBookAbil)
    endif
    if GetWidgetLife(W.dummy) < 0.405 then
        // kills the timer loop
        call FlushHandleLocals(t)
        call ReleaseTimer(t)
        call W.destroy()
    endif
    set t = null
endfunction

As you can see, this spell is very efficient, it uses few functions and achieves the same result. I'm afraid that you were too worried for specific stuff and suddenly you lost the general idea. Remember that doing an efficient spell is not only coding, it implies to have clear the idea of your spell, how configurable should it be, and how it should look in game. Remember this: if your spell is too long, is because it's becoming into a system or in something unnecessarily complex and buggy.

I hope it can help you to improve this spell and other spells in this activity.
08-07-2007, 05:41 AM#10
Av3n
I got a feeling that the same problem is gonig to happen again with multiple webs, so I'll have to use a global web var. Anitarf is doing the exact opposite to your idea having it only use 1 timer and a list of web's summoned etc. But I do agree about using a AoE indicator ability ,using spellbooks and about long code. But if I was to do it this way, I'll have to do it someway that I can access all the web's the caster summonned (Using HandleVars) and check them if the caster is within the web etc.

-Av3n
08-07-2007, 08:09 AM#11
cohadar
I suggest you use ABC system for your web.
Now this suggestion is probably seen as biased,
but think of this:

You are using loopings in global arrays to support your web structs.
That is exactly what ABC does, and instead of using those loop1
and loop2 that garble your code you could simply
use SetStructA, GetStructA, ClearStructA.

Try rewriting your code with ABC and you will realize that it is
simpler, more readable and easier that way.

Just look at the slide spell inside ABC demo map,
and you will get the idea really fast.
08-07-2007, 09:19 PM#12
moyack
Some corrections from my previous post:

Collapse loop function:
private function webloop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local Web W = Web( GetHandleInt(t, "Webdata") )
    if IsUnitInRange(W.caster, W.dummy, AOE) and GetUnitAbilityLevel(W.caster, SpellBookAbil) < 1 then
        call UnitAddAbility(W.caster, SpellBookAbil)
    endif
    if not IsUnitInRange(W.caster, W.dummy, AOE) and GetUnitAbilityLevel(W.caster, SpellBookAbil) > 0 then
        call UnitRemoveAbility(W.caster, SpellBookAbil)
    endif
    if GetWidgetLife(W.dummy) < 0.405 then
        // kills the timer loop
        if GetUnitAbilityLevel(W.caster, SpellBookAbil) > 0 then
         call UnitRemoveAbility(W.caster, SpellBookAbil)
    endif
        call FlushHandleLocals(t)
        call ReleaseTimer(t)
        call W.destroy()
    endif
    set t = null
endfunction
Collapse init_trigger function:
function InitTrig_WebMoyack takes nothing returns nothing
    local trigger t = CreateTrigger()//Creating an local trigger creating it then nulling it. No global trigger 
    //varibles are needed
    local integer i = 0
    loop
        // Disables to all players the spell container ability
        // so it can't display in the command card.
        exitwhen i > 15
        call SetPlayerAbilityAvailable(Player(i), SpellBookAbil, false)
        set i = i + 1
    endloop
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SUMMON )
    call TriggerAddCondition(t, Condition( function WebM_Conditions ) )
    call TriggerAddAction(t, function WebM_Actions )
    set t = null
endfunction

Quote:
Originally Posted by Av3n
I got a feeling that the same problem is gonig to happen again with multiple webs, so I'll have to use a global web var. Anitarf is doing the exact opposite to your idea having it only use 1 timer and a list of web's summoned etc. But I do agree about using a AoE indicator ability ,using spellbooks and about long code. But if I was to do it this way, I'll have to do it someway that I can access all the web's the caster summonned (Using HandleVars) and check them if the caster is within the web etc.

-Av3n
That bug won't happen with my version, because the struct is used to connect the caster with the web, and if the caster casts 10 webs in tandem, it will have a tunnel of webs which will make it invisible in the line. Check my PM for further information.

Please use all that can help you, and to make things clear, it is not an obligation to follow my comments. If you consider that your spell is right as is, there won't be any problem with me. If you need any help, I'm gladly help you as best as possible.
08-08-2007, 02:44 AM#13
Av3n
I do appreciate your comment guys. I'll try update it with shorter code like moyack suggested. I want to try moyack's way first. If it does work I'll adjust my spell using moyack's way. It will be ready hopefully by sunday.

-Av3n
08-09-2007, 06:39 PM#14
cohadar
Well it will work, his idea is good.

It uses one timer per web, and attaches spelldata to that timer,
it can't get more MUI than that.

But it could be somewhat inneficient if there are lots of units that cast web,
because if there are 100 webs there will be 100 timers with .1 period.
But I doubt anyone wants to make a map with 100 webs :D

@moyack:
Quote:
if GetWidgetLife(W.dummy) < 0.405 then

Where did you get that 0.405 constant ?
08-09-2007, 08:51 PM#15
moyack
Quote:
Originally Posted by cohadar
@moyack:


Where did you get that 0.405 constant ?
this is something like common knowledge, which was obtained by testing. It seems that this value is the limit between a living unit or a dead unit.