• 🏆 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!

Mui

  • Like
Reactions: Kanadaj
Hi there, this time with an utility exclusively for GUIers (sorry jassers :c) which I think you gonna love it.

It's well knowed that making MUI spells in JASS is easier than GUI. That is because of the shitty process of making it MUI, instead of just worry about the spell itself.

Some people, do use the array technique; other use hashtables; and crazy ones uses waits with locals. All of these ways work fine, the problem resides in repetitive steps, and, when we are developing it, it is hard to mantain (unless you're using wait-technique, but cmom, waits! >: o)

So, what if we create another way of make MUI abilities with a little bit of dark magic wit?

This is exactly what I'm bringing. A new, ordered, elegant and extremelly easy, way of making MUI abilities in GUI (without need of jass new gen pack).

Let's see an example of an a burning ability, those that really like me c:

  • Burning
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Burning
    • Actions
      • Set set_unit[1] = (Triggering unit)
      • Set set_unit[2] = (Target unit of ability being cast)
      • Set used_units = 2
      • -------- ------------------------------------------------------------------------------------------ --------
      • Set set_real[1] = 5.00
      • Set used_reals = 1
      • -------- ------------------------------------------------------------------------------------------ --------
      • Special Effect - Create a special effect attached to the chest of (Target unit of ability being cast) using Abilities\Spells\Human\FlameStrike\FlameStrikeEmbers.mdl
      • Set set_effect[1] = (Last created special effect)
      • Set used_effects = 1
      • -------- ------------------------------------------------------------------------------------------ --------
      • Set trigger = BurningPeriodic <gen>
      • Set timeout = 1.00
Periodic trigger...

  • BurningPeriodic
    • Events
    • Conditions
    • Actions
      • -------- A second has passed, so update --------
      • Set get_real[1] = (get_real[1] - 1.00)
      • -------- ------------------------------------------------------------------------------------------ --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • get_real[1] Greater than 0.00
        • Then - Actions
          • -------- If there is still time, make damage --------
          • Unit - Cause get_unit[1] to damage get_unit[2], dealing 10.00 damage of attack type Spells and damage type Normal
        • Else - Actions
          • -------- Out of time, finish --------
          • Game - Display to (All players) the text: Finished spell
          • -------- Automatically remove leaks, like a boss --------
          • Set automaticClean = True
          • Set finish = True
Sweet, isn't?

Without need of loops (array technique), without need of loading from hashtable (hashtable technique) and without, of course, ugly waits :). This allow the final user (you), to focus completelly on his ability :).

Hey man, you have leaks in that trigger!
Do I?, this utility can handle all destructors (remove location, destroy group, etc.), so you don't have to worry about it anymore :D.

