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

Group Control

Ever wondered if it would be somehow possible to command more than 12 units at a time?
Well, now it's possible.

This resource actually contains of 2 resources + requirements.
The first resource is Group Control, which provides a standardized way to have groups of minions.
The other is GroupOrder, which makes each unit share commands to its minions when possible and enabled.

The testmap also contains Bribe's unit indexer, although any other unit indexer would work too.

How to import:
1. Make sure "Create unknown variables" is enabled in world editor settings.
2. Copy all triggers to your map.
3. Make sure that the variables in GCConfig point to the right abilities.
Now it should work.

How to use:
1. Select the commander and only the commander.
2. Recruit units with the accordingly named ability.
3. Turn on formations to make units remember their current positions relative to commander.
4. Give orders to the commander and each unit will try to perform the same orders.
5. Turn unit moving on/off to move the commander separately from units.

Some notes:
All point orders are given in relative coordinates. If the commander casts something 500 to the right from him, then each unit will try to cast the same thing to the 500 right from his own position.
All target orders are simply shared. Works well for clumping and also for focusing on one enemy at a time.
Targetless orders are also shared.
If a commander is given a command that the units can't perform, then they will simply ignore it. The commander will still perform it though.
Creating formations is too automatic at the moment. I believe someone might want units to move as a mass for some time and then immediately switch to a formation. Currently this can not be done.
Giving the defend command to commander will make all units stop, but not the commander itself. This is an issue with the game itself. (How to fix?)

It is highly likely that this system makes my previous system, CommanderSystem, pointless. However, technically this system here is much different, so simply updating CommanderSystem wouldn't be right.

Feedback is encouraged, as I wish to make this resource better.
Credits can be given if you deem it right, but it is not absolutely necessary.

Code:
JASS:
//
//              Group Control
//                by Xonok
//
//          Provides an API for making each unit have a group of owned units. 
//          Owned units can be added/removed with functions
//          Dead units are removed automatically, their groups emptied. 
//          

