Tutorial Shadow variables - Clever way to avoid writing JASS code

Cohadar

master of fugue
Reaction score
209
Ok before we start a few notes.
This is advanced lvl tutorial if you don't know how to handle memory leaks
or even how variables work in World Editor i strongly recommend you read this 2 tutorials first:
http://world-editor-tutorials.thehelper.net/variables.php
http://world-editor-tutorials.thehelper.net/cat_usersubmit.php?view=27219

This tutorial is meant to be used as an extension of those 2 tutorials.

Purpose:
We are going to use some clever techniques to avoid writing too much JASS code

I am a programmer by profession and it might seem strange that I am advocating
people to avoid writing in JASS, but I am guided here by an universal principle:
If you can do it the easy way, don't do it the hard way

Besides if those programmers who wrote World Editor [WE] did their job properly noone would have any need to learn JASS at the first place.
And believe me when I tell you this: errors, or rather intentional flaws they
left in their GUI design could be fixed in no longer than 3 days work by a single programmer

Those flaws they left (and I honestly believe that was on purpose and that blizzard has internal full-functioning world editor) are
1. inability to declare local variables in GUI
2. inability to clean-up (deinitialize, destoy, free-memory) of variables in GUI
3. some other minor disadvantages with inability to pass order strings of derived spells and stuff like that witch are not of concern to us in this tutorial

So this tutorial will concentrate on removing first 2 WE flaws
witch are by the way the most common reasons why people write JASS code

Don't get me wrong JASS code if written good is faster, better, and more
MUI (multithreaded for programmers) than any code you can write in GUI
Also code written in assembler is more faster than any Java or C# code....
Get the picture?

By lovering the level of programming knowledge needed to write maps
we are making it possible for more people out there to enjoy in map-making

95% of people that are map-making do not want to write JASS code
so that they can do some Hi-Fi ultra-giga-mega effects in their map
they do it only because they want to remove leaks.

Ok lets start:
1. inability to declare local variables in GUI
2. inability to clean-up variables in GUI

If you read those tutorials I referenced at the beginning you already know
that you can indeed declare local variables in GUI
you just need a single line of script code:
Code:
Custom script:   local group Temp_Group = CreateGroup()

and you indeed can destroy those variables in GUI again with a single line of code
Code:
Custom script:   call DestroyGroup (Temp_Group)

So what is the problem than?

The problem is that you cannot use locally declared variables in GUI.
So if you plan to do anything with variables you declared locally to avoid leaks or to make a trigger MUI you have to use JASS.
There are some workarounds for this, like keeping original trigger in GUI
and then converting it to custom text and optimizing it,
but that is pain in the ass, and your trigger is still in JASS and if you need to modify it you can modify GUI backup, convert it again to text
optimize it again and so on and on......

Like I said pain in the ass.

And what is the solution?
Well solution is a little trick called Shadowing
It is a programmer term for declaring local variables with the same name as global variable.
And since both variables have same name, trigger can use only one,
and it will Always use local one.
The name of local is Shadowing(hiding) the name of global variable from trigger.

How does it work?
Lets imagine we declared a global integer variable
I_GlobalNumber and that we assigned it a value 77
(I like to prefix my variables with single letter to identify their type
so variables of same type get sorted together in Global Variables Editor
It also makes it obvious at any time what is the type of that variable
Use I_ for integer, S_ for string R_ for region ...)


We will now make 2 triggers
One that will display Global Value of this variable when we press Up/Down keys
and second that will shadow variable and display Shadowed value
with Exactly same command

Here they are:
Code:
Press UpDown Global
    Events
        Player - Player 1 (Red) Presses the Up Arrow key
        Player - Player 1 (Red) Presses the Down Arrow key
    Conditions
    Actions
        Game - Display to Player Group - Player 1 (Red) the text: (Integer Value:  + (String(I_Global_Number)))

Code:
Press UpDown Shadowed
    Events
        Player - Player 1 (Red) Presses the Up Arrow key
        Player - Player 1 (Red) Presses the Down Arrow key
    Conditions
    Actions
        Custom script:   local integer udg_I_Global_Number = 12345
        Game - Display to Player Group - Player 1 (Red) the text: (Integer Value:  + (String(I_Global_Number)))