MUI (utility's name) is yet in experimental-phase. I did not test it exaustly, but it did pass some of the test that I made it.

To use it, just create a trigger called "MUI" (without quotes) and paste this, and don't forget the variables!:

JASS:
// allocator
// thanks nestharus/vexorian c:
function MUI_CreateInstance takes nothing returns integer
	local integer instance

	if ( udg_recycle == 0 ) then
		set udg_instanceCount = udg_instanceCount + 1
		set instance = udg_instanceCount
	else
		set instance = udg_recycle
		set udg_recycle = udg_recycleNext[udg_recycle]
	endif

	// reserving space
	set instance = instance * udg_limit

	return instance
endfunction

// deallocator
// thanks nestharus/vexorian c:
function MUI_RecycleInstance takes integer instance returns nothing
    set instance = instance / udg_limit

	set udg_recycleNext[instance] = udg_recycle
	set udg_recycle = instance
endfunction

function MUI_ShowOverLimitError takes nothing returns nothing
    call BJDebugMsg("No puedes guardar mas de " + I2S(udg_limit) + ". Si necesitas mas, modifica la variable limit.\nYou can't save more than " + I2S(udg_limit) + ". If you need more, modify the variable called 'limit'")
endfunction

// private
function MUI_SaveUnits takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_unit[instance + i] = udg_set_unit[i]
		set udg_set_unit[i] = null

		exitwhen i == udg_used_units
		set i = i + 1
	endloop

	set udg_mui_used_units[instance] = udg_used_units
endfunction

// private
function MUI_SaveReals takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_real[instance + i] = udg_set_real[i]
		set udg_set_real[i] = 0.

		exitwhen i == udg_used_reals
		set i = i + 1
	endloop

	set udg_mui_used_reals[instance] = udg_used_reals
endfunction

// private
function MUI_SavePlayers takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_player[instance + i] = udg_set_player[i]
		set udg_set_player[i] = null

		exitwhen i == udg_used_players
		set i = i + 1
	endloop

	set udg_mui_used_players[instance] = udg_used_players
endfunction

// private
function MUI_SavePoints takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_point[instance + i] = udg_set_point[i]
		set udg_set_point[i] = null

		exitwhen i == udg_used_points
		set i = i + 1
	endloop

	set udg_mui_used_points[instance] = udg_used_points
endfunction

// private
function MUI_SaveEffects takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_effect[instance + i] = udg_set_effect[i]
		set udg_set_effect[i] = null

		exitwhen i == udg_used_effects
		set i = i + 1
	endloop

	set udg_mui_used_effects[instance] = udg_used_effects
endfunction

// private
function MUI_SaveIntegers takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_integer[instance + i] = udg_set_integer[i]
		set udg_set_integer[i] = 0

		exitwhen i == udg_used_integers
		set i = i + 1
	endloop

	set udg_mui_used_integers[instance] = udg_used_integers
endfunction

// private
function MUI_SaveUnitGroups takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_group[instance + i] = udg_set_group[i]
		set udg_set_group[i] = null

		exitwhen i == udg_used_groups
		set i = i + 1
	endloop

	set udg_mui_used_groups[instance] = udg_used_groups
endfunction

// private
function MUI_SaveBooleans takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_boolean[instance + i] = udg_set_boolean[i]
		set udg_set_boolean[i] = false

		exitwhen i == udg_used_booleans
		set i = i + 1
	endloop

	set udg_mui_used_booleans[instance] = udg_used_booleans
endfunction

// private
function MUI_SaveDestructibles takes integer instance returns nothing
	local integer i = 1

	loop
		set udg_mui_destructible[instance + i] = udg_set_destructible[i]
		set udg_set_destructible[i] = null

		exitwhen i == udg_used_destructibles
		set i = i + 1
	endloop

	set udg_mui_used_destructibles[instance] = udg_used_destructibles
endfunction

// private
function MUI_Periodic takes nothing returns nothing
	local timer expiredTimer = GetExpiredTimer()
	local integer expiredTimerId = GetHandleId(expiredTimer)

	local integer instance = LoadInteger(udg_hashtable, expiredTimerId, 0)

	local integer i
    
    // avoid recursion problems
	local unit array prevGetUnit
	local real array prevGetReal
	local player array prevGetPlayer
	local location array prevGetPoint
	local effect array prevGetEffect
	local integer array prevGetInteger
	local group array prevGetGroup
	local boolean array prevGetBoolean
    local destructable array prevGetDestructible

	local boolean prevAutomaticClean = udg_automaticClean
	local boolean prevFinish = udg_finish

	// unit
	if ( udg_mui_used_units[instance] > 0 ) then
		set i = 1

		loop
			set prevGetUnit[i] = udg_get_unit[i]
			set udg_get_unit[i] = udg_mui_unit[instance + i]

			exitwhen i == udg_mui_used_units[instance]
			set i = i + 1
		endloop
	endif

	// real
	if ( udg_mui_used_reals[instance] > 0 ) then
		set i = 1

		loop
			set prevGetReal[i] = udg_get_real[i]
			set udg_get_real[i] = udg_mui_real[instance + i]

			exitwhen i == udg_mui_used_reals[instance]
			set i = i + 1
		endloop
	endif

	// player
	if ( udg_mui_used_players[instance] > 0 ) then
		set i = 1

		loop
			set prevGetPlayer[i] = udg_get_player[i]
			set udg_get_player[i] = udg_mui_player[instance + i]

			exitwhen i == udg_mui_used_players[instance]
			set i = i + 1
		endloop
	endif

	// location
	if ( udg_mui_used_points[instance] > 0 ) then
		set i = 1

		loop
			set prevGetPoint[i] = udg_get_point[i]
			set udg_get_point[i] = udg_mui_point[instance + i]

			exitwhen i == udg_mui_used_points[instance]
			set i = i + 1
		endloop
	endif

	// effect
	if ( udg_mui_used_effects[instance] > 0 ) then
		set i = 1

		loop
			set prevGetEffect[i] = udg_get_effect[i]
			set udg_get_effect[i] = udg_mui_effect[instance + i]

			exitwhen i == udg_mui_used_effects[instance]
			set i = i + 1
		endloop
	endif

	// integer
	if ( udg_mui_used_integers[instance] > 0 ) then
		set i = 1

		loop
			set prevGetInteger[i] = udg_get_integer[i]
			set udg_get_integer[i] = udg_mui_integer[instance + i]

			exitwhen i == udg_mui_used_integers[instance]
			set i = i + 1
		endloop
	endif

	// group
	if ( udg_mui_used_groups[instance] > 0 ) then
		set i = 1

		loop
			set prevGetGroup[i] = udg_get_group[i]
			set udg_get_group[i] = udg_mui_group[instance + i]

			exitwhen i == udg_mui_used_groups[instance]
			set i = i + 1
		endloop
	endif

	// boolean
	if ( udg_mui_used_booleans[instance] > 0 ) then
		set i = 1

		loop
			set prevGetBoolean[i] = udg_get_boolean[i]
			set udg_get_boolean[i] = udg_mui_boolean[instance + i]

			exitwhen i == udg_mui_used_booleans[instance]
			set i = i + 1
		endloop
	endif
    
    // destructible
    if ( udg_mui_used_destructibles[instance] > 0 ) then
        set i = 1

		loop
			set prevGetDestructible[i] = udg_get_destructible[i]
			set udg_get_destructible[i] = udg_mui_destructible[instance + i]

			exitwhen i == udg_mui_used_destructibles[instance]
			set i = i + 1
		endloop
    endif

	call TriggerExecute(LoadTriggerHandle(udg_hashtable, expiredTimerId, 1))

	// unit
	if ( udg_mui_used_units[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				set udg_mui_unit[instance + i] = null
			else
				set udg_mui_unit[instance + i] = udg_get_unit[i]
			endif

			set udg_get_unit[i] = prevGetUnit[i]
			set prevGetUnit[i] = null

			exitwhen i == udg_mui_used_units[instance]
			set i = i + 1
		endloop
	endif

	// real
	if ( udg_mui_used_reals[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				set udg_mui_real[instance + i] = 0.
			else
				set udg_mui_real[instance + i] = udg_get_real[i]
			endif

			set udg_get_real[i] = prevGetReal[i]

			exitwhen i == udg_mui_used_reals[instance]
			set i = i + 1
		endloop
	endif

	// player
	if ( udg_mui_used_players[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				set udg_mui_player[instance + i] = null
			else
				set udg_mui_player[instance + i] = udg_get_player[i]
			endif

			set udg_get_player[i] = prevGetPlayer[i]
			set prevGetPlayer[i] = null

			exitwhen i == udg_mui_used_players[instance]
			set i = i + 1
		endloop
	endif

	// location
	if ( udg_mui_used_points[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				if ( udg_automaticClean ) then
					call RemoveLocation(udg_mui_point[instance + i])
					call RemoveLocation(udg_get_point[i])
				endif

				set udg_mui_point[instance + i] = null
			else
				if ( udg_mui_point[instance + i] != udg_get_point[i] ) then
					call RemoveLocation(udg_mui_point[instance + i])
					set udg_mui_point[instance + i] = udg_get_point[i]
				endif
			endif

			set udg_get_point[i] = prevGetPoint[i]
			set prevGetPoint[i] = null

			exitwhen i == udg_mui_used_points[instance]
			set i = i + 1
		endloop
	endif

	// effect
	if ( udg_mui_used_effects[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				if ( udg_automaticClean ) then
					call DestroyEffect(udg_mui_effect[instance + i])
					call DestroyEffect(udg_get_effect[i])
				endif

				set udg_mui_effect[instance + i] = null
			else
				if ( udg_mui_effect[instance + i] != udg_get_effect[i] ) then
					call DestroyEffect(udg_mui_effect[instance + i])
					set udg_mui_effect[instance + i] = udg_get_effect[i]
				endif
			endif

			set udg_get_effect[i] = prevGetEffect[i]
			set prevGetEffect[i] = null

			exitwhen i == udg_mui_used_effects[instance]
			set i = i + 1
		endloop
	endif

	// integer
	if ( udg_mui_used_integers[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				set udg_mui_integer[instance + i] = 0
			else
				set udg_mui_integer[instance + i] = udg_get_integer[i]
			endif

			set udg_get_integer[i] = prevGetInteger[i]

			exitwhen i == udg_mui_used_integers[instance]
			set i = i + 1
		endloop
	endif

	// groups
	if ( udg_mui_used_groups[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				if ( udg_automaticClean ) then
					call DestroyGroup(udg_mui_group[instance + i])
					call DestroyGroup(udg_get_group[i])
				endif
				set udg_mui_group[instance + i] = null
			else
				if ( udg_mui_group[instance + i] != udg_get_group[i] ) then
					call DestroyGroup(udg_mui_group[instance + i])
					set udg_mui_group[instance + i] = udg_get_group[i]
				endif
			endif

			set udg_get_group[i] = prevGetGroup[i]
			set prevGetGroup[i] = null

			exitwhen i == udg_mui_used_groups[instance]
			set i = i + 1
		endloop
	endif

	// boolean
	if ( udg_mui_used_booleans[instance] > 0 ) then
		set i = 1

		loop
			if ( udg_finish ) then
				set udg_mui_boolean[instance + i] = false
			else
				set udg_mui_boolean[instance + i] = udg_get_boolean[i]
			endif

			set udg_get_boolean[i] = prevGetBoolean[i]

			exitwhen i == udg_mui_used_booleans[instance]
			set i = i + 1
		endloop
	endif
    
    // destructible
    if ( udg_mui_used_destructibles[instance] > 0 ) then
        set i = 1

		loop
			if ( udg_finish ) then
                if ( udg_automaticClean ) then
                    call RemoveDestructable(udg_mui_destructible[instance + i])
                    call RemoveDestructable(udg_get_destructible[instance + i])
                endif
				set udg_mui_destructible[instance + i] = null
			else
				set udg_mui_destructible[instance + i] = udg_get_destructible[i]
			endif

			set udg_get_destructible[i] = prevGetDestructible[i]

			exitwhen i == udg_mui_used_destructibles[instance]
			set i = i + 1
		endloop
    endif

	if ( udg_finish ) then
        set udg_mui_used_units[instance] = 0
        set udg_mui_used_reals[instance] = 0
        set udg_mui_used_players[instance] = 0
        set udg_mui_used_points[instance] = 0
        set udg_mui_used_effects[instance] = 0
        set udg_mui_used_integers[instance] = 0
        set udg_mui_used_groups[instance] = 0
        set udg_mui_used_booleans[instance] = 0
        set udg_mui_used_destructibles[instance] = 0
    
		call MUI_RecycleInstance(instance)
		call FlushChildHashtable(udg_hashtable, expiredTimerId)

		call PauseTimer(expiredTimer)
		call DestroyTimer(expiredTimer)
	endif

	set udg_automaticClean = prevAutomaticClean
	set udg_finish = prevFinish

	set expiredTimer = null
endfunction

// private
function MUI_OnTimeoutChange takes nothing returns nothing
	local timer periodicTimer = CreateTimer()
	local integer instance = MUI_CreateInstance()
            
    // units
	if ( udg_used_units > 0 ) then
        if ( udg_used_units > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SaveUnits(instance)
            set udg_used_units = 0
        endif
	endif
    
    // reals
	if ( udg_used_reals > 0 ) then
        if ( udg_used_reals > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SaveReals(instance)
            set udg_used_reals = 0
        endif
	endif
    
    // players
    if ( udg_used_players > 0 ) then
        if ( udg_used_players > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SavePlayers(instance)
            set udg_used_players = 0
        endif
	endif
    
    // points
    if ( udg_used_points > 0 ) then
        if ( udg_used_points > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SavePoints(instance)
            set udg_used_points = 0
        endif
	endif
    
    // effects
    if ( udg_used_effects > 0 ) then
        if ( udg_used_effects > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SaveEffects(instance)
            set udg_used_effects = 0
        endif
	endif
    
    // integers
    if ( udg_used_integers > 0 ) then
        if ( udg_used_integers > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SaveIntegers(instance)
            set udg_used_integers = 0
        endif
	endif
    
    // groups
    if ( udg_used_groups > 0 ) then
        if ( udg_used_groups > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SaveUnitGroups(instance)
            set udg_used_groups = 0
        endif
	endif
    
    // boolean
    if ( udg_used_booleans > 0 ) then
        if ( udg_used_booleans > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SaveBooleans(instance)
            set udg_used_booleans = 0
        endif
	endif
    
    // destructibles
    if ( udg_used_destructibles > 0 ) then
        if ( udg_used_destructibles > udg_limit ) then
            call MUI_ShowOverLimitError()
        else
            call MUI_SaveDestructibles(instance)
            set udg_used_destructibles = 0
        endif
    endif

	call SaveInteger(udg_hashtable, GetHandleId(periodicTimer), 0, instance)
	call SaveTriggerHandle(udg_hashtable, GetHandleId(periodicTimer), 1, udg_trigger)

	call TimerStart(periodicTimer, udg_timeout, true, function MUI_Periodic)

	set udg_timeout = 0.
	set udg_trigger = null

	set periodicTimer = null
endfunction

// initializer
function InitTrig_MUI takes nothing returns nothing
	set gg_trg_MUI = CreateTrigger()
	set udg_hashtable = InitHashtable()
	
	call TriggerRegisterVariableEvent(gg_trg_MUI, "udg_timeout", GREATER_THAN, 0)
	call TriggerAddAction(gg_trg_MUI, function MUI_OnTimeoutChange)
endfunction

How does it works?
Like array technique and hashtable, just hidding the process to the final user making it more friendly and easy to work with.

Is it efficient?
If you're really asking me this, I will say: go home my friend, you're drunk.

Can you explain me a little bit more how to save things?
It's very simple.

First, we need something to save. Let's say that we want to save a caster:

  • Set set_unit[1] = (Triggering unit)
Notice that when we save, we have to use the set_x (where x is the type of the data that we are going to save).

After we save something, we need to tell to this utility, how many space do we use. So, if we save 1 unit, we just say:

  • Set used_units = 1
If we're using 2, then we say 2, etc.


When we want to retrieve the data that we just saved, we just need to use the get_x variables (where x is the type, same as set_x).

If I saved this:

  • Set set_unit[1] = (Triggering unit)
  • Set set_unit[2] = (Target unit of ability being cast)
  • Set used_units = 2
To access to (Triggering unit), we just have to use get_unit[1]. To access (Target unit of ...) we just use unit[2]. You got it?, it depends on the order in what we save it.

Same with the other variable types (real, integer, etc.).


What is trigger?
The trigger' variable is used by this utility to know what trigger to execute when the timer expires. Before you set the timeout variable, you must set the trigger first.

What is timeout?
When you set a value to timeout (mean timeout = x) a timer will begin with the timeout of timeout (dah!). When it expires (the timer) it will execute the trigger that you set.

Important: Set timeout at the very last of the trigger.

What is automaticClean?
If you don't wan't to worry about leaks, when you finish your spell, just set it to true. If it is true, this utility will clear leaks for you :).

What is finish?
Set finish to true, when you wan't to finish the spell.

Can I use the variables called mui_x?
Please no, those are used by this utility.


Any bug, let me know ;).

Greetings.

Keywords:
mui, gui, like a boss
Contents

Just another Warcraft III map (Map)

Reviews
15th Dec 2013 Maker: Auseful system that can makes creating spells easier. The system has a bit of overhead since it checks for every type even if they are not used

Moderator

M

Moderator

15th Dec 2013
Maker: Auseful system that can makes creating spells
easier.
The system has a bit of overhead since it checks for every
type even if they are not used
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
8190, index 8191 doesn't work as expected.
EDIT:
JASS:
    call ConditionalTriggerExecute(triggerToExecute)
    -->
    if TriggerEvaluate(triggerToExecute) then
        call TriggerExecute(triggerToExecute)
    endif
This looks like the old CS_cache with a custom periodic event.
 
Level 10
Joined
Sep 19, 2011
Messages
527
Where's the loop? You should make two spells instead of one for the example because I see nothing interesting about this. How does giving stuff an array number of one/1 make stuff MUI?

there is no loop my friend :D.
well the mui crap is handled by the utility, the variables get their value depending on the order in which were saved (read read c:).

so, when the timer gets executed, the utility sets value to the variables, so the user can use them ;).

is this efficient?

yes "it is". could be more efficient?, yes it can, but it will be kinda pointless (i didn't see high fps-drops). the goal of this, is to make gui-users' life easier.

Making triggering easier like this is an example of bad habits of code or something like that someone has said before.

it is not. you can keep repeating your self with the other methods, but why?, it is a waste of time. and remember, we are in gui.

the same goes for the leak-cleaner. users still have to make use of destroy-effect, group, etc., but they don't have to make x = null, that is old-school.

This fits more as a tutorial to me.

uhmm, it is not a tutorial, i'm presenting an utility. but it should have its own tutorial.

One question... how large is the limit? 8191 normally for normal MUI stuff due to array limit. If this is hashtables then it would be 255 right? I am not sure which area this falls in, but it looks like arrays. Just curious to how large the arrays go if not to the max.

i think it would be 8190 (also check MUI_GetLimit()).
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Translate the error messages and comments to English.

More: 1st Edit
is this efficient?
No. It also limits the user to some predefined storage structure (think vjass structs).
JASS:
function MUI_GetHashtable takes nothing returns hashtable
   if ( udg_hashtable == null ) then
       set udg_hashtable = InitHashtable()
   endif
   
   return udg_hashtable
endfunction

function MUI_GetHashtable takes nothing returns hashtable
	return udg_hashtable
endfunction

function MUI_InitHashtable takes nothing returns nothing // hashtable // I overlooked this sorry.
	set udg_hashtable = InitHashtable()
endfunction
 
Last edited:
Level 10
Joined
Sep 19, 2011
Messages
527
No. It also limits the user to some predefined storage structure (think vjass structs).

it isn't efficient?, what kinda of maps/abilities do you have in mind that requires this to be more efficient?, limits?, how is that?
again, this is not intended to be efficient, but to easy things up.

hashtable: it's the same
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
it isn't efficient?, what kinda of maps/abilities do you have in mind that requires this to be more efficient?
Please don't assume people won't need* that kind of efficiency.
JASS:
function MUI_GetHashtable takes nothing returns hashtable
   if ( udg_hashtable == null ) then
       set udg_hashtable = InitHashtable()
   endif
   
   return udg_hashtable
endfunction

JASS:
function MUI_GetHashtable takes nothing returns hashtable
    return udg_hashtable
endfunction

function MUI_InitHashtable takes nothing returns nothing
    set udg_hashtable = InitHashtable()
endfunction

limits?, how is that?
Ok, I apologize, I failed to look at that part.
 
Last edited:
Aha I was doing something wrong as usual, I finally made a beam with your index system. I must say it was pretty easy and fast once I got the hang of it. Thank you making such a system yet I do want to see it handle something more complex to see how much it can handle lag-wise.

Fyi I was trying to create them at a certain spot every so tick of the timer yet it moved all of them in the timer trigger so instead I created them all at once in the execute trigger.
 
Level 10
Joined
Sep 19, 2011
Messages
527
Aha I was doing something wrong as usual, I finally made a beam with your index system. I must say it was pretty easy and fast once I got the hang of it. Thank you making such a system yet I do want to see it handle something more complex to see how much it can handle lag-wise.

Fyi I was trying to create them at a certain spot every so tick of the timer yet it moved all of them in the timer trigger so instead I created them all at once in the execute trigger.

glad to read :).
i'm making more spell-examples, hopefully it will clear questions
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
English only .....

JASS:
BJDebugMsg("MUI - No podes guardar mas de " + I2S(MUI_GetLimit()) + ". Si necesitas mas, incrementa el limite en MUI_GetLimit")

The counting key functions are pointless.

I don't see how this is better than learning indexed arrays. Since indexed arrays are much faster. OR learning MUI hashtables. It is not hard at all.

It is better if you put more notes in your work.
It seems like you create multiple hashtables for each different spell.
A true MUI system would take advantage of one spell.

I would suggest a complete rework or scrapping it.

You should really explain your work better. Also anything submitted should be as efficient as possible.

Since this handles spells. It should use coordinates not locations. Creating and destroying locations is very inefficient compared to using coordinates.
 
Level 10
Joined
Sep 19, 2011
Messages
527
The counting key functions are pointless.

why?

I don't see how this is better than learning indexed arrays. Since indexed arrays are much faster. OR learning MUI hashtables. It is not hard at all.

if you like to repeat yourself, you're welcome to do it.

It is better if you put more notes in your work.
It seems like you create multiple hashtables for each different spell.
A true MUI system would take advantage of one spell.

1 hashtable

at the begining i didn't know if this were to work, so it's kinda improvised (that is why the lack of comments, although names are quite self-explanatory).

You should really explain your work better. Also anything submitted should be as efficient as possible.

oh, but it is efficient... on real/most-scenarios c:

i dont know if i can explain it better :\, i mean, i put 3 examples (i have to upload one more that was donated by Dat-C3, thanks by the way).

Since this handles spells. It should use coordinates not locations. Creating and destroying locations is very inefficient compared to using coordinates.

are you serius?, are you concern about creating/destroying locations?, omg.

this is mented to make gui-life' easier. is it efficient?, on what scenarios?, crazy ones?, of course not.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
1) They are pointless because they return the same number so just simply use that number in the functions rather than create unnecessary function calls.

2) That was pointless and ignorant. What i said is valid.

3) Then put in comments after.

4) If it is not efficient in all scenarios then it is not efficient.

5) Yes as creating and destroying locations is very inefficient.
You can simply take a location using a function and change it to coordinates. That way every time those coordinates are used it is more efficient.
If it's not efficient it most likely will not be approved.
 
Level 10
Joined
Sep 19, 2011
Messages
527
1) They are pointless because they return the same number so just simply use that number in the functions rather than create unnecessary function calls.

oh, you're refering to the periodic function, yes they're useless. their value can be saved in variables.

2) That was pointless and ignorant. What i said is valid.

nu, take a look for example at spells who make use of the array technique. what do you see?, i can see repeating stuff.

3) Then put in comments after.

woah+dude+calm+down+_e5deda70bd2d9fce029281f6819c9e8c.jpg

dude, calm down

4) If it is not efficient in all scenarios then it is not efficient.

breath, and read what you're saying.

5) Yes as creating and destroying locations is very inefficient.
You can simply take a location using a function and change it to coordinates. That way every time those coordinates are used it is more efficient.

yes, i could do that. but first, is it really necessary?, i think it is not.

If it's not efficient it most likely will not be approved.

efficiency is not everything. have faith.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
1) they dont need to be saved as variables. Simply use them in the functions.

2) I make spells using indexed arrays. They are a lot more efficient than this. Im not sure what you mean by repeated stuff ?

3) I was just saying lol.

4) I know what im saying.

5) Yes it is as you are coding in jass not in GUI.

6) Efficiency plays a big role in all systems. All systems should be as efficient as possible.
 
Level 10
Joined
Sep 19, 2011
Messages
527
2) I make spells using indexed arrays. They are a lot more efficient than this. Im not sure what you mean by repeated stuff ?

efficient is not the point, but the ease (less time doing something, gives you more time to do other), increse productivity of the user (it is easy!) and increase scalability maintainability (easy to read, easy to maintain). but (there is always a but) with this, you have to sacrifice something, and that is efficiency. you lose a little bit of that but you're gaining other stuff (which i think they are more important, dont you think so?).

3) I was just saying lol.

okey, we're cool. now give me a hug c:

4) I know what im saying.

being not efficient in x scenarios doesn't make the hole utility un-efficient. but if you're so concern about that i will take a closer look at it.

5) Yes it is as you are coding in jass not in GUI.

