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

[JASS] My first vJASS Spell...

Status
Not open for further replies.
Hi guys, Im experimenting a spell made from vJASS without using locals...
I know that this is not perfect but it's working, all I need is a VERY SIMPLE improvement of this spell without using locals...

Description:
Drops a huge rock on top of the target enemy unit while being snared.

Level 1 - Snares for 5 seconds, 50 Rock damage.
Level 2 - Snares for 10 seconds, 100 Rock damage.
Level 3 - Snares for 15 seconds, 150 Rock damage.
Level 4 - Snares for 20 seconds, 200 Rock damage.
Level 5 - Snares for 25 seconds, 250 Rock damage.

EDIT: Thanks to Berb
JASS:
scope SnareBoom
 
globals
   private hashtable HASH = InitHashtable()
   private timer TIMERDIES
   private unit Caster
   private unit Target
   private unit Dummy
   private integer SPELL_ID = 'A000'
   private integer ID   
   private integer LEVEL
   private real DAMAGE
   private integer CASTERU          = StringHash("Caster")
   private integer TARGETU          = StringHash("Target")
   private integer DAMAGER          = StringHash("Damage")
   //=================CONFIGURATIONS==================//
   private attacktype ATTYPE        = ATTACK_TYPE_SIEGE
   private damagetype DAMTYPE       = DAMAGE_TYPE_NORMAL
   private real DAMAGEBASE          = 50
   private real TIME                = 1.0
   private string SFX               = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
endglobals
 
private function SnareBCondition takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction
 
private function RockDies takes nothing returns nothing
    set TIMERDIES = GetExpiredTimer()
    set ID = GetHandleId(TIMERDIES)
    set Caster = LoadUnitHandle(HASH, CASTERU, ID)    
    set Target = LoadUnitHandle(HASH, TARGETU, ID)
    set DAMAGE = LoadReal(HASH, DAMAGER, ID)
    call DestroyEffect(AddSpecialEffect(SFX, GetUnitX(Target), GetUnitY(Target)))
    call UnitDamageTarget(Caster, Target, DAMAGE, false, false, ATTYPE, DAMTYPE, null)
endfunction
 
private function SnareBAction takes nothing returns nothing
    set Caster = GetTriggerUnit()
    set Target = GetSpellTargetUnit()
    set Dummy = CreateUnit(GetOwningPlayer(Caster), 'h000', GetUnitX(Target), GetUnitY(Target), 0)
    set TIMERDIES = CreateTimer()
    set ID = GetHandleId(TIMERDIES)
    call SetUnitFlyHeight(Dummy, 10.00, 1000.00 )
    call UnitApplyTimedLife(Dummy , 'BTLF', TIME )
    set LEVEL = GetUnitAbilityLevel(Caster, SPELL_ID)
    set DAMAGE = DAMAGEBASE * I2R(LEVEL)
    //Save Hash here
    call SaveUnitHandle(HASH, CASTERU, ID, Caster)
    call SaveUnitHandle(HASH, TARGETU, ID, Target)    
    call SaveReal(HASH, DAMAGER, ID, DAMAGE)
    call TimerStart(TIMERDIES, TIME, false, function RockDies)
endfunction
 
//===========================================================================
 
