• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[GvJ] GUIvJASS Basics

Level 6
Joined
Jan 9, 2019
Messages
102
GUIvJASS Basics
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾

Preface
GUI is nice to see and manage, but it's slower than JASS as everyone already knows. But having every single trigger written in JASS can cause creeping headaches for some. So, why not use them together?

GUI's Limits
We know that JASS can do all things there is, but it's not fully supported by the GUI, and as a matter of fact, GUI makes it perform worse. Then everyone knows that GUI is awesome. But, what exactly are GUI's limits compared to its better sibling?

1.
GUI mostly uses BJ functions, which are slower.
2.
GUI handles conditions very very badly.
3.
GUI can only access user-defined-globals (udg_ variables). It can't access locals and non-udg globals.
4.
But GUI can still access any vJASS statement through its custom-script action, but only from the actions block. And the custom-script action can actually be used to insert block statements like function and endfunction.
5.
Any GUI trigger has its initializing function automatically generated, and the generated function cannot be removed unless the trigger is converted to custom text OR the war3map.j is edited directly.

The Environment
With the current limitation of GUI, and how it usually works with temporal variables, this is the basic environment of a proper GvJ.

1.
The temporal udg variables belong to no scope, and are named exactly as their type with their first letter of each word capitalized. Arrays use plural names. Ex: UnitType, Integers.
2.
All other udg variables must belong to a scope by prefixing their name, and must not refer to very short names except for mathematical symbols. Ex: UnitIndexer_IndexedUnit, Position_X.
3.
Any group of triggers that work together or relate to each other must have the same prefix. This is not to be confused with vJASS libraries that require other libraries. Ex: UnitIndexer, UnitIndexer Init, UnitIndexer Event.
4.
GvJ systems are written in pure vJASS with the use of some udg variables, and the GUI must be able to use them with as little custom-script actions as possible.
5.
Other than system scripts, GvJ code should mostly be in GUI, but this doesn't mean a map with all of its code fully written in vJASS can't belong in GvJ environment.

BasicGvJ - The Basic Library
On top of the defined environment, GvJ must have a basic library for GvJ systems to refer. This is important to make sure those systems can properly check and use the correct temporal variables that GvJ environment has. This basic library must not be changed, but it can be extended, as in adding more udg temporal variables, and making more advanced/specific libraries related to GvJ environment.

JASS:
library BasicGvJ
//== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
//
//		_____________
//		 #  BasicGvJ ---------------
//				v1.0a, by Overfrost
//		----------------------------
//
//
//    - helps in stating that a map supports GvJ environment
//
//    - contains textmacros that are used to handle triggers generated by GUI
//
//    - refer to this link for details on GvJ and its environment
//		(hiveworkshop.com/threads/gvj-guivjass-basics.312631)
//
//
//== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
//
//		------------------
//		 #	GvJ Interface
//		------------------
//
//
//		REMOVE("Trigger_Name")
//		- removes a GUI trigger
//
//		REPLACE("Trigger_Name")
//		- creates a new empty trigger, replacing a GUI trigger
//		- removes the replaced trigger
//
//
//== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====

	//
//! textmacro REMOVE takes ID
		//
		call DestroyTrigger(gg_trg_$ID$)
		set gg_trg_$ID$ = null
		//
//! endtextmacro
//! textmacro REPLACE takes ID
		//
		call DestroyTrigger(gg_trg_$ID$)
		set gg_trg_$ID$ = CreateTrigger()
		//
//! endtextmacro

endlibrary
  • BasicGvJ GUI
    • Events
    • Conditions
    • Actions
      • -------- --------
      • -------- Only here to help defining temporal variables --------
      • -------- Should be removed immediately after pasting it to a map --------
      • -------- --------
      • -------- BasicGvJ Examples --------
      • Custom script: //! runtextmacro DESTROY("BasicGvJ_GUI")
      • Custom script: //! runtextmacro CREATE("BasicGvJ_GUI")
      • Custom script: //! runtextmacro RECREATE("BasicGvJ_GUI")
      • -------- Primitive Types --------
      • Set Booleans[0] = Boolean
      • Set Integers[0] = Integer
      • Set Reals[0] = Real
      • Set Strings[0] = String
      • -------- Commons --------
      • Set Units[0] = Unit
      • Set Items[0] = Item
      • Set Locations[0] = Location
      • Set Players[0] = Player
      • -------- Singles --------
      • Set Effect = Effect
      • Set Group = Group
      • Set Force = Force
      • Set Rect = Rect
      • Set Trigger = Trigger
      • -------- Integers in JASS --------
      • Set Ability = Ability
      • Set UnitType = UnitType

Textmacros As GUI API
The title explains itself already, but why? Firstly, what textmacros do is just copy-pasting lines of codes which is done before anything else are processed. Due to this, textmacros won't make unnecessary extra function calls.

Secondly, it makes GvJ systems easier to be implemented as both a vJASS system with the usual API and as a GvJ system with its textmacro API. Thirdly, it should be easier for GUI users to use GvJ systems with textmacro API, because there's only one syntax for executing textmacros and all textmacro arguments are always string constants.