i read your idea again... i think i don't follow it.

i mean, took a location from gui -> pass it to real -> what to do with the location? -> save the real -> when the user requires his location -> pass a real? -> user gets mad

i think locations are more easy to use in gui than coordinates.

6) Efficiency plays a big role in all systems. All systems should be as efficient as possible.

an example: your system works awesome in every scenario where it will be used. you know that you can improve it to make it work more efficient, but why?, users will no see any diference and it is not required! (cuz it does his job, and it does pretty well :) ).


Though he should be using coordinates. Check out my beam example using his indexer, it starts to lag at 12+ instances casted at the same time

it does?, uhmm i will check it later.

edit: i check the spell... think is the spell not this utility, i mean you're creating 50 units everytime you cast it and it have group leaks :\
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
These should be constant functions:

JASS:
function MUI_GetLimit takes nothing returns integer
    return 200
endfunction

// private
function MUI_GetPointCountingKey takes nothing returns integer
    return -8
endfunction

// private
function MUI_GetEffectCountingKey takes nothing returns integer
    return -7
endfunction

// private
function MUI_GetRealCountingKey takes nothing returns integer
    return -6
endfunction

// private
function MUI_GetPlayerCountingKey takes nothing returns integer
    return -5
endfunction

// private
function MUI_GetIntegerCountingKey takes nothing returns integer
    return -4