The only difference in triggers is
Code:
Custom script:   local integer udg_I_Global_Number = 12345
where we declared new local variable with same name as global one
(with udg_ prefix of course) and we assigned it a new value

Here is what we will see when Up/Down is pressed
Code:
Integer Value: 77
Integer Value: 12345

Note that we used exactly same action to display numbers
Code:
        Game - Display to Player Group - Player 1 (Red) the text: (Integer Value:  + (String(I_Global_Number)))
one displayed global value, the other displayed local
we used GUI command on a local variable
nice eh :)

So we basically have 2 variables and only local Shadowed variable is visible to the trigger because it is "pretending" to be the global one
so we can use GUI with it.


Lets make a more complicated example:
Here is a trigger for moving all units from one region to the center of other region:

Code:
Move Right
    Events
        Player - Player 1 (Red) Presses the Right Arrow key
    Conditions
    Actions
        Unit Group - Pick every unit in (Units in R Left <gen>) and do (Actions)
            Loop - Actions
                Unit - Order (Picked unit) to Move To (Center of R Rigth <gen>)

Now moderately good map maker would like to optimize this trigger
but he does not want to do a full JASS conversion
and he would do something like this:

Code:
Move Right
    Events
        Player - Player 1 (Red) Presses the Right Arrow key
    Conditions
    Actions
        Set Temp_Group = (Units in R Left <gen>)
        Unit Group - Pick every unit in (Units in R Left <gen>) and do (Actions)
            Loop - Actions
                Unit - Order (Picked unit) to Move To P_RightCenter
        Custom script:   call DestroyGroup (udg_Temp_Group)

Where Temp_Group is of course global variable he declared for this purpouse.

There are a couple of flaws to this approach:
If any other trigger uses this Temp_Group variable
this is no longer MUI because Temp_Group can be modified by 2 triggers at same time it would make things really bad.

So he must not use Temp_Group in other triggers
but he wants to optimize other triggers also
so he makes Temp_Group1, Temp_Group2, Temp_Group3

Then he realizes he needs Temp_Pointers too
And Temp who knows what also
and witch trigger is using Temp_Group34 ?
and your global variables editor becomes a very crowded place.

Here is the solution:
Code:
Move Right
    Events
        Player - Player 1 (Red) Presses the Right Arrow key
    Conditions
    Actions
        [B]Custom script:   local group udg_Temp_Group = CreateGroup()[/B]
        Set Temp_Group = (Units in R Left <gen>)
        Unit Group - Pick every unit in (Units in R Left <gen>) and do (Actions)
            Loop - Actions
                Unit - Order (Picked unit) to Move To P_RightCenter
        Custom script:   call DestroyGroup (udg_Temp_Group)

Bolded line is the only thing we added to fix this trigger
We created a local variable with the same name as global one
I call upon the powers of Shadow Orb to crush you pitiful mortals....
khm, never mind that...

So guess what just happened, our optimized trigger is using a new shadowed local variable, and the rest of trigger is at the same time both GUI and MUI :)

Witch is even better we can now use Temp_Group for any other trigger
in the same way and it will still be both GUI and MUI

One global variable does it all.
It just has a lot of shadows (like football players under reflectors)
doing local work for it in triggers.

You can also think of global variable as a Hero
and of Shadows as illusions :D

and at the end:

4 steps for flawless Shadowing:
1. create a global variable that will be shadowed
(use standard prefix for this variables i like to prefix them all with Temp_
and NEVER use this variables for anything else except shadowing)
2. when you want to use this variable locally first create a Shadow
Custom script: local group udg_Temp_Group = CreateGroup()
3. And ofter you done with it clean-up of course
Custom script: call DestroyGroup (udg_Temp_Group)
4. NEVER declare more than ONE Shadow variable in one trigger
Two shadow variables will fuckup the trigger, the first one to notice this was Tom Jones
and as was later discovered this is because all Shadow variables will occupy
same memory space due to stupidity of GUI -> JASS compiler