function InitTrig_Snare_Boom takes nothing returns nothing
    local trigger SnareB = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( SnareB, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( SnareB, Condition( function SnareBCondition ) )
    call TriggerAddAction( SnareB, function SnareBAction )
    set SnareB = null
endfunction
endscope

Now, please tell me if there are leaks like I never flush the hashtable, well I've done it before but didnt work...

Also, is there a way to reffer to a dying/triggering unit instead of timer?...
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
JASS:
    call SaveUnitHandle(HASH, 1, ID, Caster)
    call SaveUnitHandle(HASH, 2, ID, Target)    
    call SaveReal(HASH, 3, ID, DAMAGE)

You really should be using ID as the first index (the parent index). In this particular case I don't think it will make a difference because the indexes are fairly simple, but as a general rule you don't want to be using a common parent hash-table index such as 1.

JASS:
    set Caster = null
    set Target = null
    set Dummy = null
    set TIMERDIES = null

You don't need to null global variables. You're going to be recycling their references each time SnareBActions is used.

JASS:
    set Caster = LoadUnitHandle(HASH, 1, ID)    
    set Target = LoadUnitHandle(HASH, 2, ID)
    set DAMAGE = LoadReal(HASH, 3, ID)

You load them properly (remember though to switch those indexes around) but you do not remove the references from the hash table. You need to do:

JASS:
call RemoveSavedHandle(HASH, 1, ID)
call RemoveSavedHandle(HASH, 2, ID)
call RemoveSavedReal(HASH, 3, ID)

And again, in the RockDies function, you do not need to null those global variables. They will be recycled as soon as the function is called again.
 
thanks dude, I've fixed it, any more suggestions?...

one more thing, I experimented with...



JASS:
private integer CASTERU          = S2I("Caster")
private integer TARGETU          = S2I("Target")
private integer DAMAGER         = S2I("Damage")

but it gives me a bloody error "Worldedit121.exe" error so I have and no matter how much I tried to resolve this, the error keeps on going until I've decided to make a new map and transfers all the Info there...

so is this always the case?...

now I know why S2I doesnt work coz integer is not a string...
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Or you should just learn to use constant integers, or even "key".

I mean i'm really surprised you didn't tell him, it's not like it's something very confusing, hashtables use integers arguments, not string like gamecache, so it's very pointless to use string when it's not needed.

Also takes in consideration that StringHash is both case and "slash" insensitive.

Code:
StringHash("AbCdEFGh") == StringHash("abcdefgh")
StringHash("a/b") == StringHash("a\\b")

But that should be the same with the string gamecache arguments, at least i know it's the same for the case insensitivity, not tested the other one.
 
Yeah Im gonna clear that leaking timer...forgot to destroy it :)...

Troll, its not pointless to use string coz it makes the hash very unique and more trackable...as long as you put the right syntax, it shouldnt be a problem...anyway the 'key' is the 'ID' (if that's what you're talkign about)...

but my question still remains unaswered:

S2I = returns integer
StringHash = returns integer

but why S2I has an error or cannot be used?...
 
I did answer your question a few times:

Like I said in the other thread, you need to use StringHash, not S2I. S2I returns 0 for all of those.

S2I works ONLY if the first character in the string is an integer. So if the string is "lol" S2I returns 0. If the string is "something else" it returns 0. If all of your keys are 0 you constantly overwrite your data, which obviously fails.

StringHash properly returns a unique integer for strings. StringHash("lol") produces a value and StringHash("something else") produces a totally different value.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Troll, its not pointless to use string coz it makes the hash very unique and more trackable...as long as you put the right syntax, it shouldnt be a problem...anyway the 'key' is the 'ID' (if that's what you're talkign about)...

I mean the vjass type "key", basically it's just a constant integer that you don't choice the value, each "key" of your map is unique, the first begin to 1 and it grows up one by one.

Pointless is pointless, in your case it would be "useful" to convert quickly an old map which was using gamecache and don't use constant values.
But meh i won't argue anymore about a such obvious thing, anyway your map won't explode if you use unneeded strings in an hashtable.
 
More about S2I and StringHash.