endfunction

// private
function MUI_GetUnitGroupCountingKey takes nothing returns integer
    return -3
endfunction

// private
function MUI_GetBooleanCountingKey takes nothing returns integer
    return -2
endfunction

// private
function MUI_GetUnitCountingKey takes nothing returns integer
    return -1
endfunction

// private
function MUI_GetPointStartNumber takes nothing returns integer
    return MUI_GetLimit() * 7
endfunction

// private
function MUI_GetEffectStartNumber takes nothing returns integer
    return MUI_GetLimit() * 6
endfunction

// private
function MUI_GetRealStartNumber takes nothing returns integer
    return MUI_GetLimit() * 5
endfunction

// private
function MUI_GetPlayerStartNumber takes nothing returns integer
    return MUI_GetLimit() * 4
endfunction

// private
function MUI_GetIntegerStartNumber takes nothing returns integer
    return MUI_GetLimit() * 3
endfunction

// private
function MUI_GetUnitGroupStartNumber takes nothing returns integer
    return MUI_GetLimit() * 2
endfunction

// private
function MUI_GetBooleanStartNumber takes nothing returns integer
    return MUI_GetLimit() * 1
endfunction

// private
function MUI_GetUnitStartNumber takes nothing returns integer
    return 0
endfunction

Oh, and the debug message should be in English.
 