Lastly, textmacros are very very versatile that they can even be used to insert partial block statements. Though used simply to pass partial identifiers is huge enough, as can be seen in BasicGvJ library.

Passing Arguments and Return Values
There are some interesting facts about this if we're to use textmacros as GUI API. The first fact is that one single custom-script action will be very efficient in many aspects, including how the arguments are all pseudo-arguments. Another fact is that one textmacro run can return multiple values at once, returned as GUI-friendly temporal variables.

And more, the arguments required by a textmacro API are "passed" by reference, increasing its versatility. With all of those in mind, here's an excerpt from a GvJ system plus a GUI example alongside it.

JASS:
library Color requires optional BasicGvJ
//== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
//
//		__________
//		 #	Color ------------------
//				v1.2a, by Overfrost
//		----------------------------
//
//
//	  - packs separated (r,g,b) values within [0, 255] range into one integer (hex)
//
//	  - unpacks a color integer (hex) into separated (r,g,b) values
//		(e.g. 0xFFCC00 -> r=255, g=204, b=0)
//
//	  - each Color instance is actually a color-hex integer,
//		so this constructor is valid:  Color(0xFFCC00)
//
//	  - can convert from hsl to rgb, but stores the resulting (r,g,b) values only
//
//	  - can convert its (r,g,b) values into corresponding (h,s,l) values
//
//	  - has built-in methods to retrieve playerColors and to colorize strings
//
//
//  --------------------------
//   #  Optional Requirements
//	--------------------------
//
//    - BasicGvJ  (GvJ Support)
//          hiveworkshop.com/threads/gvj-guivjass-basics.312631
//
//
//== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
//
//	------------------
//	 #	GvJ Interface
//	------------------
//
//
//		Variables:
//
//			Color  = integer
//			- the index of a Color, to be stored in another variable if needed
//
//			Color_Red    = integer
//			Color_Green  = integer
//			Color_Blue   = integer
//			- the RGB value of the color, separated to integers within [0, 255] range
//
//
//		COLOR_HEX("Color_Hex")  ex. COLOR_HEX("FF0000")
//			sets:
//				Color        = (storable index of the color)
//				Color_Red    = (red value of the color)
//				Color_Green  = (green value)
//				Color_Blue   = (blue value)
//
//
//		COLOR_RGB("R_Value", "G_Value", "B_Value")  ex. COLOR_RGB("0", "0", "255")
//			sets:
//				Color        = (storable index)
//				Color_Red    = (red value)
//				Color_Green  = (green value)
//				Color_Blue   = (blue value)
//
//			- R, G, B are all in integer within [0, 255] range
//
//
//		COLOR_HSL("H_Value", "S_Value", "L_Value")  ex. COLOR_HSL("120", "1", "0.5")
//			sets:
//				Color        = (storable index)
//				Color_Red    = (red value)
//				Color_Green  = (green value)
//				Color_Blue   = (blue value)
//
//			- Range:
//				H = [0, 360 or more]
//				S = [0, 1]
//				L = [0, 1]
//
//			- H, S, L are all in real
//
//
//		COLOR_OF_PLAYER("Player_Number")  ex. PLAYER_COLOR("1")
//			sets:
//				Color        = (storable index)
//				Color_Red    = (red value)
//				Color_Green  = (green value)
//				Color_Blue   = (blue value)
//
//			- 1 is player red, 2 is blue, and so on
//
//
//		COLOR("Color")  ex. COLOR("udg_Integer")
//			sets:
//				Color_Red    = (red value)
//				Color_Green  = (green value)
//				Color_Blue   = (blue value)
//
//			- "Color" is an integer storable after retrieving colors by above methods
//
//
//		COLOR_BLEND("Color", "Another_Color", "Blend_Factor")  ex. COLOR_BLEND("udg_Integers[0]", "udg_Integers[1]", "0.4")
//			sets:
//				Color        = (storable index of the resulting color)
//				Color_Red    = (red value of the resulting color)
//				Color_Green  = (green value)
//				Color_Blue   = (blue value)
//
//			- blends "Color" and "Another_Color", creating a new color
//			  between the two based on the factor
//
//			- "Blend_Factor" range is a real within [0, 1] range
//
//			- if the factor is 0, the new color is the same as "Color"
//
//			- if the factor is 1, the new color is the same as "Another_Color"
//
//			- if the factor is 0.5, the new color is a color between the two
//
//
//		COLOR_COLORIZE("String", "Color")  ex. COLOR_COLORIZE("udg_String", "udg_Color")
//			sets:
//				String  = (colorized string)
//
//			- colorizes a string by appending corresponding color-codes to its start and end
//
//
//== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====
  • RainbowWall Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Remove this trigger because it won't run again, the rest of the code will still resolve normally --------
      • Custom script: //! runtextmacro REMOVE("RainbowWall_Init")
      • -------- The character that will be spammed --------
      • Set RainbowWall_Char = !
      • -------- Starting hue, this won't matter too much --------
      • Set RainbowWall_Hue = 0.00
      • -------- Starts the main timer --------
      • Countdown Timer - Start RainbowWall_Timer as a Repeating timer that will expire in 0.03 seconds
  • RainbowWall
    • Events
      • Time - RainbowWall_Timer expires
    • Conditions
    • Actions
      • -------- Clears the temporal variable --------
      • Set Strings[0] = <Empty String>
      • -------- Builds the text by calling the builder 3 times for different hues --------
      • Trigger - Run RainbowWall Builder <gen> (ignoring conditions)
      • Set RainbowWall_Hue = (RainbowWall_Hue + 60.00)
      • Trigger - Run RainbowWall Builder <gen> (ignoring conditions)
      • Set RainbowWall_Hue = (RainbowWall_Hue + 60.00)
      • Trigger - Run RainbowWall Builder <gen> (ignoring conditions)
      • -------- Reverts the hue, but also increments the hue a little --------
      • Set RainbowWall_Hue = (RainbowWall_Hue - 118.00)
      • -------- Display the wall! --------
      • Game - Display to (All players) the text: Strings[0]
  • RainbowWall Builder
    • Events
    • Conditions
    • Actions
      • -------- Stores the first color --------
      • Custom script: //! runtextmacro COLOR_HSL("udg_RainbowWall_Hue", "1", "0.5")
      • Set Integers[0] = Color
      • -------- Stores the end color --------
      • Custom script: //! runtextmacro COLOR_HSL("udg_RainbowWall_Hue + 60", "1", "0.5")
      • Set Integers[1] = Color
      • For each (Integer Integer) from 1 to 20, do (Actions)
        • Loop - Actions
          • -------- Sets udg_Color to a blend between the two colors, with an incrementing factor --------
          • Custom script: //! runtextmacro COLOR_BLEND("udg_Integers[0]", "udg_Integers[1]", "udg_Integer * 0.05")
          • -------- Colorizes the next character to be written --------
          • Custom script: //! runtextmacro COLOR_COLORIZE("udg_RainbowWall_Char", "udg_Color")
          • -------- Appends the colorized character to the text --------
          • Set Strings[0] = (Strings[0] + String)

