• 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.

Posts moved from FP's tutorial thingy

Status
Not open for further replies.
Level 10
Joined
Aug 19, 2008
Messages
491
What's with the PauseUnitBJ? PauseUnit does the exact same thing, with the parameter order reversed. It takes a unit first, then a boolean. If there's a native version of a BJ function, call the native version, especially if the BJ function is just a swapped copy of the native version. It's faster that way.
Well let me quote Flame_Phoenix from his tutorial:

BJ variables are NOT evil as many people think, they are actually faster. Only some BJ functions are evil

I didn't think that PauseUnitBJ was a function so I added it instead of PauseUnit.
Does it leak or what?

- - - - -

As for the code, I will give it another look tomorrow, I can't do it right now.
Don't worry, I have patience. If you want I could wait a week, take your time.

And as for making them constants, I did it.
But now it's buggy, it refuse to target some of the enemies.
It normally stops all of them, but now it only stops one, or at most two :sad:
Is this news to you?
 
Last edited:
Level 4
Joined
Mar 14, 2009
Messages
98
I've never said BJ functions were evil. I said they were slower. And he's talking about bj variables, those global variables that have bj_ at the start, like bj_wantDestroyGroup or bj_groupAddGroupDest. BJ variables are like pre-made, blizzard-defined global variables.

BJ functions, on the other hand, are slower than their native counterparts, although it shouldn't really matter much. If you're into optimization, you'd want to avoid BJ functions. Here's an example:

This is the PauseUnit function, it's a native.
JASS:
native          PauseUnit           takes unit whichUnit, boolean flag returns nothing

This is the PauseUnitBJ function, it's not a native.
JASS:
function PauseUnitBJ takes boolean pause, unit whichUnit returns nothing
    call PauseUnit(whichUnit, pause)
endfunction

The difference? One function call and swapping parameter positions. It's small, but it adds up eventually if you're doing stuff every 0.1 seconds. I still use some of them sometimes, because some of them are more than just swapped parameter positions and an extra function call.

Example:
JASS:
function IsUnitGroupEmptyBJ takes group g returns boolean
    // If the user wants the group destroyed, remember that fact and clear
    // the flag, in case it is used again in the callback.
    local boolean wantDestroy = bj_wantDestroyGroup
    set bj_wantDestroyGroup = false

    set bj_isUnitGroupEmptyResult = true
    call ForGroup(g, function IsUnitGroupEmptyBJEnum)

    // If the user wants the group destroyed, do so now.
    if (wantDestroy) then
        call DestroyGroup(g)
    endif
    return bj_isUnitGroupEmptyResult
endfunction

On a final note, the only way to know if a BJ is more than a swapped version of a native is to look it up in a function list.
 
As to your problem, don't worry, it won't bug if you don't put the "= CreateGroup()" at the start. Let's look at the code for proof:
As I said, just because it works it doesn't mean you should do it. I am not encouraging bad coding practices specially when he doesn't fully understand such concepts (of handles and pointers) yet.

Edit: On a side note, I believe that range is radius, not diameter. I could be wrong though, but I'm pretty sure it's right.
You are wrong, it is diameter afaik.

I didn't think that PauseUnitBJ was a function so I added it instead of PauseUnit.
Does it leak or what?
Note, BJ variables are like bj_PI or something like that. PauseUnitBJ is a function. It doesn't leak, but it is slower. You should use PauseUnitBJ. When using TESH usually red functions are BJ's (therefore you must be careful with them), and purple functions are natives.
I've never said BJ functions were evil. I said they were slower.
Sometimes using a BJ function is good because it deeply simplifies the code. Some multiboard BJ's and well as the TriggerRegisterEventBJ make part of BJ's that are not stupid to use IMO.
As for the code:
Please remove "set trg = null". Trigger variables are pointers. In this case a pointer is simply a number (a big integer) that points to a position of memory. You don't need to null it.

JASS:
    private function NoTarget takes nothing returns boolean
        return true
        //If you check this as 'false', it means that your spell targets an area on the ground,
        //and not around the caster as it was originally designed.
        //Remember to change Range so it coresponds to the spell's!
    endfunction
Good idea, but this function is useless. Note that you can replace this function with a "private constant boolean NO_TARGET = true" variable.
However, if you wish to use a function, then you should pass it a parameter level. This way the user can have ThunderClap like spell in level 1, and can have a target area spell in level 2. However I find this situations quite uncommon, so I advise you to change it into a constant variable. Btw, why constant? (can you tell me?)

Also
JASS:
 if ( NoTarget() == true ) then
            call GroupEnumUnitsInRange( all, casterX, casterY, Range( level ), b )
        else
            call GroupEnumUnitsInRange( all, spellX, spellY, Range( level ), b )
        endif
is equal to:
JASS:
 if  NoTarget() then //if true then
            call GroupEnumUnitsInRange( all, casterX, casterY, Range( level ), b )
        else
            call GroupEnumUnitsInRange( all, spellX, spellY, Range( level ), b )
        endif
And this second version is faster. This also applies to variables. Note that the if statement operates under a boolean value, so you don't need to compare when you already have boolean values.
Case when you need to compare:
variableNumber > 4
Case when you don't need to compare:
true == true //if your functions NoTarget() is true, this is what the game sees. There is no need for it. Just make it "true".

The loop:
JASS:
  loop
            set f = FirstOfGroup( all )
            set unitX = GetUnitX( f )
            set unitY = GetUnitY( f )
            exitwhen f == null
            call PauseUnitBJ( true, f )
            call DestroyEffect( AddSpecialEffect( PAUSE_EFFECT, unitX, unitY) )
            call GroupRemoveUnit( all, f )
        endloop
Should be:

JASS:
  loop
            set f = FirstOfGroup( all )
            set unitX = GetUnitX( f )
            set unitY = GetUnitY( f )
            exitwhen f == null
            call GroupRemoveUnit( all, f )
            call PauseUnit(f, true)
            call DestroyEffect( AddSpecialEffect( PAUSE_EFFECT, unitX, unitY) )
        endloop
Note that I replaced the BJ function with a native and that I moved the GroupRemoveUnit to top. I did this to avoid hard bugs, bugs that exist when a unit is not removed (by some unknown reason). This way the unit is removed from the group and you can still work on it. In this simple case, hard bugs wouldn't be a problem, but in more complex spells, this is a life saver.
Also, do the same for you unpause loop.

Other than these, I think the spell will be Ok.
Also Peetoon is doing a good job, he deserves rep++.
 
Level 4
Joined
Mar 14, 2009
Messages
98
As I said, just because it works it doesn't mean you should do it. I am not encouraging bad coding practices specially when he doesn't fully understand such concepts (of handles and pointers) yet.
Neither am I. I'm just saying that creating a group that you'll never use is pretty bad coding practice, and it leaks a group. By the way, leaking a group and leaking a group variable are two different things. Leaking a group is when a group is no longer accessible because no group variables are pointing to it, so you have no possibility of destroying the offending group. Leaking a group variable is when a group variable isn't nulled at the end of a function.