Last edited:
Level 16
Joined
Jul 31, 2012
Messages
2,217
I'm going to spare my review table for this one, when i first saw the trigger script .... it was awful, the really really long trigger is like 4x larger than normal MUI, we, coders do! And, for instance, i was completely Lost in it
I'll stand with the first comments and say: make a Tutorial, attach a demo map to the thread, it would be extremely better
 
Level 10
Joined
Sep 19, 2011
Messages
527
These should be constant functions:

yep, i will add it later, thanks.
also i will translate the messages too.

when i first saw the trigger script .... it was awful, the really really long trigger is like 4x larger than normal MUI, we, coders do! And, for instance, i was completely Lost in it

because of the comments. remove them, and you will get a nice/clean code.

I'll stand with the first comments and say: make a Tutorial, attach a demo map to the thread, it would be extremely better

demo map is already attached.
tutorial... uhmm... i will wait to see this approved.

You think its the group leaks? I think it is the costly use of the GUI move unit instantly. It might of handled around 20 beams otherwise.

is for the massive unit creation.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
Like said earlier something like this is bad coding practice. It is also inefficient and the TO doesn't seem to want to change anything to make it better.

I made something like this a while back but with some major differences. The spells were saved by the triggers handle id. Then each instance was increased for that certain spell. I then used a timer event loop to keep track of when the instance should end so there is no unecessary looping. It was still slow because it used hashtables and I never submitted it because it is simply bad practice.
 
