• 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] Need Help Writing JASS

Status
Not open for further replies.
Level 13
Joined
Aug 31, 2005
Messages
823
I need help writing a JASS spell. The spell will do the following:
-The casting unit will slide towards the target unit (during the slide the unit will be in the "Stand_Ready" animation)
-The casting unit will deal 200 damage to the targeted unit
-if the casting unit or the targeted unit dies while the casting unit is moving towards the targeted unit, the spell will stop.

I also have a question about the game cache. Can you have more than 1 Game Cache variable per map?

My JASS Trigger so far:
JASS:
//Slash
function RawSlash takes nothing returns integer
    return 'A000' 
endfunction
//Slash Speed
function speed_constant takes nothing returns real
    return 10.00 
endfunction
I know its not much but this is my frist JASS so any help is greatly appreciated.
 
Level 10
Joined
Jul 14, 2004
Messages
463
Well, your spell idea sounds not too easy for your first spell. Do you know how sliding and all the things you need might work in GUI more or less (you don't have to build it, actually)? If you then have read Daelin's JASS Tutorial, you should get much more far than you are now.
If we have to explain every little thing to you alone, that gets too much (at least for me). :wink:
 
Level 5
Joined
May 22, 2006
Messages
150
If you do have a working GUI-trigger, it is not too hard to compile it into a Jass script.
All you need is a big amount of time (let us say... two hours, maybe less) and two files:
common.j and blizzard.j

Now this is, what to do:
- open the trigger in the wc3 editor
- compile it into "custom text"

You will see a Jass script of incredibly bad quality.
Now get started:
- look out for function names in parameters
("Trig_<name of trigger>_Func<some long number>" or something like this)

Each of these functions is declared somewhere in the code's upper part.
if their structure matches this one:
JASS:
function <name> takes nothing returns boolean
  if not <boolean expression> then
    return false
  endif
  return true
endfunction
or this one:
JASS:
function <name> takes nothing returns boolean
  return (<boolean expression>)
endfunction

Copy the part between "not" and "then" and paste it into the parameter, where the function's name was before.
After this, delete the function.

Example before cleaning:
JASS:
function Trig_DummyOfTheLord_Func00000300001 takes nothing returns boolean
  if not GetUnitUserData(GetTriggerUnit()) == 0 then
    return false
  endif
  return true
endfunction

function Trig_DummyOfTheLord_Actions takes nothing returns nothing
  if Trig_DummyOfTheLord_Func00000300001 then
    call BJDebugMsg("Test.")
  endif
endfunction
Example after cleaning:
JASS:
function Trig_DummyOfTheLord_Actions takes nothing returns nothing
  if GetUnitUserData(GetTriggerUnit()) == 0 then
    call BJDebugMsg("Test.")
  endif
endfunction

- look out for calls of the functions "GetBooleanAnd(boolean,boolean)" and "GetBooleanOr(boolean,boolean)"

Replace them by combining their parameters with " and " for "GetBooleanAnd" and " or " for "GetBooleanOr".

Example before cleaning:
JASS:
return GetBooleanAnd(true,false)
Example after cleaning:
JASS:
return true and false

(these examples are nonsense but show the correct structure)

- if your trigger had a condition, watch out for the function "Trig_<name of trigger>_Conditions"

Most likely it's structure will be similar to this one of the replaceable functions I mentioned above.
Clean it as shown.

Example before cleaning:
JASS:
function Trig_DummyOfTheLord_Conditions takes nothing returns boolean
  if not GetSpellAbilityId() == 'AEim' then
    return false
  endif
  return true
endfunction
Example after cleaning:
JASS:
function Trig_DummyOfTheLord_Conditions takes nothing returns boolean
  return GetSpellAbilityId() == 'AEim'
endfunction

Now, the syntax should be more or less clean of unnecessary junk.
It is time to make the trigger multiinstanceable.

- look for global variables: "udg_<name>"

If each of these is only used in a single function and it's value must not be accessible on the next time, the trigger triggers, the way is easy to go:

Straight under the functions headline, write something similar to "local <type> <name>"
To find out, which type the variable shall have, look into your globals and just copy the type shown there.
If your editor is not the english version, seek the english translation of the shown type name in "common.j" and take this one.

Now, simply remove "udg_" whereever in the function the variable is used.
At the function's end, add a line similar to "set <name> = null" if your variable has no primitive type like integer or real. (The editor will throw an error, if it has)

Now, you have to think a little bit:
Do the value referenced by your variable has to exist further after the trigger triggered once?
If not, you have to destroy it in order to prevent a memory leak, which could only be "repaired" by restarting the map.
So, for each object type there is a removing function.
Look after it in "common.j", it's name must be "Remove" or "Destroy" plus the value's type's name.
The function has to be called before setting the variable to null.

Example:
JASS:
function Trig_DummyOfTheLord_Actions takes nothing returns nothing
  local unit a = CreateUnit(Player(0),'h001',136.9,700,0)
  call RemoveUnit(a)
  set a = null
endfunction

If you need to use a variable in more than one function, ask for further instruction, this one is already pretty long.

So, now your trigger should be free of leaks and multiinstanceable.
One thing remains to be done.

- look if one of the functions called in your code is defined in "blizzard.j"

If you find one (and you will find one), try replace it with the actions in it's body if you think, you are able to and if it appears to be useful (for example replacing "RMaxBJ" does not pay the time)

Example before cleaning:
JASS:
call SetUnitLifePercentBJ(GetTriggerUnit(),79)
Example after cleaning:
JASS:
call SetUnitState(GetTriggerUnit(),UNIT_STATE_LIFE,GetUnitState(GetTriggerUnit(),UNIT_STATE_LIFE) * 0.79)

Surely you will ask yourself, what this could be good for, as most times the code will grow larger.
Well, let us say it this way:
Each call of a function needs a very small amount of time.
So if you call a function, the computer needs the time to call this function and the time to call any function used inside this one.
So, you save a little bit, if you write the function's body directly into your code instead of calling it.
Mostly, doing that is not necessary, but after some thousand times, even the littlest amount of time grows into a respectable lot.

... And one should anyway try to write one's script as efficient as possible.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Aye, the problem is that that doesn't cover Handle Vars, nor writing the script from scratch :?, so the spell wont be MUI, nor necessarily completely understandable

And again, I suggest JASSCraft

Its still a good way to learn, in fact, its the way I learned :p

PS. You can have as many GCs as you want
 
Level 5
Joined
May 22, 2006
Messages
150
I do not think, that you need to transport values between functions to do, what is wanted, it does not need any alteration than replacing globals with locals to make this script multiinstanceable.

And if it does:
If you need to use a variable in more than one function, ask for further instruction, this one is already pretty long.

No problem to add that.

... But I guess, following you is still worthy, as I am not the most skilled person in explaining even in my mother tongue. ^^
 
Level 13
Joined
Aug 31, 2005
Messages
823
wow awesome guys. thanks :D ! I actually wrote my Jass script (using the tutorial) and I copied a jass script from Shadow1500's shield Jass. Only problem now is im not sure how to impliment both into the same map since they use seperate gamecache functions and CnP the code just gives alot of errors.
Heres Shadow's code:
JASS:
//================
function H2I takes handle h returns integer
    return h
    return 0
endfunction
 
// ===========================
function LocalVars takes nothing returns gamecache
    return udg_hash
endfunction
 
function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction
 
function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
         call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
 
function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
function SetHandleBoolean takes handle subject, string name, boolean value returns nothing
    if value==false then
        call FlushStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreBoolean(LocalVars(), I2S(H2I(subject)), name, true )
    endif
endfunction

function GetHandleBoolean takes handle subject, string name returns boolean
    return GetStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction
 
function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleEffect takes handle subject, string name returns  effect
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTextTag takes handle subject, string name returns  texttag
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTriggerAction takes handle subject, string name returns triggeraction
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
 
function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction


// Shield System
// By Shadow1500
// HandleVars by KaTTaNa

// configuration


constant function BarColor takes nothing returns string
    return "|cFF8000FF"
endfunction
constant function BarChar takes nothing returns string
    return "'"
endfunction
constant function BarLength takes nothing returns integer
    return 20
endfunction
constant function BarSize takes nothing returns real
    return 12.50
endfunction
constant function BarOffset takes nothing returns real
    return -32.00
endfunction
constant function BarOffsetY takes nothing returns real
    return -42.00
endfunction
constant function DeadFixerAbility takes nothing returns integer
    return 'A004'
endfunction
constant function AllowDamageSpill takes nothing returns boolean
    return true
endfunction //if true then when the shield breaks it will only block some of the damage done depending on its HP
// end configuration


function DamageModify_Child takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = GetHandleUnit(t,"u")

    if GetHandleBoolean(t,"fix") then
        call UnitRemoveAbility( u, DeadFixerAbility() )
    endif
    call SetUnitState(u ,UNIT_STATE_LIFE, GetHandleReal(t,"finalhp") )

    set u = null
    call FlushHandleLocals(t)
    call DestroyTimer(t)
    set t = null
endfunction

// This function will "block" a certain amount of damage done to the unit
// Needs to be used in the EVENT_UNIT_DAMAGED event.
function DamageModify takes unit whichUnit, real dmg, real dmgnew returns nothing
    local timer t = CreateTimer()
    local real life = GetUnitState(whichUnit, UNIT_STATE_LIFE)
    local real maxlife = GetUnitState(whichUnit, UNIT_STATE_MAX_LIFE)
    local boolean usetimer = false
    local real dmgback = dmg-dmgnew
    local real finalhp = life-dmgnew
    if dmgnew>dmg then
        return
    endif
    if dmg > maxlife then
        call UnitAddAbility( whichUnit,DeadFixerAbility())
        call SetHandleBoolean(t, "fix", true)
        set maxlife = 1000+maxlife
        set usetimer = true
    endif
    if ( life+dmgback > maxlife ) then
        call SetUnitState(whichUnit ,UNIT_STATE_LIFE, maxlife )
        set usetimer = true
    else
        call SetUnitState(whichUnit ,UNIT_STATE_LIFE, life+dmgback )
    endif
    if usetimer then
        call SetHandleHandle(t,"u",whichUnit)
        call SetHandleReal(t,"finalhp",finalhp)
        call TimerStart(t, 0, false, function DamageModify_Child)
    else
        call DestroyTimer(t)
    endif
    set t = null
endfunction

function UpdateBar takes unit whichUnit returns nothing
    local string bar = GetHandleString(whichUnit,"scolor")
    local integer y = 0
    local real slife = GetHandleReal(whichUnit,"slife")
    local real maxslife = GetHandleReal(whichUnit,"maxslife")
    local boolean endc = false

    loop
        exitwhen y==BarLength()
        if (not endc) and (slife < (y * (maxslife/BarLength()))+1) then
            set endc = true
            set bar = bar+"|r|cFF000000"
        else
            set bar = bar+BarChar()
        endif
        set y = y + 1
    endloop
    set bar = bar+"|r"
    call SetTextTagTextBJ(GetHandleTextTag(whichUnit,"lifebar"),bar,BarSize())
endfunction
function DestroyShield takes unit whichUnit returns nothing
    local texttag tag = GetHandleTextTag(whichUnit,"lifebar")
    local trigger trig = GetHandleTrigger(whichUnit,"shielddmg")
    local timer t = GetHandleTimer(whichUnit,"shieldtimer")
    call SetHandleBoolean(whichUnit,"sd",false)
    
    // destroy shield
    call UnitRemoveAbility(whichUnit,GetHandleInt(t,"buff"))
    call SetHandleReal(whichUnit,"slife",0)
    call SetHandleReal(whichUnit,"maxslife",0)
    call SetHandleReal(whichUnit,"reg",0)
    call SetHandleReal(whichUnit,"sarmor",0)
    call SetHandleString(whichUnit,"scolor",null)
    
    // destroy tag
    call DestroyTextTag(tag)
    call SetHandleHandle( whichUnit, "lifebar", null )
    set tag = null
    
    // destoy trigger
    call SetHandleHandle(whichUnit,"shielddmg",null)
    call TriggerRemoveAction(trig,GetHandleTriggerAction(trig,"action"))
    call FlushHandleLocals(trig)
    call DestroyTrigger(trig)
    set trig = null
    
    // destroy timer
    call SetHandleHandle(whichUnit,"shieldtimer",null)
    call PauseTimer(t)
    call FlushHandleLocals(t)
    call DestroyTimer(t)
    set t = null
endfunction
function ShieldDamage takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real dmg = GetEventDamage()
    local real slife = GetHandleReal(u,"slife")
    local real take = 1-GetHandleReal(u,"sarmor")
    if GetTriggerEventId() == EVENT_UNIT_DEATH then
        call DestroyShield(u)
        set u = null
        return
    endif
    if (slife<(dmg*take)) then
        if GetHandleBoolean(u,"sd") then
            call DestroyShield(u)
        else
            call SetHandleReal(u,"slife",0)
        endif
        if AllowDamageSpill() then
            call DamageModify(u,dmg,dmg-(slife*(1/take))) // use the remaining power of the shield to block some of the damage
        else
            call DamageModify(u,dmg,0)
        endif
    else
        call DamageModify(u,dmg,0) // block all damage
        call SetHandleReal(u,"slife",slife-(dmg*take))
        call UpdateBar(u)
    endif
    set u = null
endfunction
function CreateShield_Child takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit whichUnit = GetHandleUnit(t,"u")
    local texttag tag = GetHandleTextTag(whichUnit,"lifebar")
    local real x = GetUnitX(whichUnit)+BarOffset()
    local real y = GetUnitY(whichUnit)+BarOffsetY()
    local integer pulse = GetHandleInt(t,"pulse")
    local real upcheck = GetHandleReal(t,"check")
    local real reg = GetHandleReal(whichUnit,"reg")
    local real slife = GetHandleReal(whichUnit,"slife")
    local real maxslife = GetHandleReal(whichUnit,"maxslife")
    local integer buffId = GetHandleInt(t,"buff")
    if (GetHandleBoolean(whichUnit,"sd") and pulse==0) or ((GetUnitAbilityLevel(whichUnit,buffId)==0) and buffId!=0) then
        call DestroyShield(whichUnit)
        set t = null
        return
    endif
    
    if reg!=0 then
       if (slife+reg)>=maxslife then
           call SetHandleReal(whichUnit,"slife",maxslife)
       else
           call SetHandleReal(whichUnit,"slife",slife+reg)
       endif
       call UpdateBar(whichUnit)
    elseif upcheck!=GetHandleReal(whichUnit,"slife") then
        call SetHandleReal(t,"check",GetHandleReal(whichUnit,"slife"))
        call UpdateBar(whichUnit)
    endif
    
    // countdown on how many times did timer ran
    if pulse>0 then
        call SetHandleInt(t,"pulse",pulse-1)
    endif
    
    call SetTextTagPos(tag,x,y,140)
    call SetTextTagVisibility(tag,IsUnitVisible(whichUnit,GetLocalPlayer()))

    set whichUnit = null
    set tag = null
    set t = null
endfunction
function CreateShieldEx takes unit target, real shieldhp, real duration, integer buffId, real regenerate, real armor, string color, boolean dmgspill returns nothing
    // duration of -1 = shield remains on unit when depleted
    // duration of 0 = shield stays on unit until depleted
    // duration of 1 or higher = shield stays until depleted or until duration ends
    // buffId is optional
    // regenerate is the amount of points regenerated every second
    // armor must be between 0 and 1, with 1 making the shield take no damage
    // color must in blizzard's color code format
    // when dmgspill is off the shield will block all damage when depleted
    local texttag tag = GetHandleTextTag( target, "lifebar")
    local trigger whenDamaged = GetHandleTrigger(target,"shielddmg")
    local timer t = GetHandleTimer(target,"shieldtimer")
    local boolean useOld = false    
    local real x = GetUnitX(target)+BarOffset()
    local real y = GetUnitY(target)+BarOffsetY()

    if GetHandleReal(target,"slife")!=0 then
        if GetHandleInt(t,"buff")!=buffId then
            call UnitRemoveAbility(target,GetHandleInt(t,"buff"))
        endif
        call PauseTimer(t)
        call SetHandleReal(target,"reg",0)
        call SetHandleReal(target,"sarmor",0)
        call SetHandleString( target, "scolor", null)
        call DestroyTextTag(tag)
        set useOld = true
    else
        set t = CreateTimer()
        set whenDamaged = CreateTrigger()
    endif
    set tag = CreateTextTag()
    call SetHandleReal( target, "slife", shieldhp)
    call SetHandleReal( target, "maxslife", shieldhp)
    
    call SetHandleString( target, "scolor", color)
    
    // take care of the texttag
    call SetTextTagTextBJ(tag,"tag",BarSize())
    call SetTextTagPos(tag,x,y,140)
    call SetTextTagPermanent(tag,true)
    call SetTextTagColor(tag,255,255,255,255)
    call SetTextTagVisibility(tag,IsUnitVisible(target,GetLocalPlayer()))
    call SetHandleHandle( target, "lifebar", tag )
    call UpdateBar(target)
    
    // take care of the trigger
    if not useOld then
        call TriggerRegisterUnitEvent(whenDamaged,target,EVENT_UNIT_DAMAGED)
        call TriggerRegisterUnitEvent(whenDamaged,target,EVENT_UNIT_DEATH)
        call SetHandleHandle(whenDamaged,"action",TriggerAddAction(whenDamaged,function ShieldDamage))
        call SetHandleHandle(target,"shielddmg",whenDamaged)
    endif
    call SetHandleBoolean( whenDamaged, "dmgspill", dmgspill)

    // take care of the timer
    call SetHandleHandle( target, "shieldtimer", t)
    call SetHandleHandle( t,"u",target)
    call SetHandleReal( t, "check", shieldhp)
    if regenerate!=0 then
        call SetHandleReal(target,"reg",(regenerate)/(1/0.04))
    endif
    if armor!=0 then
        call SetHandleReal(target,"sarmor",armor)
    endif
    call SetHandleInt(t,"pulse",-1)
    if duration>=0 then
        call SetHandleBoolean( target, "sd", true)
        if duration>0 then
            call SetHandleInt(t,"pulse",R2I(duration/0.04)) 
        endif
    endif
    call SetHandleInt(t,"buff",buffId)
    
    call TimerStart(t,0.04,true,function CreateShield_Child)

    set whenDamaged = null
    set t = null
    set tag = null
endfunction
function CreateShield takes unit target, real shieldhp, real duration, integer buffId, real regenerate, real armor returns nothing
    call CreateShieldEx( target, shieldhp, duration, buffId, regenerate, armor, BarColor(), AllowDamageSpill())
endfunction
function CreateShieldSimple takes unit target, real shieldhp, real duration, integer buffId returns nothing
    call CreateShieldEx( target, shieldhp, duration, buffId, 0, 0, BarColor(), AllowDamageSpill())
endfunction


And heres my code: (Based off Vexorians Tutorial)

JASS:
function LocalVars takes nothing returns gamecache
    if udg_cache==null then
        set udg_cache=InitGameCache("cache")
    endif
    return udg_cache
endfunction

//-------------------------------------------------

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function H2U takes handle h returns unit
    return h
    return null
endfunction

//-------------------------------------------------

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

//-------------------------------------------------

function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

//-------------------------------------------------

function SetHandleHandle takes handle subject, string name,  handle value returns nothing
    call SetHandleInt( subject, name, H2I(value) )
endfunction

//-------------------------------------------------

function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction

//-------------------------------------------------

function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction


//-------------------------------------------------

function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction

//-------------------------------------------------

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction
 
Level 13
Joined
Aug 31, 2005
Messages
823
I think the Handle Vars is my problem though. See I made my Jass spell on a seperate map from the other Jass spell im using. And because they are on different maps they are using seperate catche functions and whatnot. And im not exactly sure how to impliment both into the same map. Can I just change the GameCatch Variable on one though the script? (Im thinking CnP = Bad Idea for Jass Scripts)
 
Status
Not open for further replies.
Top