library GroupControl initializer InitTri_GroupControl
    //+ System globals. Don't use elsewhere
    globals
        private trigger DismissTrig = CreateTrigger()
        private trigger RecruitTrig = CreateTrigger()
        private trigger FormationTrig = CreateTrigger()
        private trigger SelectTrig = CreateTrigger()
        private trigger DeselectTrig = CreateTrigger()
        private trigger OrderTrig = CreateTrigger()
        private trigger OrderSwapTrig = CreateTrigger()
        private trigger UnitDieTrig = CreateTrigger()
        private unit u
        private unit caster
        private unit owner
        private player p
        private integer ID
        private integer ID2
        real array FormationX
        real array FormationY
        private string s
    endglobals
    //-
    
    //+ Public functions. Use them however you wish
    function ShowOwnageMarker takes unit u, boolean b returns nothing
        set s = ""
        set ID = GetUnitUserData(u)
        if udg_OwnageMarker[ID] != null then
            call DestroyEffect(udg_OwnageMarker[ID])
            set udg_OwnageMarker[ID] = null
        endif
        if b then
            if GetLocalPlayer() == GetOwningPlayer(u) then
                set s = "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
            endif
            set udg_OwnageMarker[ID] = AddSpecialEffectTarget(s,u,"overhead")
        endif
        set s = ""
    endfunction
    function DismissUnit takes unit u returns nothing
        set ID = GetUnitUserData(u)
        set owner = udg_UnitOwner[ID]
        set ID2 = GetUnitUserData(owner)
        call GroupRemoveUnit(udg_OwnedUnits[ID2],u)
        set udg_UnitOwner[ID] = null
        call DestroyEffect(udg_OwnageMarker[ID])
        set udg_OwnageMarker[ID] = null
        set udg_OwnedUnitCount[ID2] = udg_OwnedUnitCount[ID2] - 1
    endfunction
    function RecruitUnit takes unit u, unit commander returns nothing
        set ID = GetUnitUserData(u)
        set ID2 = GetUnitUserData(commander)
        if udg_UnitOwner[ID] == null then
            if udg_OwnedUnits[ID2] == null then
                set udg_OwnedUnits[ID2] = CreateGroup()
            endif
            call GroupAddUnit(udg_OwnedUnits[ID2],u)
            set udg_UnitOwner[ID] = commander
            if IsUnitSelected(commander,GetOwningPlayer(commander)) then
                call ShowOwnageMarker(u,true)
                //set udg_OwnageMarker[ID] = AddSpecialEffectTarget("Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl",u,"overhead")
            endif
            set udg_OwnedUnitCount[ID2] = udg_OwnedUnitCount[ID2] + 1
        endif
    endfunction
    //-
    
    //+ System functions. Don't use elsewhere
    private function Dismiss takes nothing returns boolean
        if GetSpellAbilityId() == udg_DismissAbil then
            set u = GetSpellTargetUnit()
            call DismissUnit(u)
        endif
        return false
    endfunction
    private function Recruit takes nothing returns boolean
        if GetSpellAbilityId() == udg_RecruitAbil then
            set u = GetSpellTargetUnit()
            set caster = GetTriggerUnit()
            call RecruitUnit(u,caster)
        endif
        return false
    endfunction
    private function SaveCoords takes nothing returns nothing
        set u = GetEnumUnit()
        set ID2 = GetUnitUserData(u)
        set FormationX[ID2] = GetUnitX(u) - GetUnitX(owner)
        set FormationY[ID2] = GetUnitY(u) - GetUnitY(owner)
    endfunction
    private function Formation takes nothing returns boolean
        if GetSpellAbilityId() == udg_FormationOFFAbil then
            set owner = GetTriggerUnit()
            set ID = GetUnitUserData(owner)
            call ForGroup(udg_OwnedUnits[ID],function SaveCoords)
        elseif GetSpellAbilityId() == udg_FormationONAbil then
        endif
        return false
    endfunction
    private function GroupSelect takes nothing returns nothing
        call ShowOwnageMarker(GetEnumUnit(),true)
    endfunction
    private function GroupDeselect takes nothing returns nothing
        call ShowOwnageMarker(GetEnumUnit(),false)
    endfunction
    private function Select takes nothing returns boolean
        set u = GetTriggerUnit()
        if GetTriggerPlayer() == GetOwningPlayer(u) then
            set ID = GetUnitUserData(u)
            call ForGroup(udg_OwnedUnits[ID],function GroupSelect)
            //call ShowOwnageMarker(u,true)
        endif
        return false
    endfunction
    private function Deselect takes nothing returns boolean
        set u = GetTriggerUnit()
        if GetTriggerPlayer() == GetOwningPlayer(u) then
            set ID = GetUnitUserData(u)
            call ForGroup(udg_OwnedUnits[ID],function GroupDeselect)
            //call ShowOwnageMarker(u,false)
        endif
        return false
    endfunction
    private function CommanderDeath takes nothing returns nothing
        call DismissUnit(GetEnumUnit())
    endfunction
    private function UnitDie takes nothing returns boolean
        set u = GetTriggerUnit()
        set ID = GetUnitUserData(u)
        if udg_UnitOwner[ID] != null then
            set owner = udg_UnitOwner[ID]
            set ID2 = GetUnitUserData(owner)
            call GroupRemoveUnit(udg_OwnedUnits[ID2],u)
            set udg_OwnedUnitCount[ID2] = udg_OwnedUnitCount[ID2] - 1
            set udg_UnitOwner[ID] = null
            call DestroyEffect(udg_OwnageMarker[ID])
            set udg_OwnageMarker[ID] = null
        endif
        if udg_OwnedUnitCount[ID] > 0 then
            set owner = u
            set ID2 = ID
            call ForGroup(udg_OwnedUnits[ID2],function CommanderDeath)
        endif
        return false
    endfunction
    //-
    
    //++
    function InitTri_GroupControl takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > 15
            call TriggerRegisterPlayerUnitEvent(DismissTrig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            call TriggerRegisterPlayerUnitEvent(RecruitTrig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            call TriggerRegisterPlayerUnitEvent(FormationTrig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            call TriggerRegisterPlayerUnitEvent(SelectTrig,Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
            call TriggerRegisterPlayerUnitEvent(DeselectTrig,Player(i),EVENT_PLAYER_UNIT_DESELECTED, null)
            call TriggerRegisterPlayerUnitEvent(UnitDieTrig,Player(i),EVENT_PLAYER_UNIT_DEATH, null)
            set i = i + 1
        endloop
        call TriggerAddCondition(DismissTrig,Filter(function Dismiss))
        call TriggerAddCondition(RecruitTrig,Filter(function Recruit))
        call TriggerAddCondition(FormationTrig,Filter(function Formation))
        call TriggerAddCondition(SelectTrig,Filter(function Select))
        call TriggerAddCondition(DeselectTrig,Filter(function Deselect))
        call TriggerAddCondition(UnitDieTrig,Filter(function UnitDie))
    endfunction
endlibrary
JASS:
//
//              GroupOrder
//               by Xonok
//
//          This system provides the functionality to give orders to groups, 
//          through simple functions. 
//          This system requires GroupControl,
//          in order to access udg_OwnedUnits[]
//
//          GroupOrderTarget(group,order,target) -> nothing
//          |
//          Issues an order to all units in the group. 
//          The order can target any widget(unit,tree,etc). 
//         
//          GroupOrderPoint(group,order,x,y) -> nothing
//          |
//          Issues an order to all units in the group.
//          The provided coordinates are treated as absolute. 
//
//          GroupOrderOffset(group,order,x,y) -> nothing
//          |
//          Issues an order to all units in the group.
//          The provided coordinates are treated as relative to the unit.
//
//          GroupOrder(group,order) -> nothing
//          |
//          Issues an untargeted order to all units in the group
//          
//          Invalid orders don't do anything (courtesy of blizzard)
//

library GroupOrder initializer InitTri_GroupOrder requires GroupControl
    globals
        private integer ID
        private integer ID2
        private real x
        private real y
        private real x2
        private real y2
        private real x3
        private real y3
        private real RefX
        private real RefY
        private unit caster
        private unit u
        private integer order
        private widget target
    endglobals
    
    //+ System functions. Not for using elsewhere
    private function TargetGroupOrder takes nothing returns nothing
        call IssueTargetOrderById(GetEnumUnit(),order,target)
    endfunction
    private function PointGroupOrder takes nothing returns nothing
        set u = GetEnumUnit()
        call IssuePointOrderById(u,order,x,y)
    endfunction
    private function OffsetGroupOrder takes nothing returns nothing
        set u = GetEnumUnit()
        call IssuePointOrderById(u,order,GetUnitX(u)+x,GetUnitY(u)+y)
    endfunction
    private function FormationGroupOrder takes nothing returns nothing
        set u = GetEnumUnit()
        set ID2 = GetUnitUserData(u)
        call IssuePointOrderById(u,order,x+FormationX[ID2],y+FormationY[ID2])
    endfunction
    private function OrderGroup takes nothing returns nothing
        call IssueImmediateOrderById(GetEnumUnit(),order)
    endfunction
    //-
    
    //+ Public functions. Use however you wish
    function GroupOrderTarget takes group g,integer order2,widget target2 returns nothing
        set order = order2
        set target = target2
        call ForGroup(g,function TargetGroupOrder)
    endfunction
    function GroupOrderPoint takes group g,integer order2,real x1,real y1 returns nothing
        set order = order2
        set x = x1
        set y = y1
        call ForGroup(g,function PointGroupOrder)
    endfunction
    function GroupOrderOffset takes group g, integer order2,real x1,real y1 returns nothing
        set order = order2
        set x = x1
        set y = y1
        call ForGroup(g,function OffsetGroupOrder)
    endfunction
    function GroupOrderFormation takes group g,integer order2,real x1,real y1 returns nothing
        set order = order2
        set x = x1
        set y = y1
        call ForGroup(g,function FormationGroupOrder)
    endfunction
    function GroupOrder takes group g, integer order2 returns nothing
        set order = order2
        call ForGroup(g,function OrderGroup)
    endfunction
    //-
    
    //+ System function. Must stay down here, as it references public functions. 
    private function Order takes nothing returns boolean
        if GetUnitAbilityLevel(GetTriggerUnit(),udg_OrderONAbil) > 0 then
            set caster = GetTriggerUnit()
            set order = GetIssuedOrderId()
            set ID = GetUnitUserData(caster)
            if GetOrderTarget() != null then
                set target = GetOrderTarget()
                call GroupOrderTarget(udg_OwnedUnits[ID],order,target)
            elseif GetOrderPointX() != null then
                set x2 = GetUnitX(caster)
                set y2 = GetUnitY(caster)
                set x3 = GetOrderPointX()
                set y3 = GetOrderPointY()
                if GetUnitAbilityLevel(caster,udg_FormationONAbil) > 0 then
                    call GroupOrderFormation(udg_OwnedUnits[ID],order,x3,y3)
                else
                    call GroupOrderOffset(udg_OwnedUnits[ID],order,x3-x2,y3-y2)
                endif
            else
                call GroupOrder(udg_OwnedUnits[ID],order)
            endif
        endif
        return false
    endfunction

    //++
    function InitTri_GroupOrder takes nothing returns nothing
        local integer i = 0
        set gg_trg_GroupOrder = CreateTrigger(  )
        loop
            exitwhen i > 15
            call TriggerRegisterPlayerUnitEvent(gg_trg_GroupOrder,Player(i),EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER,null)
            call TriggerRegisterPlayerUnitEvent(gg_trg_GroupOrder,Player(i),EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER,null)
            call TriggerRegisterPlayerUnitEvent(gg_trg_GroupOrder,Player(i),EVENT_PLAYER_UNIT_ISSUED_ORDER,null)
            set i = i + 1
        endloop
        call TriggerAddCondition( gg_trg_GroupOrder, function Order )
    endfunction
endlibrary

Configuration can be done in GUI:
  • GCConf
    • Events
      • Time - Elapsed game time is 2.30 seconds
    • Conditions
    • Actions
      • Set DismissAbil = Dismiss Unit
      • Set FormationONAbil = Formation:ON
      • Set FormationOFFAbil = Formation:OFF
      • Set OrderOFFAbil = Orders:OFF
      • Set OrderONAbil = Orders:ON
      • Set RecruitAbil = Recruit Unit
      • -------- You can delete everything after this point once the system is imported. --------
      • -------- It is only here to make sure that all required variables get created. --------
      • Set OwnageMarker[0] = (Last created special effect)
      • Set OwnedUnitCount[0] = 0
      • Set OwnedUnits[0] = (Last created unit group)
      • Set UnitOwner[0] = No unit
Recommended way of turning group orders and formations on/off for specific commanders:
  • OrderSwap
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
    • Actions
      • -------- Example on how you can possibly enable/disable group orders --------
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Ability being cast) Equal to (==) OrderOFFAbil
          • Then - Actions
            • Unit - Remove OrderOFFAbil from (Triggering unit)
            • Unit - Add OrderONAbil to (Triggering unit)
            • Special Effect - Create a special effect attached to the origin of (Triggering unit) using Abilities\Spells\NightElf\BattleRoar\RoarCaster.mdl
            • Special Effect - Destroy (Last created special effect)
          • Else - Actions
            • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
              • If - Conditions
                • (Ability being cast) Equal to (==) OrderONAbil
              • Then - Actions
                • Unit - Remove OrderONAbil from (Triggering unit)
                • Unit - Add OrderOFFAbil to (Triggering unit)
                • Special Effect - Create a special effect attached to the origin of (Triggering unit) using Abilities\Spells\NightElf\Taunt\TauntCaster.mdl
                • Special Effect - Destroy (Last created special effect)
              • Else - Actions
                • -------- Same with formations --------
                  • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Ability being cast) Equal to (==) FormationOFFAbil
                    • Then - Actions
                      • Unit - Remove FormationOFFAbil from (Triggering unit)
                      • Unit - Add FormationONAbil to (Triggering unit)
                    • Else - Actions
                      • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (Ability being cast) Equal to (==) FormationONAbil
                        • Then - Actions
                          • Unit - Remove FormationONAbil from (Triggering unit)
                          • Unit - Add FormationOFFAbil to (Triggering unit)
                        • Else - Actions
Contents

Group Control v1.04 (Map)

Reviews
12:22, 6th Dec 2013 Maker: A nice system though it takes a lot of space in command card. You could consider a spell book version and automatic formations

Moderator

M

Moderator

12:22, 6th Dec 2013
Maker: A nice system though it takes a lot of space in
command card.
You could consider a spell book version and automatic formations
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
It cannot be tested yet, since you have an InitTrig function inside your library.
Also I've already looked into the code and saw some issues. Fix the testmap first, then I point out them out. :)

Edit: Attached a screenshot. And yes I have the very lastest JNPG.
 

Attachments

  • Error.jpg
    Error.jpg
    536.4 KB · Views: 254
Level 19
Joined
Mar 18, 2012
Messages
1,716
On a quick review:
  • Use TriggerRegisterAnyUnitEventBJ instead of call TriggerRegisterPlayerUnitEvent(...) its exactly the same
  • At the moment you call TriggerAddCondition(DismissTrig,Filter(function Dismiss)) 15 times to the corresponding trigger, one time would be enough though. Same goes for the other conditions.
  • set s = "" after showing the SpecialEffect is not needed.
  • I strongly recomment you to use local variables, instead of for example private unit u
  • Stuff like effects should be nulled when your done with them call DestroyEffect(udg_OwnageMarker[ID])
  • After all you hardly null anything oO
Now to GroupOrder
  • Again use local variables
  • Use the bj instead of TriggerRegisterPlayerUnitEvent

The concept itself is interresting, but has some issues to me at its current state:
  • Not every unit can be a captain, it requires 3 abilities atm, which is quite alot
  • It would be nice to distinguish between orders like attack and move and spells. One may be interrested in moving all units at once, but not casting 20 spells simultaneously.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
On a quick review:
  • Use TriggerRegisterAnyUnitEventBJ instead of call TriggerRegisterPlayerUnitEvent(...) its exactly the same - The reason I use TriggerRegisterPlayerUnitEvent is that doing multiple loops for every trigger is not as good as just doing 1 loop with those.
  • At the moment you call TriggerAddCondition(DismissTrig,Filter(function Dismiss)) 15 times to the corresponding trigger, one time would be enough though. Same goes for the other conditions. - Good point, fixed
  • set s = "" after showing the SpecialEffect is not needed. - I do this to be absolutely sure that it doesn't desync
  • I strongly recomment you to use local variables, instead of for example private unit u - Passing local variables every time is pointlessly inefficient. The way I did it works much better, as I don't constantly create new variables and only set values when they are actually necessary to change(instead of creating a new variable each time I pass it)
  • Stuff like effects should be nulled when your done with them call DestroyEffect(udg_OwnageMarker[ID]) - This would be true for locals, but in the case of globals it's not needed.
  • After all you hardly null anything oO
Now to GroupOrder
  • Again use local variables - Same as before
  • Use the bj instead of TriggerRegisterPlayerUnitEvent - Same as before

The concept itself is interresting, but has some issues to me at its current state:
  • Not every unit can be a captain, it requires 3 abilities atm, which is quite alot - This is an example use atm, but yes, I should have made it more apparent by moving some of the functions to separate triggers (or should I? That means more passing and clutter).
    You are welcome to suggest a better way to recruit/dismiss units.
  • It would be nice to distinguish between orders like attack and move and spells. One may be interrested in moving all units at once, but not casting 20 spells simultaneously. - This is good in theory and I've thought about that too, but the question is how exactly. I don't want to clutter up the command card with toggles for everything.

Most of this is just the way I do things and I believe it to be more efficient and easier to understand.
Fixed the thing about too many conditions though, I had trouble with finding out why the ownage markers are stacked when selecting units.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Yeah, blizzard messed that one up, global variables of handle and agent type leak memory when not nulled instantly after getting removed from the game. You can ask Troll-Brain for details.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
numbers? Every handle is number, pointer to memory, and every agent is reference counted, so if you dont clear all references(including global variables) the handle id will not reset, which is like 4 bytes of leak/unit referenced

You are desyncing String table but that shouldnt be problem, also the set s = "" is really useless, you cant possibly desync from not doing that
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
To me the formation looks strange. And two of the units seem to try to go to the same location, I had 7+ units recruited. Maybe you could look into this.

I want to get this resource approved.

I guess this means that I need to update the description to be more understandable.

The formations are defined by yourself. The moment that you turn formations ON, the unit positions are remembered. Adding units after this does not give them any positions, so they will try to clump.

As of now, they don't turn towards where you are moving, because I have not figured out a way to know the direction of the whole group.

EDIT: I can make defining a formation as a separate ability. But if I add features like that, then soon each commander will have multiple spellbooks full of abilities. I don't want to go down that path, so I have not yet added all functionality that I could.
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Nestharus said:
I don't personally know of any resource that has been approved without putting the code in the main post. Moderators have always said to put the code into the main post as well =)

What does that have to do with anything? Just because you don't remember something doesn't mean that the rules have changed.

Also do I really need to bring up your UnitIndexer thread again? Your code isn't in the main post, yet you're bitching at the OP for not having his.

You're either trolling or just plain stupid.
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Unless there were special circumstances, I don't personally know of any resource that has been approved without putting the code in the main post. Moderators have always said to put the code into the main post as well =)

No.

Approved by Magtheridon96
Approved by hvo-busterkomo
Approved by Bribe
Approved by Deuterium
Approved by TriggerHappy
Approved by Diablo-dk
Approved by Eccho
Approved by Dr Super Good
Approved by Hanky
Approved by The Reborn Devil
Approved by PurplePoot
 
Level 11
Joined
Jul 4, 2016
Messages
626
Just in case anyone is using in the future in recent patches and notice that units for players above 15 isn't working, you need to in the trigger GroupOrder and GroupControl, in the last function, you need to either increase the loop to 26, or switch the first group of calls with the TriggerRegisterAnyUnitEventBJ.

I didn't know why it wasn't working for me at first until I changed it.
 
Last edited:
Level 38
Joined
Feb 27, 2007
Messages
4,951
Just in case anyone is using in the future in recent patches and notice that units for players above 15 isn't working, you need to in the trigger GroupOrder, in the last function, you need to either increase the loop to 26, or switch all those calls with the TriggerRegisterAnyUnitEventBJ.
For ease of copying:
JASS:
    function InitTri_GroupControl takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > 26 //changed to support 24p maps
            call TriggerRegisterPlayerUnitEvent(DismissTrig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            call TriggerRegisterPlayerUnitEvent(RecruitTrig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            call TriggerRegisterPlayerUnitEvent(FormationTrig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            call TriggerRegisterPlayerUnitEvent(SelectTrig,Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
            call TriggerRegisterPlayerUnitEvent(DeselectTrig,Player(i),EVENT_PLAYER_UNIT_DESELECTED, null)
            call TriggerRegisterPlayerUnitEvent(UnitDieTrig,Player(i),EVENT_PLAYER_UNIT_DEATH, null)
            set i = i + 1
        endloop
        call TriggerAddCondition(DismissTrig,Filter(function Dismiss))
        call TriggerAddCondition(RecruitTrig,Filter(function Recruit))
        call TriggerAddCondition(FormationTrig,Filter(function Formation))
        call TriggerAddCondition(SelectTrig,Filter(function Select))
        call TriggerAddCondition(DeselectTrig,Filter(function Deselect))
        call TriggerAddCondition(UnitDieTrig,Filter(function UnitDie))
    endfunction
 
Level 4
Joined
May 19, 2020
Messages
319
Even though it's very old and still outdated, I don't see another one available on Hive with the same variety of features. This still seems to be one of the most effective and well-ordered systems for a squad, mainly because you can visually ("!") identify the units that are in your command group and also because you manage to maintain an organized formation, even if you create as organic as possible, it will always be exactly as desired. Some will complain due to the fact that it uses up a lot of space on the skills cards, but this helps to have quick options to activate or deactivate everything you need in single click . Alternatively, just use common units as leaders and disregard this use to heroes.

Some suggestions to improve the functionality of this system:
1. Selecting drives individually slows things down a bit. We could try to create a recruitment skill for multiple units simultaneously. Not needing to be individually (as it also exists in Dismiss), we could try to use an "Area of Effect" skill that would make all targets within an AOE selectable, able to identify all player units (or allies ) with a radius from the skill's target Point/Unit, which instantly adds all units in that area of influence to the groupment.

2. We can recruit allies by changing the "Targets Allowed" of RecruitUnit, but for these units the group identifier does not appear (for units only of Main player). We could add this look ("!") for units of Allied players too.

3. There is conflict in recruiting between leaders. If you recruit a commander, everything works perfectly, but if you select the recruited commander to also recruit the other starting one, in an attempt to use commands simultaneously between them, the game Crashes! But I don't see it as a big problem, maybe it's the only unresolved conflict within this system.
 
Last edited:
Level 7
Joined
Dec 21, 2022
Messages
23
For me it only works on this map without saving it. However, when I move the system to another map or save this map from the author, when I try to run the map it takes me to the main menu of the game. Can this be fixed or has someone created a better and more reliable system?
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
My guess is you're using a world editor that doesn't support vJASS, or you don't have it enabled.
I wrote this ages ago when everyone used JNGP.
However, I haven't been active for years, so I have no idea what the state of tools is. Does vJASS work on the normal editor? If so, do you need to enable JassHelper or something? No idea.
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
Yes, it’s now rolled into the base WE but is not enabled by default. Trigger Editor > top menu bar > JASSHelper > Enable JASSHelper & vJASS.

Without JASSHelper enabled it simply won’t save; there will be hundreds or thousands of errors. Disabling the triggers to allow the map to save obviously isn’t the correct move, so if you did that Vamper….

Best guess is Vamper is using a really old world editor like 1.24b or something from before 2013.
 
Level 7
Joined
Dec 21, 2022
Messages
23
Yes, it’s now rolled into the base WE but is not enabled by default. Trigger Editor > top menu bar > JASSHelper > Enable JASSHelper & vJASS.

Without JASSHelper enabled it simply won’t save; there will be hundreds or thousands of errors. Disabling the triggers to allow the map to save obviously isn’t the correct move, so if you did that Vamper….

Best guess is Vamper is using a really old world editor like 1.24b or something from before 2013.
I am using the current JNGP editor. The only options I have related to JASS are those visible in the attached image (everything is on and not working). In the trigger editor, I don't have any options related to JASSHelper or vJASS in the toolbar.
 

Attachments

  • Jass.png
    Jass.png
    1,011.6 KB · Views: 3
  • Jass2.png
    Jass2.png
    1.4 MB · Views: 2
Level 38
Joined
Feb 27, 2007
Messages
4,951
I am using the current JNGP editor.
There is no current JNGP. It has been deprecated for years now and should not be used by anyone. Do not use the JNGP. There is no benefit other than exceeding the destructable limit.

Update your game. It is literally free and does not require Reforged ownership. It will be extremely difficult to get help or support with other topics otherwise, and you will not be able to use most resources/suggestions.
 
Top