S2I will actually convert a string into an integer given a string that can actually be parsed into an integer
"123" can be parsed as 123
"hello" can be parsed into ????
"1h" is parsed into 1 (all characters that aren't -,0,1,2,3,4,5,6,7,8,9 are ignored)


StringHash will actually "hash" a string. Hashing involving taking a value and then generating a pretty unique value from it. For example, a lot of encryption uses an md5 hash to prevent people from modifying the stuff. Save/load codes use a hash (a checksum) to prevent people from tampering with codes. A hash by itself is just a hash, a value that is tied with another value (possibly multiple values). For example, in save/load, you might have 100000 unique hashes, meaning that every 100000 codes, the hashes would repeat. This means that one hash will actually work for different things. StringHash works the same way.

In actuality, each character in a string has a value associated with it. A string can be converted into an integer (the entire string). The only problem is that most strings can't fit into an integer (2^32/2-1 signed limit remember?).

There are many different hashing algorithms. The http://www.hiveworkshop.com/forums/jass-functions-413/snippet-ascii-190746/ library actually shows a portion of the hashing algorithm in wc3 (single characters). It probably doesn't work on multiple characters (I doubt the string is simply converted into a huge integer).

StringHash(character) / 0x1F0748 + 0x3EA

That will actually return the ascii value of a character (not case sensitive and doesn't tell between / and \ as troll said).

I hope you now understand the difference between StringHash and S2I. Yes, they both return an integer, but StringHash doesn't convert a string into an integer, it hashes it.


Also, the Ascii library tells us that JASS strings use ANSI, which is these 2 tables
http://ascii-table.com/ascii.php
http://ascii-table.com/ansi-codes.php
 
...but StringHash doesn't convert a string into an integer, it hashes it.

its a straightforward answer...actually I already understand what Bribe told me, so I'm not experimenting anymore...+rep to all...

EDIT:
Now I made another spell, I closed the WE then changed the filename, when I opened it, then save, it gets the same error...bloody hell, what happened to JNGP?...
 
Last edited:
Level 1
Joined
May 25, 2009
Messages
5
Just curious, is there any reason as to why you're doing this with globals rather than locals? If you're doing it for a speed difference, locals are faster than globals.

Also, if you state a global that doesn't change and you're just putting it for easy reference or accessibility; such as the spell's ID, make sure you make that global into a constant variable, the optimizer sorts that out for you once you protect your map.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
As you declare globals in a map, reading from a global variable gets slower because the scope gets flooded.

And what about the local declaration ?

Anyway if that should matter that much vJass would be just unusable, since it's pretty much based on globals.

I mean the usage of a global or a local shouldn't be a matter of "speed", but just a matter of usage by itself.
 
OK, again...regarding my post #3 question about the error, still no one has answered it...

but it gives me a bloody error "Worldedit121.exe" error so I have and no matter how much I tried to resolve this, the error keeps on going until I've decided to make a new map and transfers all the Info there...

now its getting worse, everytime I open a map and then save it, its getting that bloody error...my version btw is 1.24e...

perhaps this error is normal coz JNGP is using 1.21?...I cant switch to 1.21 coz my maps are 1.24e, so Im so pissed...

this is the image of the error (sorry bribe, I posted this again)...


167625-albums4053-picture45667.jpg

 
Level 1
Joined
May 25, 2009
Messages
5
First, check if you have a worldedit version of 1.21, which is the latest supported version.

If that doesn't work, update JassHelper.

> Anyway if that should matter that much vJass would be just unusable, since it's pretty much based on globals.

vJass was created because there was a need for OOP.. some other features such as structs may be based on globals, but encapsulation was created in vJass before structs were.

As for structs, structs aren't there just to "provide an easier way to use globals," or whatever you're thinking, structs are there to make code un-readable and provide easier-to-reach MUI capability.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Risen said:
vJass was created because there was a need for OOP.. some other features such as structs may be based on globals, but encapsulation was created in vJass before structs were.

As for structs, structs aren't there just to "provide an easier way to use globals," or whatever you're thinking, structs are there to make code un-readable and provide easier-to-reach MUI capability.

I don't get what you want mean by "make code un-readable", a joke maybe ?

I really know how the whole vJass stuff is converted in jass, and why vJass was created, no need to explain me.
Again my point is you should use a global or a local variable only based on the usage by itself, not because of the usage "speed", which i still doubt there would be a noticeable difference in any real scenario.
 
Status
Not open for further replies.
Top