To prove this create two global integer variables TempA and TempB
and try this trigger:
Code:
There can be only One
    Events
        Player - Player 1 (Red) Presses the Up Arrow key
    Conditions
    Actions
        Custom script:   local integer udg_TempA = 11
        Custom script:   local integer udg_TempB = 33
        Game - Display to (All players) the text: (A =  + (String(TempA)))
        Game - Display to (All players) the text: (B =  + (String(TempB)))

The game will print:
Code:
A = 33
B = 33

Major fuckup.

One Shadow variable will NOT mess up other local(non-shadow) variables
You can use locals in trigger with shadow variable and all will be fine.


Here is the proof:
Code:
Shadow and Local Test
    Events
        Player - Player 1 (Red) Presses the Up Arrow key
    Conditions
    Actions
        Custom script:   local integer SecondTempInteger = 44  // Local
        Custom script:   local integer udg_TempInteger = 33  //Shadow
        Game - Display to (All players) the text: Shadow and Local Te...
        Game - Display to (All players) the text: (Shadow =  + (String(TempInteger)))
        Custom script:   call DisplayTextToForce( GetPlayersAll(), ( "Local = " + I2S(SecondTempInteger) ) )

It will write:
Code:
  Shadow = 33
  Local = 44
all is fine.

It is always a good idea to put those //Shadow and //Local comments like in trigger above
It will help you remember what is what,
and it will certainly be obvious if you have 2 //Shadow
And one more time:
Don't use more than one shadow



If you stick to the rules all will be fine and
all your code will be both GUI and MUI and Leak free
Imba :D


and finally a map demo,
it contains examples from this tutorials and
couple of triggers that are both optimized(with shadowing) and unoptimized
so you can compare
 

master maste

New Member
Reaction score
32
And believe me when I tell you this: errors, or rather intentional flaws they
left in their GUI design could be fixed in no longer than 3 days work by a single programmer

If you are a programmer then why don't you fix up the world editor in 3 days and make a new version like WEU but one that actually works properly?

anyways still trying to read the thread.
 

Cohadar

master of fugue
Reaction score
209
There is no if in me being a programmer
I do it for a living.

And believe me I would fix world editor in 2 days if someone gave me the source code.

Someone steal the code from blizzard plz :)

Besides if WE was open-source from beginning
there would be like 1000 programmers waiting in line to fix it,
and it would be fixed long before I got the chance to do it.
And like is said it is not broken to be fixed,
it is intentionally crippled by blizzard

Hell I would not even need a source code if there was a way to write plug-in for a damn thing, I would simply make a plug-in that would automatize Shadowing

PLEAZE NOTE: Blizzard Entertainment does NOT directly support this editor....

mofos
 

Cohadar

master of fugue
Reaction score
209
From WE help File
Code:
The Warcraft III Expansion World Editor allows the creation of scenario, 
campaign and multiplayer maps. 
This is the most complete design tool Blizzard Entertainment has ever released to the public. 
This editor is [B]virtually unchanged[/B] from the editor the level designers used to create their maps.

This is of course a gross lie.
They added like tons of features between WC3 and TFT editor
and adding code that would make it possible to declare local variables
would be a children's play compared to all changes they did

Oh and in programmer slang virtually unchanged means except for some minor modifications...

All you need is to convert new GUI command that would look something like

Declare Local <name> of Type <Type>
into one line of custom code text

and import new variable name into local compiler name cache

that is literally no more than 10 lines of c++ code

There is no real reason for this not to be done but pure
corporate commercial idiotism
 

Andrewgosu

The Silent Pandaren Helper
Reaction score
716
Quite nicely explained, although, it would be more reasonable to learn JASS for true than to mess with custom script via GUI. But that's just my opinion.

Anyway, again, good job.
 

Cohadar

master of fugue
Reaction score
209
Does first post refresh Thread time when I update it?
Anyway I edited it again.
Look for green text
 
General chit-chat
Help Users

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top