Please remove "set trg = null". Trigger variables are pointers. In this case a pointer is simply a number (a big integer) that points to a position of memory. You don't need to null it.
Don't remove it. You will have to null everything that is a handle. The reason why most people don't null the default gg_trg_Untitled_Trigger_001 or whatever the trigger is named is because those are globals. Globals don't have to be nulled, but what they point to should be destroyed if they aren't going to be used anymore. That's because you can always use a global from anywhere, and even if they are nulled the space they take up will never be recycled so it shouldn't matter if you null them or not (unless you put a condition like GlobalVariable1==null). Local handle variables are lost forever, unless they are nulled so the game can recycle the memory used by the variable. Destroying/Removing what they point to doesn't remove the handle variable leaks as they're still pointing to something.

Again, I'll try to explain what handles are. There are six basic variable types in warcraft. They are integer, real, boolean, string, handle, and code. I missed code in my earlier post. Don't ask me about code though, I have no idea how to use it. So you ask where the units are and the items and the other cool stuff? They're all extended by handle. That means all units are handles, all groups are handles, all triggers are handles, etc. Again, as I've said before, the general rule is null everything that's a handle, unless it's a global.

To demonstrate everything I've said, copy this trigger into a test map, then run it.
JASS:
function H2I takes handle H returns integer
    return H
    return 0
endfunction

function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
    local group Temp = CreateGroup()
    call BJDebugMsg("Group exists: "+I2S(H2I(Temp)))
    call DestroyGroup(Temp)
    call BJDebugMsg("Group destroyed: "+I2S(H2I(Temp)))
    set Temp = null
    call BJDebugMsg("Group nulled: "+I2S(H2I(Temp)))
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerRegisterPlayerEventEndCinematic( gg_trg_Untitled_Trigger_001, Player(0) )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
Keep pressing escape, and you should see the handle number of the created groups. Notice how even after destroying the group the handle number on the variable stays the same. And then it becomes 0 after the variable is nulled. Also, note that the handle numbers doesn't keep going up. After trying that, delete the line "set Temp = null". Then try it. You should notice the handle number keeps going up. That's because the game isn't recycling the memory it used for the Temp variable. You can also try not destroying the group, but nulling the variable. The same thing should happen, handle count is going up. That's leaking in action. And all those unused handles will eventually cause your game to lag. You can also try to change what it creates. Make it create triggers or locations or units or whatever you want, just change the variable type and how to create the thing. CreateGroup doesn't really work well with location variables. The example uses the popular return bug, just so you know.
 
Last edited:
Don't remove it.
Ignore this.
I already explained why ... if you don't believe me I will be forced to make a post at wc3c to prove I am right.

missed code in my earlier post. Don't ask me about code though, I have no idea how to use it.
Basically when you have a function that takes another function as an argument (for example).
all triggers are handles
I am not sure about this...
Again, as I've said before, the general rule is null everything that's a handle, unless it's a global.
Globals can also and SHOULD also be nulled if you don't use them. GUI is a master piece of this example, read some Daelin tutorials, you will see how he manages globals for group picking units in spells.

To demonstrate everything I've said, copy this trigger into a test map, then run it.
Ahhh the infamous H2I bug ...
 
Level 4
Joined
Mar 14, 2009
Messages
98
I suggest you try the demo trigger as well, change the variable to trigger, and then correct me after. Also, nulling globals has no point unless you're going to use them in a condition to compare them to null, warcraft isn't going to put the memory back into the heap anyway.

P.S. Thanks for telling me what code is good for. Personally, I still can't find a good use for it. Well, adding actions to a trigger is useful, or filters for boolexprs. But I can't think of any other use for them.
 
I suggest you try the demo trigger as well, change the variable to trigger, and then correct me after. Also, nulling globals has no point unless you're going to use them in a condition to compare them to null, warcraft isn't going to put the memory back into the heap anyway.
I will make a thread in wc3c and come with an answers soon.
P.S. Thanks for telling me what code is good for. Personally, I still can't find a good use for it. Well, adding actions to a trigger is useful, or filters for boolexprs. But I can't think of any other use for them.
Well, see this system for an example of the use of functions.
Chain Lightning System - The Hive Workshop - A Warcraft III Modding Site
 
Level 10
Joined
Aug 19, 2008
Messages
491
Wow I can't keep track on this anymore, so I'll answer what I saw.

1: Flame_Phoenix, you told me to replace the function NoTarget with a private constant boolean NO_TARGET.
I tried that but the spell got buggy as hell, sometimes it only targets one or two units :confused:
2: I already gave +rep to Peetoon, I can't do it again in some time :sad:
3: Ok I'll try to change most of the functions to the ones you described, Flame.
 
Level 4
Joined
Mar 14, 2009
Messages
98
Oh...right. My test scenario was using dynamic triggers, so nulling them was important. Then both of us were wrong in the matter, and yet both of us were partly right. On a side note, I've always thought that handles took more than 4 bytes. I wonder where Vexorian gets this stuff.

Edit:
@Cheezeman: You don't need to null it because you won't destroy it anyway. Sorry about that. And you might as well null globals if you want to be neat. I just realized that we were arguing about microseconds and 4 bytes of data. XD

Edit2: I was wrong about globals not having to be nulled. Like I said before, there are two types of leaks. I was thinking too one-sidedly and thought that global variables don't leak because they don't get recycled, but the "ghost" that remains at what they were pointing at does get recycled. By not nulling the global, the game thinks that the pointed-at-thing still exists, hence it isn't recycled. So it leaks the "ghost" of what it pointed at, making the handle count go up. Sorry about that. Long story short, you have to null globals.
 
Last edited:
Level 10
Joined
Aug 19, 2008
Messages
491
At last, I'm back.
Sorry dude but this weekend was busy :hohum:

Anyway here's the new code with a cool special feature my good friend tomtefar1988 thought me.

The bug is still there, but it seems like I've detected the problem.
The ability isn't preloaded correctly :sad:

Here's the map: View attachment TimeStop.w3x
Here's the code:

JASS:
//===========================================================================
//==
//==                            Time Stop v1.3
//==                Created by Cheezeman @ hiveworkshop.com
//==
//==    Stops the time for all units in a certain area around the caster.
//==
//==        This spell isn't fully developed, it has some bugs and so.
//==            That's why I don't spend more time on a header
//==
//===========================================================================

scope TimeStop initializer Init
//===========================================================================
//=============================== SETUP =====================================
//===========================================================================
    globals
        private constant integer SPELL_ID = 'A003'
        //The rawcode of the blank active Hero spell.
        //To detect this you must go to object editor, select the ability and hit Ctrl + D.
        //Only the four first letters are required.
        
        private constant integer SPECIAL_DUMMY_ID = 'h003'
        //This is the rawcode of the special effect's dummy unit.
        
        private constant integer DUMMY_ID = 'h000'
        //The rawcode of the normal dummy unit.
    endglobals
    
    private function Duration takes integer level returns real
        //The duration of the spell. Remember the . at the end
        return 3. + ( 2. * level )
    endfunction
    
    private function Range takes integer level returns real
        //The range of the spell in diameter (just like normal range). Remember the . at the end
        return 150. + ( 150. * level )
    endfunction
    
    private function Targets takes unit caster, unit target returns boolean
        //These are the targets the spell will affect. For example, it won't affect dead or magic immune targets.
        return IsUnitEnemy( target, GetOwningPlayer( caster ) ) and ( GetWidgetLife( target ) > 0.405 ) and ( IsUnitType( target, UNIT_TYPE_STRUCTURE ) == false) and ( IsUnitType( target, UNIT_TYPE_HERO ) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(target, UNIT_TYPE_MECHANICAL) == false)
    endfunction