Level 10
Joined
Sep 19, 2011
Messages
527
i did say it was very long, not that it wasn't clean

oh you're right, sorry about that.

Also my spells took me about 15 minutes with indexed arrays. So this won't save any time.

that's fine, but i can't agree that this doesn't save any time, have you try it?.

Hashtables are up to 8 times slower than indexed arrays.

benchmark please.

even if that is true, players won't notice that (at least you were doing some crazy stuffs).

edit:
if they do, then i will work in efficiency.

-------------------------------------

took a random spell to compare, these are the results

http://image.gxzone.com/images/6/a/6a4a8ca827c.jpg

(with = with this utility)
(without = array technique)

credits for the spell goes to: Napo from http://worldofeditors.forosweb.net/t355-waveform-por-napo-spell

edit:
Also how do you follow which unit belongs to what spell ?

you save it.

Like said earlier something like this is bad coding practice. It is also inefficient and the TO doesn't seem to want to change anything to make it better.

i will improve it, but not in areas where it does not need to.

I made something like this a while back but with some major differences. The spells were saved by the triggers handle id. Then each instance was increased for that certain spell. I then used a timer event loop to keep track of when the instance should end so there is no unecessary looping. It was still slow because it used hashtables and I never submitted it because it is simply bad practice.

it is not bad practice, it's just sugar. it simplifies the process to the final user, how could be that a bad practice?.