As it can be seen, the use of the udg_ prefix is unavoidable. But actually, this is a good thing and should be the standard for passing udg variables. Experienced GUI users should've already known a little about this prefix, as it appears every time they're removing locations in call RemoveLocation(udg_Something). So it shouldn't be too hard for them to grasp this.

Other GUI API
While there are different ways of making a good GUI API, other kinds of API shouldn't be regarded as GvJ API, even if those API work in a GvJ environment. This is because as the name itself implies, the API must be both GUI and vJASS at the same time. It is also for standardizing the definition of a GvJ API, which is always a textmacro statement.

Conclusion
This is more of a way to organize a map's scripts as it doesn't introduce anything new. Rather, it enforces rules to bridge the difference between JASS and GUI and to standardize a clearer work-environment for GUI. Also, it must be clear that this is not aimed for speed. If anyone wants speed, go with pure vJASS.


Brought to you by Overfrost!
 

Attachments

  • BasicGvJ_mv1.0a.w3x
    30.1 KB · Views: 77
Last edited:

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,182
I am very doubtful about this tutorial.


Firstly, the title is possibly misleading. I thought it stood for GUI vs vJASS. Maybe just me, but something to consider


Secondly, from what I can see the use of text macros are not needed. Why would you wrap the destroy trigger function inside a text-macro instead of just using DestroyTrigger(GetTriggeringTrigger()) it's still one line that does not need any changes in order to work. It's also an extremely small benefit to destroy trigger for performance reasons and I don't think it showcases why to use this workflow.
Same for calling the other function showcased later in the tutorial, you can just use normal jass functions and it would not be more complicated at all.


Lastly, and perhaps most importantly. Who would use this?
In order to cash in on the benefit of jass, you need to understand it. And if you can understand jass... why would you ever go back to GUI which is objectively worse in every single metric except user-friendliness. Not to mention that these days there are other alternatives that beat jass in all metrics and simply does not work with GUI.
There are a few mad lads like Tank-Commander who knows both very well and for some reason still has most of his spells in GUI but he is an exception and he mostly uses pure GUI, not a mix.
It's kinda like swapping out the engine in your racing car for normal ones.

There are a few notable exceptions.
  • When making cinematics GUI is indeed superior for the most part and there is possibly some room to call jass functions. But for the most part, this is not needed.
  • Certain functions are unique to jass, such as setting lightning Z values and UI natives. However, for UI you would be forced to go full jass pretty much and for one-time uses like lightning Z you would swap out that one line and nothing else.
  • Some like to make a config trigger in GUI and the core system/spell in jass to make it "GUI friendly"
Unless I have misunderstood something I don't see how this can be salvaged.
You're welcome to PM/DM me whenever you are back (inactive currently) to point out how wrong I am but graveyarded until then.
 
Level 1
Joined
Apr 8, 2020
Messages
110
Although the content might not be up to par, this tutorial can still be sticked somewhere as an example of a good format for tutorials. It has just the right amount of colors in the right places, highlighting the important segments.
 
Top