//===========================================================================
//============================= END SETUP ===================================
//===========================================================================
    globals
        private boolexpr b
        private group all
        private unit tempCaster
    endglobals
//===========================================================================
    private function Pick takes nothing returns boolean
        return Targets( tempCaster, GetFilterUnit() )
    endfunction
//===========================================================================
    //This function was made by Blade_dk. Visit [url]www.wc3c.com[/url] for more info.
    private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = CreateGroup()
        call ForGroup( g, function GroupAddGroupEnum )
        return bj_groupAddGroupDest
    endfunction
//===========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local unit caster = GetTriggerUnit()
        local player p = GetOwningPlayer( caster )
        local unit f
        local unit dummyEffect
        local integer level = GetUnitAbilityLevel( caster, SPELL_ID )
        local group copy = CreateGroup()
        local real casterX = GetUnitX( caster )
        local real casterY = GetUnitY( caster )
        set tempCaster = caster

        //The special effect above the caster
        set dummyEffect = CreateUnit( p, SPECIAL_DUMMY_ID, casterX, casterY, 0.00 )
        call UnitApplyTimedLife( dummyEffect, 'BTLF', Duration( level ) )
        call SetUnitTimeScalePercent( dummyEffect, ( 100. / Duration( level ) ) )
        call SetUnitFlyHeight( dummyEffect, 60., 90000. )

        //Set the group. There are two groups, because I'm using 2 loops and GroupRemoveUnit()
        call GroupEnumUnitsInRange( all, casterX, casterY, Range( level ), b )
        set copy = CopyGroup( all )
        
        //First loop. This one pauses all units and sets their animation speed to 0%
        loop
            set f = FirstOfGroup( all )
            exitwhen f == null
            call GroupRemoveUnit( all, f )
            call PauseUnit( f, true )
            call SetUnitTimeScalePercent( f, 0. )
        endloop
        
        //The duration of the spell
        call TriggerSleepAction( Duration( level ) )

        //Second loop. This unpauses all units in the copied group and sets their animation speed back to 100%
        loop
            set f = FirstOfGroup( copy )
            exitwhen f == null
            call GroupRemoveUnit( copy, f )
            call PauseUnit( f, false )
            call SetUnitTimeScalePercent( f, 100. )
        endloop

        //Cleanup
        call SetUnitTimeScalePercent( dummyEffect, 500. )
        call DestroyGroup( copy )
        set copy = null
        set caster = null
        set dummyEffect = null
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger trg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( trg, Condition( function Conditions ) )
        call TriggerAddAction( trg, function Actions )
        set trg = null
        
        //setting globals
        set b = Condition( function Pick )
        set all = CreateGroup()
        
        //preloading ability
        set bj_lastCreatedUnit = CreateUnit(Player( PLAYER_NEUTRAL_PASSIVE ), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility( bj_lastCreatedUnit, SPELL_ID )
        call KillUnit( bj_lastCreatedUnit )
    endfunction
endscope
 
Level 10
Joined
Aug 19, 2008
Messages
491
What are the bugs of the spell?

The first problem is that the code seems to preload incorrectly. I took the conclusion from that sometimes when I use the spell the first time, some enemy units wont get targeted.

The second problem is that the ability doesn't stack, and I can't fix it for now until I learn timers. (not stacking = the units are unpaused the original time, even though

The third problem is that sometimes when the creeps are unpaused, they instantly run back to their base. This might be caused because the units are Neutral Hostile, and they always run back to original position if they don't detect a fight.
 
The first problem is that the code seems to preload incorrectly. I took the conclusion from that sometimes when I use the spell the first time, some enemy units wont get targeted.
That didn't occurred to me at all. Are you sure of what you are saying? Also, the preloading of the ability has nothing to do with the spell bug you report ...

The second problem is that the ability doesn't stack, and I can't fix it for now until I learn timers. (not stacking = the units are unpaused the original time, even though
Actually Timers will not be your best solution if you want to fix this spell. Your best solution will be to use a dummy spell like "stun" and change the ability.

The third problem is that sometimes when the creeps are unpaused, they instantly run back to their base. This might be caused because the units are Neutral Hostile, and they always run back to original position if they don't detect a fight.
This is not a bug. If a unit gets stunned it has the same behavior, it is the hardcode from warcraft 3 and is the standard behavior for a unit.
 
Level 10
Joined
Aug 19, 2008
Messages
491
That didn't occurred to me at all. Are you sure of what you are saying? Also, the preloading of the ability has nothing to do with the spell bug you report ...
Lol now I am sure it isn't a bug anymore... doesn't occur :wink: not sure why though


Actually Timers will not be your best solution if you want to fix this spell. Your best solution will be to use a dummy spell like "stun" and change the ability.
Please tell me how, I'd really like to know how to fix that :cry:
EDIT: Read my edit!


This is not a bug. If a unit gets stunned it has the same behavior, it is the hardcode from warcraft 3 and is the standard behavior for a unit.
Well that doesn't look very realistic, which is what I want.
I think I'm gonna change the owning player to Purple instead of Hostile.

: : EDIT : :
Great news! The skill stacks now! And I can't find any leaks either!!! :grin:
Only 1 new local variable and an if/then/else statment made it work

The only thing I think I gotta do before public release is the special effect.
I wanna get rid of that extra dummy unit, hope there's a way though :hohum:
 
Last edited:
Level 10
Joined
Aug 19, 2008
Messages
491
You made the ability stack ?
Do you use stun spell like I suggested ?

No! I only used a new local variable and an if/then/else statment.

What I basically did was to use the "call SetUnitUserData()" function.
By setting their values to "Current value + 1" when I loop them the first time, and then setting it to - 1 the second time, I can check if it's 0 or not! :thumbs_up:
So if it is 0, I will unpause and do that other stuff.
If not, nothing will happen.

Hope you understood, I could post the code again.
 
Level 10
Joined
Aug 19, 2008
Messages
491
Arrghh !!! Who told you to use that !? you should NOT use that for a spell, it is suicide !!
Stop that madness at once!

Don't worry, I'm trying to find another way :hohum:
My dream is to make this 100% self sufficient, but I'm not sure if that's possible. Anyway that's why I want it gone, now I wanna hear your reason.

No, I came up with it by myself. What, does it leak?
 
Don't worry, I'm trying to find another way
My dream is to make this 100% self sufficient, but I'm not sure if that's possible. Anyway that's why I want it gone, now I wanna hear your reason.

No, I came up with it by myself. What, does it leak?
One unit can only have 1 data using that function. If as caster has 2 spells (different spells) and both use SetUnitUserData, you will have problems.
I think that creating a dummy unit and ordering it to cast "Thunder Bolt" is an easy solution. However if you really want to pause units, that will be more complicated (level 3 lesson).

You really want to evolve ... add me to your msn, I will teach you when I have time.
Please post the final code as well, I want to see it (without Data thing!)
 
Level 10
Joined
Aug 19, 2008
Messages
491
One unit can only have 1 data using that function. If as caster has 2 spells (different spells) and both use SetUnitUserData, you will have problems.
This was the exact reason I don't want it. However, I know it works now, so if there's a way to 'go around' the function using variables to get the same result, I won't have a hard time configuring it.

I think that creating a dummy unit and ordering it to cast "Thunder Bolt" is an easy solution. However if you really want to pause units, that will be more complicated (level 3 lesson).
Yes, I really want to pause them. I have several reasons for this:
1: Dummy abilities are somewhat lame and slow
2: Thunderbolting the units won't pause their abilities' cooldown, which is what I want. Stop time should really be Stop time, not Freeze units.
3: My dream is to make this self-sufficient down to the level that the only thing you have to do is copy the text into a trigger.
By adding a thunder bolt ability, I will do the exact oposite.
You really want to evolve ... add me to your msn, I will teach you when I have time.
Please post the final code as well, I want to see it (without Data thing!)
Yeah, I really do. vJass is awesome :thumbs_up:
Okay I'll add you on MSN, but I think it's best for us to try to keep discussion here aswell since I usualy open my mouth before I think and it's somewhat better to have time to write "small tutorials" and long posts.
And other people can learn from this aswell (though I'm not sure anyone will read this)
 
My dream is to make this self-sufficient down to the level that the only thing you have to do is copy the text into a trigger.
If you want to make this thing decent, you will have to use other systems made by the community. Only 1 trigger won't be an option for this spells, using Timers and Groups correctly will force you to add 2 systems, having in mind you also want to keep track of units, you will need a 3rd system.

We go easy on things ....
Post the code =P
 
Level 10
Joined
Aug 19, 2008
Messages
491
If you want to make this thing decent, you will have to use other systems made by the community. Only 1 trigger won't be an option for this spells, using Timers and Groups correctly will force you to add 2 systems, having in mind you also want to keep track of units, you will need a 3rd system.
Well, on second thought I think that if having 3 trigger ('objects', yes) will solve it I can live with that. My "issue" is that I want to release this code in public, but I want the code to be extreamly easy to understand and implent so even the n00biest n00bs can use it without effort, but at the same time have it really optimized.

We go easy on things ....
Post the code =P
What! I totally forgot that! :xxd: I guess I'm tierd from the test we had 2 hours ago...

Anyway here it is: (btw I changed the name)
JASS:
//============================================================================
//==                                                                        ==
//==                            Space Trap v1.3                             ==
//==                Created by Cheezeman @ hiveworkshop.com                 ==
//==                                                                        ==
//==    Stops the time for all units in a certain area around the caster.   ==
//==                                                                        ==
//==        This spell isn't fully developed, it has some bugs and so.      ==
//==            That's why I don't spend more time on a header              ==
//==                                                                        ==
//============================================================================

scope SpaceTrap initializer Init
//===========================================================================
//=============================== SETUP =====================================
//===========================================================================
    globals
        private constant integer SPELL_ID = 'A000'
        //The rawcode of the blank active Hero spell.
        //To detect a Rawcode you must go to object editor, select the object - in this case
        //the ability - and hit Ctrl + D.
        //Only the four first letters are required.
        
        private constant integer DUMMY_ID = 'h000'
        //The rawcode of the normal dummy unit, used for preload.
        
        private constant integer SPECIAL_DUMMY_ID = 'h003'
        //The rawcode of the special effect dummy unit.

        private constant real EFFECT_SCALE = 2.
        //Sets the scale of the special effect, were 1. is normal.
        //Don't forget the . in the end!
    endglobals
    
    private function Duration takes integer level returns real
        return 3. + ( 2. * level )
        //The duration of the spell. Remember the . at the end
    endfunction
    
    private function Range takes integer level returns real
        return 150. + ( 150. * level )
        //The range of the spell. Remember the . at the end!
    endfunction
    
    private function Targets takes unit caster, unit target returns boolean
        return ( IsUnitType( target, UNIT_TYPE_HERO ) == false ) and (IsUnitType(target, UNIT_TYPE_MECHANICAL) == false) and IsUnitEnemy( target, GetOwningPlayer( caster ) ) and ( GetWidgetLife( target ) > 0.405 ) and ( IsUnitType( target, UNIT_TYPE_STRUCTURE ) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false)
        //These are the targets the spell will affect. For example, it won't affect dead or magic immune targets.
        //Because many people might wish to change Mechanical and Hero, I've added them first.
    endfunction
//===========================================================================
//============================= END SETUP ===================================
//===========================================================================
    globals
        private boolexpr b
        private group all
        private unit tempCaster
    endglobals
//===========================================================================
    private function Pick takes nothing returns boolean
        return Targets( tempCaster, GetFilterUnit() )
    endfunction
//===========================================================================
    //This function was made by Blade_dk. Visit [url]www.wc3c.com[/url] for more info.
    private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = CreateGroup()
        call ForGroup( g, function GroupAddGroupEnum )
        return bj_groupAddGroupDest
    endfunction
//===========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local unit caster = GetTriggerUnit()
        local player p = GetOwningPlayer( caster )
        local unit f
        local unit dummyEffect
        local integer level = GetUnitAbilityLevel( caster, SPELL_ID )
        local group copy = CreateGroup()
        local real casterX = GetUnitX( caster )
        local real casterY = GetUnitY( caster )
        local integer unitData
        local real duration = Duration( level )
        set tempCaster = caster

        //Adding the special effect.
        set dummyEffect = CreateUnit( p, SPECIAL_DUMMY_ID, casterX, casterY, 0.00 )
        call SetUnitScale( dummyEffect, EFFECT_SCALE, EFFECT_SCALE, EFFECT_SCALE )
        call SetUnitFlyHeight( dummyEffect, 60., 90000. )
        call UnitApplyTimedLife( dummyEffect, 'BTLF', duration )
        call SetUnitTimeScale( dummyEffect, 1. / duration )
        
        //Set the group. There are two groups, because I'm using 2 loops and GroupRemoveUnit()
        call GroupEnumUnitsInRange( all, casterX, casterY, Range( level ), b )
        set copy = CopyGroup( all )
        
        //First loop. This one pauses all units, increases their custom value by 1
        //and sets their animation speed to 0%
        loop
            set f = FirstOfGroup( all )
            exitwhen f == null
            set unitData = GetUnitUserData( f )
            call GroupRemoveUnit( all, f )
            call SetUnitUserData( f, unitData + 1 )
            call PauseUnit( f, true )
            call SetUnitTimeScale( f, 0. )
        endloop
        
        //The duration of the spell
        call TriggerSleepAction( duration )

        //Second loop. This reduces their custom values by 1, and if the 
        //value is = 0 it will unpause it and turn the animation speed back to 100%
        //However, if it isn't 0 it means that it's in effect by another user, thus it will not unpause.
        //This method is used so the spell can stack.
        loop
            set f = FirstOfGroup( copy )
            exitwhen f == null
            set unitData = GetUnitUserData( f ) - 1
            call GroupRemoveUnit( copy, f )
            call SetUnitUserData( f, unitData )
            if unitData == 0 then
                call PauseUnit( f, false )
                call SetUnitTimeScale( f, 1. )
            endif
        endloop

        //Cleanup
        call SetUnitTimeScale( dummyEffect, 5. )
        call DestroyGroup( copy )
        set copy = null
        set caster = null
        set dummyEffect = null
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger trg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( trg, Condition( function Conditions ) )
        call TriggerAddAction( trg, function Actions )
        set trg = null
        
        //setting globals
        set b = Condition( function Pick )
        set all = CreateGroup()
        
        //preloading ability
        set bj_lastCreatedUnit = CreateUnit(Player( PLAYER_NEUTRAL_PASSIVE ), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility( bj_lastCreatedUnit, SPELL_ID )
        call KillUnit( bj_lastCreatedUnit )
    endfunction
endscope
 
Well, on second thought I think that if having 3 trigger ('objects', yes) will solve it I can live with that. My "issue" is that I want to release this code in public, but I want the code to be extreamly easy to understand and implent so even the n00biest n00bs can use it without effort, but at the same time have it really optimized.
Good quality vJass spells cannot be modified by newbs. Spells this advanced will require knowledge from the user to modify them.
However, that is why you have a SETUP section. In Setup section all is easy to change, and that is the only place of your code users should change.

I will look into the code when I have some time, and for God sake, please REMOVE "set trg = null".

Why? Well see this:
http://www.wc3c.net/showthread.php?t=105673
 
Level 10
Joined
Aug 19, 2008
Messages
491
I will look into the code when I have some time, and for God sake, please REMOVE "set trg = null".

Why? Well see this:
Should I null triggers? - Wc3campaigns

I read his post and interpreted it as I should set trg = null :hohum:
Well aparently I misread "First of all, always null handle local variables when they reach the end of their 'scope', always."
Anyway ok, I will remove it.

Oh and, when it's ready for public release, I'll be sure to modify the SETUP section with a lot of comments. But for now, because I keep changing it, I'll leave it as it is.


"student? uh oh."
LOL! xD
 
Level 10
Joined
Aug 19, 2008
Messages
491
So, here is one reason. If you take time to read to the end of the post, you'll see more people explaining the same reason. Basically, when you don't destroy a handle local, you don't need to null it.

So I should null triggers when I destroy it? (Like in initialization triggers)
JASS:
call DestroyTrigger( trg )
set trg = null
I know this means I can't use it again :p
 
So I should null triggers when I destroy it? (Like in initialization triggers)
call DestroyTrigger( trg )
set trg = null
I know this means I can't use it again :p
Lol... if you destroy a trigger your spells will not run. A spell runs because it fires an active trigger. If the trigger is dead nothing happens.
Mainly, you can never kill that local trigger. That local trigger is permanent, i just make it local for security reasons, but it could also be a global trigger.
See it this way: that local trigger is like my Chuck Norris unit in the post. It never dies (because you always need it), so you don't need to null it.

Now, see your copy group example. Do you need it always ?
No, in the end of the spell, the group is useless, so you destroy it. Because you can destroy it, you also null it. This is also valid for any other handle variables you have: if it can die, it should be nulled.
 
Level 10
Joined
Aug 19, 2008
Messages
491
Good news Flame_Poenix, I solved the problem with the help from the dudes on wc3c.net. :thumbs_up:
It works perfectly for a small map like mine, though I'm not sure how it will work for huge-ass maps like TBR or something that uses a lot of units.
You'll see why.

Anyway here it is:
JASS:
//============================================================================
//==                                                                        ==
//==                            Time Stop v1.5                              ==
//==                Created by Cheezeman @ hiveworkshop.com                 ==
//==                                                                        ==
//==    Stops the time for all units in a certain area around the caster.   ==
//==                                                                        ==
//==        This spell isn't fully developed, it has some bugs and so.      ==
//==            That's why I don't spend more time on a header              ==
//==                                                                        ==
//============================================================================

scope SpaceTrap initializer Init
//===========================================================================
//=============================== SETUP =====================================
//===========================================================================
    globals
        private constant integer SPELL_ID = 'A000'
        //The rawcode of the blank active Hero spell.
        //To detect a Rawcode you must go to object editor, select the object - in this case
        //the ability - and hit Ctrl + D.
        //Only the four first letters are required.
        
        private constant integer DUMMY_ID = 'h000'
        //The rawcode of the normal dummy unit, used for preload.
        
        private constant integer SPECIAL_DUMMY_ID = 'h003'
        //The rawcode of the special effect dummy unit.

        private constant real EFFECT_SCALE = 2.
        //Sets the scale of the special effect, were 1. is 100%
        //Don't forget the . in the end!
        
        private constant real FLY_HEIGHT = 60.
        //This is the height of the special effect.
    endglobals
    
    private function Duration takes integer level returns real
        return 3. + ( 2. * level )
        //The duration of the spell. Remember the . at the end
    endfunction
    
    private function Range takes integer level returns real
        return 150. + ( 150. * level )
        //The range of the spell. Remember the . at the end!
    endfunction
    
    private function Targets takes unit caster, unit target returns boolean
        return ( IsUnitType( target, UNIT_TYPE_HERO ) == false ) and (IsUnitType(target, UNIT_TYPE_MECHANICAL) == false) and IsUnitEnemy( target, GetOwningPlayer( caster ) ) and ( GetWidgetLife( target ) > 0.405 ) and ( IsUnitType( target, UNIT_TYPE_STRUCTURE ) == false) and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false)
        //These are the targets the spell will affect. For example, it won't affect dead or magic immune targets.
        //Because many people might wish to change Mechanical and Hero, I've added them first.
    endfunction
//===========================================================================
//============================= END SETUP ===================================
//===========================================================================
    globals
        private boolexpr b
        private group all
        private unit tempCaster
        private integer array UnitIdNumber[24573] //Extends the array to 3x its original limit
    endglobals
//===========================================================================
    private function Pick takes nothing returns boolean
        return Targets( tempCaster, GetFilterUnit() )
    endfunction
//===========================================================================
    //This function was made by grim001. Visit [url=http://www.wc3c.net]Welcome to Wc3Campaigns[/url] for more info.
    function H2I takes handle h returns integer
        return h
        return 0
    endfunction

    function GetUnitIndex takes unit u returns integer
        return H2I(u) - 0x100000
    endfunction
//===========================================================================
    //This function was made by Blade_dk. Visit [url]www.wc3c.com[/url] for more info.
    private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = CreateGroup()
        call ForGroup( g, function GroupAddGroupEnum )
        return bj_groupAddGroupDest
    endfunction
//===========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
//===========================================================================
    private function Actions takes nothing returns nothing
        local unit caster = GetTriggerUnit()
        local player p = GetOwningPlayer( caster )
        local unit f
        local unit dummyEffect
        local integer level = GetUnitAbilityLevel( caster, SPELL_ID )
        local group copy = CreateGroup()
        local real casterX = GetUnitX( caster )
        local real casterY = GetUnitY( caster )
        local integer unitData
        local real duration = Duration( level )
        set tempCaster = caster
        
        //Adding the special effect.
        set dummyEffect = CreateUnit( p, SPECIAL_DUMMY_ID, casterX, casterY, 0.00 )
        call SetUnitScale( dummyEffect, EFFECT_SCALE, EFFECT_SCALE, EFFECT_SCALE )
        call SetUnitFlyHeight( dummyEffect, GetUnitFlyHeight( caster ) + FLY_HEIGHT, 90000. )
        call UnitApplyTimedLife( dummyEffect, 'BTLF', duration )
        call SetUnitTimeScale( dummyEffect, 1. / duration )
        
        //Set the group. There are two groups, because I'm using 2 loops and GroupRemoveUnit()
        call GroupEnumUnitsInRange( all, casterX, casterY, Range( level ), b )
        set copy = CopyGroup( all )
        
        //First loop. This one pauses all units, increases their integer value by 1
        //and sets their animation speed to 0%
        loop
            set f = FirstOfGroup( all )
            set unitData = GetUnitIndex( f )
            exitwhen f == null
            set UnitIdNumber[unitData] = UnitIdNumber[unitData] + 1
            call GroupRemoveUnit( all, f )
            call PauseUnit( f, true )
            call SetUnitTimeScale( f, 0. )
        endloop
        
        //The duration of the spell
        call TriggerSleepAction( duration )

        //Second loop. This reduces their integer values by 1, and if the 
        //value is = 0 it will unpause it and turn the animation speed back to 100%
        //However, if it isn't 0 it means that it's in effect by another user, thus it will not unpause.
        //This method is used so the spell can stack.
        loop
            set f = FirstOfGroup( copy )
            exitwhen f == null
            set unitData = GetUnitIndex( f )
            call GroupRemoveUnit( copy, f )
            set UnitIdNumber[unitData] = UnitIdNumber[unitData] - 1
            if UnitIdNumber[unitData] == 0 then
                call PauseUnit( f, false )
                call SetUnitTimeScale( f, 1. )
            endif
        endloop

        //Cleanup
        call SetUnitTimeScale( dummyEffect, 5. )
        call DestroyGroup( copy )
        set copy = null
        set caster = null
        set dummyEffect = null
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger trg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( trg, Condition( function Conditions ) )
        call TriggerAddAction( trg, function Actions )
        
        //setting globals
        set b = Condition( function Pick )
        set all = CreateGroup()
        
        //preloading ability
        set bj_lastCreatedUnit = CreateUnit(Player( PLAYER_NEUTRAL_PASSIVE ), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility( bj_lastCreatedUnit, SPELL_ID )
        call KillUnit( bj_lastCreatedUnit )
    endfunction
endscope
 
Level 10
Joined
Aug 19, 2008
Messages
491
Can you explain me the indexing better? It is the first time I see such a thing.
Yeah I think I understood it after some explaining from the dudes in wc3c.net.

What it does is that it takes a handle (I belive it's much like a Widget), and returns its 'id-number', or Index Number. I don't know how it returns, but it does.

However, this integer is extremely huge, so we have to shorten with that "- 0x1000000" line. (I don't know how this works either)
The returned integer is what we can call ID-number, which I then use in an Array to 'cheat' the SetUnitUserData(). It basically does the same thing, but it can be used for a lot of things and not just one number, like SetUnitUserData().
For example, you can have "set Boolean_Flag[ GetUnitIndex(u) ] = true", among other uses.

The dumb part about this is that the returned value is really huge, so I don't know if it will work when having lots of units in a map (since they all have different numbers).
I tried this with 200 units on a map and it worked fine, so there should be little problem in small-sized maps.
I gonna try to preform an experiment with it this weekend, by implenting it to an unprotected map I found that spawns and kills a whole lot of units.
Let's hold our thumbs that it works :thumbs_up:

Also, grim001 did not created H2I; Kattana did.
I'm sure he did, however, I just wanted to give credit to the dude who showed me this.

- -

On another topic, I found another bug :sad:
The special effect it uses sometimes doesn't fly up to the selected height, which is kind of wierd. I noticed that if I use it on the bridge I made, the effect's fly height is fine, but when try it on the ground I get different results (in the fly height) every time I use the spell.
Really strange...
I'm gonna see if this line will solve it when I get home:
JASS:
    private function Actions takes nothing returns nothing
        //
        //Locals and shit
        //
        set dummyEffect = CreateUnit( p, SPECIAL_DUMMY_ID, casterX, casterY, 0.00 )
        call SetUnitScale( dummyEffect, EFFECT_SCALE, EFFECT_SCALE, EFFECT_SCALE )
        call UnitApplyTimedLife( dummyEffect, 'BTLF', duration )
        call SetUnitTimeScale( dummyEffect, 1. / duration )
        //
        //Loops
        //
        call TriggerSleepAction( .01 )
        call SetUnitFlyHeight( dummyEffect, GetUnitFlyHeight( caster ) + FLY_HEIGHT, 90000. )
        //
        //The duration of the spell
        call TriggerSleepAction( duration - .01 )
    endfunction
 
Level 4
Joined
Mar 14, 2009
Messages
98
Just to clarify some things:
0x1000000 is the handle ID of the very first handle created in any game. The second would have 0x1000001, and so on. So that's why we subtract 0x1000000. I could be wrong and the first could be 0x1000001, I'm not entirely sure. Either way it's pretty close to that, so subtracting by 0x1000000 allows you to get much smaller numbers.

A widget is a subtype of handle. Subtypes of widget are unit, item, and destructable. All of them are handles. Think of it this way, imagine handle means polygon, widget means quadrilateral, unit means rhombus, and item means rectangle. All rectangles and rhombuses are quadrilaterals, and all quadrilaterals are polygons, but it doesn't work the other way around. Get it now? Yeah, it's simple when you put it in terms of elementary geometry. Or maybe that's just my inner nerd speaking.

I believe your spell would work as long as less than 24,574 handles are in the game. Massively leaking would cause your spell to fail. Then again, your spell failing would be the least of that map's problems.
 
Ahh, as I suspected... listen to me cheeze, that code can be seen as "sick" code by some people, and with a reason ...In later lessons we will try to remove that...

´I'm sure he did, however, I just wanted to give credit to the dude who showed me this.
This is the original creator of the infamous H2I bug. He even made a tutorial about it. But this technique is old, and deprecated, do NOT use it. Why? You can use H2I with no problems, but you will have problems if you have I2H. H2I gives you an unique number that identifies a handler (a unit, a group or an item per example). However this number is has some problems (as you already know) which is why I2H fails and can cause cache corruption. H2I should only be used by really vJass pros, I don't use it ... ever. vJass evolved to fix this problem (among many other things).
WC3Jass.com :: View script - Using the Handle Vars

Also, why the hell do you need this for?
JASS:
call SetUnitTimeScale( f, 0. )
You know, the unit is paused, this makes no effect at all ...
About the dummy unit problem, I didn't had that problem mmmm

After explaining this to me, I think I should teach you .. Timers =P
 
Level 4
Joined
Mar 14, 2009
Messages
98
I don't see what's wrong with H2I. It's the only way to attach things to timers, among other things. I2H should be avoided whenever possible though.(That means always.) Anything you can do with I2H can be done another way. H2I opens a whole new world of possibilities though, mostly attachment systems.

Also, Cheezeman, great solution to make it MUI without using the unit's custom value. :thumbs_up: (That was my first smiley on THW!)
 
Level 10
Joined
Aug 19, 2008
Messages
491
Also, Cheezeman, great solution to make it MUI without using the unit's custom value. :thumbs_up: (That was my first smiley on THW!)
I feel honoured! :smile:

I don't see what's wrong with H2I. It's the only way to attach things to timers, among other things.
I don't see it either, but the experienced vJassers on wc3c.net said that it might bug in large maps, so I trusted them :wink:
It's a strange feeling though :bored: I didn't really think that I could get an ID from just 2 simple functions (I belive they can be merged to 1 aswell).
I thought it would be a lot more advanced...
 
I don't see it either, but the experienced vJassers on wc3c.net said that it might bug in large maps, so I trusted them
You do well in trusting them. Specially in grim001, he has quite a reputation.
It's a strange feeling though I didn't really think that I could get an ID from just 2 simple functions (I belive they can be merged to 1 aswell).
Dota has it on 1 function if I am not mistaken, but DOta's code is considered sick...
I thought it would be a lot more advanced...
The concepts behind are very advanced, you can be sure of that. Any newb can use it, but making a good use of it is not for everyone.

And where are the answers to my questions?
 
Level 10
Joined
Aug 19, 2008
Messages
491
Ok somehow I managed to miss like 4 posts here, not sure why :bored:

Anyway, here we go... one quote at a time.
Just to clarify some things:
0x1000000 is the handle ID of the very first handle created in any game. The second would have 0x1000001, and so on. So that's why we subtract 0x1000000. I could be wrong and the first could be 0x1000001, I'm not entirely sure. Either way it's pretty close to that, so subtracting by 0x1000000 allows you to get much smaller numbers.

A widget is a subtype of handle. Subtypes of widget are unit, item, and destructable. All of them are handles. Think of it this way, imagine handle means polygon, widget means quadrilateral, unit means rhombus, and item means rectangle. All rectangles and rhombuses are quadrilaterals, and all quadrilaterals are polygons, but it doesn't work the other way around. Get it now? Yeah, it's simple when you put it in terms of elementary geometry. Or maybe that's just my inner nerd speaking.

I believe your spell would work as long as less than 24,574 handles are in the game. Massively leaking would cause your spell to fail. Then again, your spell failing would be the least of that map's problems.
Hm... So if there are more than 24 574 units/items/destructables in a map at the same time, this spell will fail?
And by removing leaks, it delete's a handle's Index number, right? So a new handle can be assigned to it.
If this is true then I don't see a problem, since a map of that scale don't - and will not - exist without proper leak removal.

Anyway, that way of decribing unit/item/destructable -> widget -> handle was nicley done. :thumbs_up:

Ahh, as I suspected... listen to me cheeze, that code can be seen as "sick" code by some people, and with a reason ...In later lessons we will try to remove that...


This is the original creator of the infamous H2I bug. He even made a tutorial about it. But this technique is old, and deprecated, do NOT use it. Why? You can use H2I with no problems, but you will have problems if you have I2H. H2I gives you an unique number that identifies a handler (a unit, a group or an item per example). However this number is has some problems (as you already know) which is why I2H fails and can cause cache corruption. H2I should only be used by really vJass pros, I don't use it ... ever. vJass evolved to fix this problem (among many other things).
WC3Jass.com :: View script - Using the Handle Vars
I never intended to use I2H, nor would I ever need to. There are more efficent ways of doing that (or I can just skip it since I don't belive I will ever need such a system).

Also, why the hell do you need this for?
JASS:
call SetUnitTimeScale( f, 0. )
You know, the unit is paused, this makes no effect at all ...
That's just a line I put there before, when experimenting with different ways of 'removing' the extra dummy unit. I just simply forgot to remove it :xxd:

About the dummy unit problem, I didn't had that problem mmmm
Try scrolling down and use it several times on Multiplayer :bored:

After explaining this to me, I think I should teach you .. Timers =P
Wow I can't wait.
I'm not even sure what they do :xxd:

The concepts behind are very advanced, you can be sure of that. Any newb can use it, but making a good use of it is not for everyone
Ah well. I could always implent that PUI some dude made on wc3.net ( = Perfect Unit Indexing ).
I think it can be a good solution, though I don't understand how it works (which really sucks cause I just don't wanna make spells, I wanna learn how they work.)
 
Level 10
Joined
Aug 19, 2008
Messages
491
So any progress on this?

I've made a decision, and that is to leave this aside.
I'm gonna publish this spell here on hive and see what the members think of it. It's already MUI and as efficient as I can make it myself.

Why?
Well I wanna move on and not stay at the same stage forever.
I'd love to see some explenations on Timers, what they are and their use.

When it's published I'll be sure to report and link it here, I've still got some work to do (better test map, setup comments, and I wish to add a buff to the units).
Wish me luck :thumbs_up:
 
Level 10
Joined
Aug 19, 2008
Messages
491
You are making a mistake ...
I was going to teach you timers and structs, but having in mind you are obviously not even interested in learning how to use GroupUtils on your own, I doubt you will be interested in learning how to use Table or TimerUtils either ...

Good luck though...

Ehm, what?
Dude, you've only told me that you're gonna teach me "simple system" "timers", and I don't know wtf you're talking about.
When did I say I don't want to learn anything at all? Or I'm not interested in learning Table and TimerUtils?
If you're refering to that site you linked (on msn), I havn't had time to read it. The last few weeks of school has been a living hell
and my computer fucked up 2 days ago, so I don't have non-stop access to internet.

Wth man? What did I ever do to deserve this?
 
Level 10
Joined
Aug 19, 2008
Messages
491
So, you don't have time to implement the system, and yet you have time to release a not high-quality version of your spell?
I don't have time to release it either :hohum: just cause my computer is down.

I still want to teach you, but I thought you wanted to learn from this spell... I guess I over-reacted, sorry xD
Yeah sure, why not? I don't have time to publish my spell anyway :sad:
 
Level 10
Joined
Aug 19, 2008
Messages
491
So... news ? xD

Well yes, I'm practicing labriaries at the moment.
So far I've only done one thing by myself which I find usefull for many purposes:
JASS:
library Functions
    function GetUnitMaxLife takes unit u returns real
        local real life = GetWidgetLife( u )
        local real lifePercent = GetUnitLifePercent( u ) / 100. 
        return life / lifePercent
    endfunction
endlibrary
You can probobly tell what it does by looking at it :hohum:

Currently I'm working on a Hero and for all the ideas I have now, my obtained knowledge will be sufficient.
I'm eagerly awaiting whatever useful information will come with session 2
 
Libraries should have "public" functions for safety.
JASS:
library Functions
    public function GetUnitMaxLife takes unit u returns real
        local real life = GetWidgetLife( u )
        local real lifePercent = GetUnitLifePercent( u ) / 100.
        return life / lifePercent
    endfunction
endlibrary

When calling, you would do:
JASS:
call Functions_GetUnitMaxLife(caster)

Why use public? Basically, if you have two functions with the same name inside different libraries, they will conflict. If you use public, they won't conflict because you type "LibraryName_FunctionName".
If you plan to release something, you should use the keyword public.
 
Level 10
Joined
Aug 19, 2008
Messages
491
Libraries should have "public" functions for safety.
JASS:
library Functions
    public function GetUnitMaxLife takes unit u returns real
        local real life = GetWidgetLife( u )
        local real lifePercent = GetUnitLifePercent( u ) / 100.
        return life / lifePercent
    endfunction
endlibrary

When calling, you would do:
JASS:
call Functions_GetUnitMaxLife(caster)

Why use public? Basically, if you have two functions with the same name inside different libraries, they will conflict. If you use public, they won't conflict because you type "LibraryName_FunctionName".
If you plan to release something, you should use the keyword public.

Ok, thanks for the advice.
 
Level 10
Joined
Aug 19, 2008
Messages
491
Ok now I've got some serious issues.
I'm trying to make a special spell. It increases the max life of a unit by a given amount. It works with a library created by Blade.dk, and it works 100%. The library that is. (FYI, the function is "SetUnitMaxState( unit, state, amount )")

It uses two triggers: one for the actual effect, one for debuging if the targeted Hero dies.
The bug is that when the Hero revives, it gains a severe bonus to health (around 2000%), and I seriously don't get why.

Here are the codes. If you're interested in the library visit SetUnitMaxState & Bonus Mod
Notes:
- Don't worry about wierd things in the libraries, they work perfect. (Proof: I used them both in my first version of the spell, but when I changed the build of the spell it gets messed up)
- This can easily be solved by not making it stack using a global boolean array, though that would suck :sad:
JASS:
scope OliphantBerry initializer Init
//===========================================================================
    globals
        private constant integer SPELL_ID = 'A04C'
        private constant integer DUMMY_ID = 'o005'
        private constant real DURATION = 10.
        private constant real OLIPHANT_BERRY_INCREASED_LIFE_PERCENT = .25
    endglobals
//===========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
//===========================================================================
    globals
        real array IncreasedLife[8]
    endglobals
//===========================================================================
    private function Actions takes nothing returns nothing
        //Locals
        local unit target = GetSpellTargetUnit()
        local player owner = GetOwningPlayer( target )
        local unitstate state = UNIT_STATE_MAX_LIFE
        local integer ownerid = GetConvertedPlayerId( owner )
        local real lifeMax = GetUnitMaxLife( target )
        local real increasedLife = OLIPHANT_BERRY_INCREASED_LIFE_PERCENT * lifeMax
        local real newLifeMax = ( 1 + OLIPHANT_BERRY_INCREASED_LIFE_PERCENT ) * lifeMax
        
        //Actions
        set IncreasedLife[ownerid] = IncreasedLife[ownerid] + (OLIPHANT_BERRY_INCREASED_LIFE_PERCENT * lifeMax)
        call SetUnitMaxState( target, state, newLifeMax )
        
        call TriggerSleepAction( DURATION )
        
        call SetUnitMaxState( target, state, GetUnitMaxLife(target) - increasedLife + 1. )
        
        //Clean and debug
        if ( not ( IsUnitDead( target ) ) ) then
            set IncreasedLife[ownerid] = IncreasedLife[ownerid] - increasedLife
        endif
        set target = null
        set owner = null
        set state = null
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( t, Condition( function Conditions ) )
        call TriggerAddAction(t, function Actions )
        
        //Preload
        set bj_lastCreatedUnit = CreateUnit(Player( PLAYER_NEUTRAL_PASSIVE ), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility( bj_lastCreatedUnit, SPELL_ID )
        call KillUnit( bj_lastCreatedUnit )
    endfunction
endscope

JASS:
scope OliphantBerryDebug initializer Init
//===========================================================================
    private function Actions takes nothing returns nothing
        local unit revived = GetTriggerUnit()
        local unitstate state = UNIT_STATE_MAX_LIFE
        local player owner = GetOwningPlayer(revived)
        local integer ownerid = GetConvertedPlayerId(owner)
        
        call SetUnitMaxState( revived, state, GetUnitMaxLife(revived) - IncreasedLife[ownerid] )
        set IncreasedLife[ownerid] = 0.
        
        set revived = null
        set state = null
        set owner = null
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_HERO_REVIVE_FINISH )
        call TriggerAddAction( t, function Actions )
    endfunction
endscope



: : EDIT : :
I also have another problem now, this time with only 1 trigger.
It's very similar to your spell, so you'll have no problem understanding it.
However, my problem is that I get an infinite loop.
The BJDebugMsg() isn't shown after the loop.

Here's the code:
JASS:
scope BombxD initializer Initialize
//***************************************************************************
//********************************** SETUP **********************************
//***************************************************************************

    globals
        private constant integer NUMBER_OF_USERS = 8
        private constant integer SPELL_ID = 'A04F'
    endglobals
    
    private function Range takes integer level returns real
        return 500. * level
    endfunction
    
    private function AllowedVictims takes unit caster, unit victim returns boolean
        return ( GetWidgetLife( victim ) < .405 ) and ( IsUnitType( victim, UNIT_TYPE_MAGIC_IMMUNE ) == true) and ( IsUnitEnemy( victim, GetOwningPlayer( caster ) ) == true ) and ( IsUnitType( victim, UNIT_TYPE_STRUCTURE ) == true ) and ( IsUnitType( victim, UNIT_TYPE_MECHANICAL ) == true )
        //Dead, not magic immune, enemy, not structure and not mechanical
    endfunction

//***************************************************************************
//******************************** END SETUP ********************************
//***************************************************************************
    globals
        private unit GlobalCaster
        private group victims
        private boolexpr victim_types
    endglobals
//***************************************************************************
    private function Pick takes nothing returns boolean
        return AllowedVictims( GlobalCaster, GetFilterUnit() )
    endfunction
//***************************************************************************
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELL_ID
    endfunction
//***************************************************************************
    private function Actions takes nothing returns nothing
        local unit caster = GetTriggerUnit()
        local player owner = GetOwningPlayer( caster )
        local location spell_loc = GetSpellTargetLoc()
        local real spellX = GetLocationX( spell_loc )
        local real spellY = GetLocationY( spell_loc )
        local integer level = GetUnitAbilityLevel( caster, SPELL_ID )
        local integer debug_int
        local unit debug_unit
        
        //wait X seconds
        set GlobalCaster = caster
        call GroupEnumUnitsInRange( victims, spellX, spellY, Range( level ), victim_types )
        
        call BJDebugMsg( "This is before the loop" )
        if victims == null then
            call BJDebugMsg( "victims == null" )
        else
            call BJDebugMsg( "victims is ok" )
        endif

        set debug_unit = FirstOfGroup( victims )

        
        loop
            set debug_unit = FirstOfGroup( victims )
            exitwhen debug_unit == null
            set debug_int = debug_int + 1
            call GroupRemoveUnit( victims, debug_unit )
        endloop
        
        call BJDebugMsg( "Number of victims == " + I2S( debug_int ) )
        call BJDebugMsg( "HEHU" )
        
        set caster = null
        set owner = null
        call RemoveLocation( spell_loc )
        set spell_loc = null
    endfunction
//***************************************************************************
    private function Initialize takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( t, Condition( function Conditions ) )
        call TriggerAddAction( t, function Actions )
        
        //Globals
        set victims = CreateGroup()
        set victim_types = Condition( function Pick )
        
    endfunction
endscope
 
Last edited:
Status
Not open for further replies.
Top