edit: just for the record, why are you saying that this is bad practice?
 
Last edited:
Level 29
Joined
Oct 24, 2012
Messages
6,543
This type of thing is bad practice because it is doesn't show the right way on how spells should be made.

Ask bribe for his benchmark. He is the one that found this out.

Quote:
Also how do you follow which unit belongs to what spell ?
you save it.

When someone asks something like that normally you show them where in the code it does that not just say you save it.

I don't code in GUI anymore. This won't save time. Also my spells are complex you should look before saying they are simple.

The spell you showed is also inefficient and very bad to.
That spell uses an old style of indexing and de-indexing. The old style runs through indexes that are already used. It also has more lines of code than the new style.

In your periodic function you also leak handles.
 
Level 10
Joined
Sep 19, 2011
Messages
527
This type of thing is bad practice because it is doesn't show the right way on how spells should be made.

simply, no.

Ask bribe for his benchmark. He is the one that found this out.

. . .

When someone asks something like that normally you show them where in the code it does that not just say you save it.

the utility is like structs from vjass but to gui.

you init the "process" with MUI_Start(timeout).

MUI_Start it's like struct.create(), it gives you an instance where you can save your data (unit, real, etc.). also, it starts a timer with the timeout that has been passed.

that timer, holds the instance (the one that we just created with MUI_Start), so when it expires, we're able to retrieve the data.

when the data is retrieved, it is assigned to the variables so that the user can use them.

MUI_End it is like struct.destroy, it recycles the instance and clear things up.

the saving process is:

instance, amount_of_this_type_of_data_saved, data

where amount_of_this_type_of_data_saved is a counting which increases every time the user saves something of that same type.

example:

call MUI_Unit(unit)

instance, 1, unit

call MUI_Unit(unit)

instance, 2 (here, the counter has been increased), unit

if a say:

call MUI_Real(real)

instance, 1, real

why 1?, because it's the first of this type

but, why it doesn't collide?

because of MUI_GetStartXNumber, where X it's the type.

so, if unit begins at 0, the counter will start at that number.
if reals begins at 1000, counter will start at that number.

call MUI_Real(real)

instance, MUI_GetRealStartNumber + counter, real
 
Level 10
Joined
Sep 19, 2011
Messages
527
I don't code in GUI anymore. This won't save time.

i was talking about if you did try this utility. if you didn't, you can't say if this does or not saves time.

The spell you showed is also inefficient and very bad to.
That spell uses an old style of indexing and de-indexing. The old style runs through indexes that are already used. It also has more lines of code than the new style.

took a random spell to compare

if you have other, please share.

In your periodic function you also leak handles.

where?

you mean for the points?, the utility handles it.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
I know how structs work i only code in jass and vJass. Try one of my spells with this system.

All local handles must be nulled when assigned a value. You barely null half of them.

Also as previously stated get rid of all the function calls that aren't needed. If you are only using one hashtable. Why would you create extra handles (that you are leaking btw) to store the hashtable if you already know the hashtables name...
Same with a ton of the other function calls.
 
Level 10
Joined
Sep 19, 2011
Messages
527
Also as previously stated get rid of all the function calls that aren't needed.

they're needed to avoid the user of making an initializator.

Why would you create extra handles (that you are leaking btw) to store the hashtable if you already know the hashtables name...

i'm not creating extra handles, i'm working with only 1 hashtable. if the hashtable has been created, return it, if not, create it, and then return it.

it doesn't leak, because the hashtable is never destroyed.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
See this...
JASS:
local hashtable hashTable = MUI_GetHashtable()

that line that has local in front of it creates a local handle. It is then used and it now points to the global variable.

Every local created will be stuck in memory unless it is released. It is only released if it is null by the end of the function.

Please read more on how to code in jass / vJass

Also creating locals as i have already said is pointless in this case. Simply use the global variables for it.

EVERY local handle must be nulled.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
There's actually no leak dude, the handle is never destroyed, it will never be recycled so the local handles will always point to a single handle instance. But the amount of function calls should be reduced if possible.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
There's actually no leak dude, the handle is never destroyed, it will never be recycled so the local handles will always point to a single handle instance. But the amount of function calls should be reduced if possible.

local variables are handled wrong in wc3. No1 knows why but local variables that are handled have to be nulled or it leaks.
 
Level 9
Joined
Dec 12, 2007
Messages
489
I suggest you to remove the remaining BJ in your script, as you can see:
JASS:
function ConditionalTriggerExecute takes trigger trig returns nothing
    if TriggerEvaluate(trig) then
        call TriggerExecute(trig)
    endif
endfunction

and I suggest you to inline those MUI_GetHashtable() directly to the hashtable itself rather than using senseless wrapper, why senseless?
consider that MUI_GetHashtable() will:
1. always return the same hashtable
2. always out of user reach as it is system use
3. always initiated first time and never be initiated again
4. never be destroyed
so why always check if its exist everytime it is called?

rather than chobibo's method to change this:
JASS:
function MUI_GetHashtable takes nothing returns hashtable
   if ( udg_hashtable == null ) then
       set udg_hashtable = InitHashtable()
   endif
   
   return udg_hashtable
endfunction
into
JASS:
function MUI_GetHashtable takes nothing returns hashtable
    return udg_hashtable
endfunction

function MUI_InitHashtable takes nothing returns nothing
    set udg_hashtable = InitHashtable()
endfunction
I suggest you to do this:
JASS:
function MUI_InitHashtable takes nothing returns nothing
    set udg_hashtable = InitHashtable()
endfunction
  • MapInit
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call MUI_InitHashtable()
and use the hashtable directly like this:
JASS:
function MUI_SetRecycleInstance takes integer instance returns nothing
    call SaveInteger(udg_hashtable, 0, 2, instance)
endfunction
it saves one function call and one if check almost everywhere in your script as any function in your script have MUI_GetHashtable() in it.

and yes, local handle will have pointer leak if not nulled.
and why use local on this?
local hashtable hashTable = MUI_GetHashtable()
use the udg_hashtable directly.

add support for destructable, although its rarely used.

why give the option to remove leak or not in MUI_End? the only case where it will be false is only when the user passes a global variable which is used in the map which is a bad practice anyway.
 
Last edited:
Level 29
Joined
Oct 24, 2012
Messages
6,543
I suggest you to remove the remaining BJ in your script, as you can see:
JASS:
function ConditionalTriggerExecute takes trigger trig returns nothing
    if TriggerEvaluate(trig) then
        call TriggerExecute(trig)
    endif
endfunction

and I suggest you to inline those MUI_GetHashtable() directly to the hashtable itself rather than using senseless wrapper, why senseless?
consider that MUI_GetHashtable() will:
1. always return the same hashtable
2. always out of user reach as it is system use
3. always initiated first time and never be initiated again
4. never be destroyed
so why always check if its exist everytime it is called?

rather than chobibo's method to change this:
JASS:
function MUI_GetHashtable takes nothing returns hashtable
   if ( udg_hashtable == null ) then
       set udg_hashtable = InitHashtable()
   endif
   
   return udg_hashtable
endfunction
into
JASS:
function MUI_GetHashtable takes nothing returns hashtable
    return udg_hashtable
endfunction

function MUI_InitHashtable takes nothing returns nothing
    set udg_hashtable = InitHashtable()
endfunction
I suggest you to do this:
JASS:
function MUI_InitHashtable takes nothing returns nothing
    set udg_hashtable = InitHashtable()
endfunction
  • MapInit
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call MUI_InitHashtable()
and use the hashtable directly like this:
JASS:
function MUI_SetRecycleInstance takes integer instance returns nothing
    call SaveInteger(udg_hashtable, 0, 2, instance)
endfunction
it saves one function call and one if check almost everywhere in your script as any function in your script have MUI_GetHashtable() in it.

and yes, local handle will have pointer leak if not nulled.
and why use local on this?
local hashtable hashTable = MUI_GetHashtable()
use the udg_hashtable directly.

add support for destructable, although its rarely used.

why give the option to remove leak or not in MUI_End? the only case where it will be false is only when the user passes a global variable which is used in the map which is a bad practice anyway.

That is what i've been saying for a lot of posts lol. Thanks

The last thing i believe is necessary as some spells may have several steps. If it destroys something before all steps are completed it will cause crash.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
local variables are handled wrong in wc3.
Ah, yes, I overlooked the use, since it is being used periodically it does indeed leak references.
No1 knows why but local variables that are handled have to be nulled or it leaks.
Actually, Pipedream explained why, hava a look here.
 
Top