Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
GUI Faction system, useful for many styles of maps.
Requirements:
° Unit Indexer (already included on the map);
° UNITDATA Hashtable with all melee player races' unit's attack range stored (You need to add new data of your units to the hashtable)
So this system consists of creating many groups of units owned by the same player and make them threat each other as ally/enemy. For example I have a mercenary camp creeps and a murloc camp creeps, they're both neutral hostile, but I want to make them threat each other as enemies, we set the mercenary camp creeps into one faction, called "Mercenary Camp" and the murloc camp creeps into other faction "Murloc Camp", when they meet each other they will attack themselves as they triggered set to be enemies. This has a great use for huge RPG maps on which have many cities and the user wants one city to be at war with another and yet don't want to spend 2 player slots for them, instead you put all units of one city into a faction, let's say "City #1" and the units from another city into a different faction "City #2".
On this test map we have 4 unit groups and 2 factions: Group1 (Humans) belongs to "The Alliance", Group2 (Night Elves) also belongs to "The Alliance", Group3 (Orcs) and Group4 (Undead) belongs to "The Horde", therefore Group1 and Group2 are enemies of Group3 and Group4 because their factions are enemies. All units are owned by the same player (Player 1). To demonstrate the system, on your map you can make them Neutral Passive or Hostile to save player slots instead of using a Computer Player, for example with this system, on dota we could add 1 player to each team making it 6v6 instead of 5v5 as we would simulate the Sentinel and the Scourge inside Neutral Passive, as the minions and bases could be Neutral Passive and still interact as ally/enemies with players and each other.
How to add unit data to the UNITDATA Hashtable:
UNITDATA Hashtable Values
Events
Conditions
Actions
-------- PEASANT --------
Set UNITDATA_UnitType = Peasant
Custom script: set udg_UNITDATA_UnitTypeInteger = udg_UNITDATA_UnitType
Hashtable - Save 90 as UNITDATA_ATTACKRANGE of UNITDATA_UnitTypeInteger in UNITDATA_Hash
This will save 90 as the Peasant's attack range on our hashtable.
Pros:
° Nice for working with RPG maps and other projects that needs many factions;
° Number of factions are limited by your imagination;
° Simulates basic unit behavior, (will threat units on other groups as enemies);
° Supports:
* Attack order;
* Attack-Move order;
* Patrol order;
* Hold Position order;
* Stop order;
* Move order.
° Easy to implement, just use the function:
Set FACSYS_FactionName[(Custom value of (Triggering unit))] = My Faction
Unit Group - Add (Your Unit) to FACSYS_AssignedGroup
Where "My Faction" is replaced by the name of the faction you wish to add the unit to.
Cons:
° No buffs or abilities can be used on it's current state as the factions some times belongs to the same player (abilities won't be able to cast and buffs will also affect units on the enemy group);
° Units can only patrol between 2 points.
Credits:
° @Bribe for he's "Unit Indexer" system.
° @BloodSoul for helping providing the following line of code, on which this system would not be possible:
-------- ------------------------- --------
-------- We need to use this custom script because there is no "Order unit to patrol unit" function in GUI --------
-------- First you need to add custom and neutral units that you're going to use into the UNITDATA Hashtable (the trigger is at the "Requirements" folder) --------
-------- It is necessary that you use the following functions to add the unit's attack range value into the UNITDATA Hashtable --------
-------- Example: --------
-------- PEASANT --------
Set UNITDATA_UnitType = Peasant
Custom script: set udg_UNITDATA_UnitTypeInteger = udg_UNITDATA_UnitType
Hashtable - Save 90 as UNITDATA_ATTACKRANGE of UNITDATA_UnitTypeInteger in UNITDATA_Hash
-------- This will save 90 as the Peasant's attack range on our hashtable. --------
-------- We need this value so that orders like "Hold Position", for example, work correctly. --------
-------- In order to add a unit to a faction you just need to set the string variable FACSYS_FactionName[Custom value of Unit] = "name of faction" --------
-------- Something like this: --------
Set FACSYS_FactionName[(Custom value of (Triggering unit))] = My Faction
-------- ------- --------
-------- But for memory saving reasons we usually do it like this: --------
Set FACSYS_Unit[1] = (Triggering unit)
Set FACSYS_TempInteger[1] = (Custom value of FACSYS_Unit[1])
Set FACSYS_FactionName[FACSYS_TempInteger[1]] = My Faction
-------- Now follows an explanation of the setup and how it works --------
-------- After configuring the Object Editor data, you just need to set the FACSYS_GroupInteger of an unit to the value corresponding to the faction you want the unit to join --------
-------- For example: --------
Set FACSYS_FactionName[FACSYS_TempInteger[1]] = The Alliance
-------- This will make (Picked unit) join Faction with the name (The Alliance for example) --------
-------- If I would set it to (The Horde) for example, then it would be joining The Horde --------
-------- --------------------- --------
-------- If a unit from one faction meets one from the opposing faction they will behave like normal enemy units would --------
-------- But you can set a unit's faction to "Passive" if you wish that unit to have no faction, making them a passive unit --------
-------- --------------------- --------
-------- First you need to decide what will make a unit join a faction, if it's race, it's owner etc --------
-------- Then you set FACSYS_FactionName[Custom value of Your Unit] = The namer of the faction you want the unit to join --------
-------- Example: If I want all human units in the map to be part of the alliance I will do the following actions: --------
-------- Pick all human units in playable map area --------
Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-------- If we want the unit to belong to a specific group inside the faction we just add the unit to the group --------
-------- For example: Now we want all human units to join the Human group inside The Alliance --------
-------- Again we pick the desired units --------
Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
Loop - Actions
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(Race of (Picked unit)) Equal to Human
Then - Actions
-------- And then add them to the Human group --------
Unit Group - Add (Picked unit) to FACSYS_GroupHumans
Else - Actions
-------- This way if we wish, we can remove all humans from the alliance or make them join another faction --------
-------- Like this: --------
Unit Group - Pick every unit in FACSYS_GroupHumans and do (Actions)
Loop - Actions
-------- Clear the unit's faction --------
Set FACSYS_FactionName[FACSYS_TempInteger[1]] = Passive
-------- Or make them join another --------
Set FACSYS_FactionName[FACSYS_TempInteger[1]] = The Horde
-------- --------------------- --------
-------- You can create new factions, you just need to assign them with a name of your wish --------
-------- Same goes for groups, you can create new ones to help identify units belonging to a race, a region (a village or town) or by owning player etc. --------
-------- --------------------- --------
-------- I think this is all the user should know before getting started, thank you and let me know if you face any issues! --------
FACSYS Pre Setup
Events
Game - UnitIndexEvent becomes Equal to 3.00
Conditions
Actions
Set FACSYS_OrderAttack = (Order(attack))
Set FACSYS_OrderHold = (Order(holdposition))
Set FACSYS_OrderMove = (Order(move))
Set FACSYS_OrderSmart = (Order(smart))
Set FACSYS_OrderStop = (Order(stop))
Set FACSYS_OrderPatrol = (Order(patrol))
Set FACSYS_PERIODIC_TIMEOUT = 0.33
Set FACSYS_TRIG_Region = FACSYS Region Actions <gen>
Set FACSYS_TRIG_Periodic = FACSYS Periodic Checking <gen>
Trigger - Add to FACSYS_TRIG_Periodic the event (Time - Every FACSYS_PERIODIC_TIMEOUT seconds of game time)
FACSYS Periodic Checking
Events
Conditions
Actions
-------- CHECK FOR NEARBY ENEMIES AND CURRENT ORDERS --------
-------- We pick the units on the previously set group so it saves us time and memory --------
Unit Group - Pick every unit in FACSYS_AssignedGroup and do (Actions)
Loop - Actions
Set FACSYS_Unit[1] = (Picked unit)
Set FACSYS_TempInteger[1] = (Custom value of FACSYS_Unit[1])
Set FACSYS_Loc[1] = (Position of FACSYS_Unit[1])
Set FACSYS_TempOrder = (Current order of FACSYS_Unit[1])
Set FACSYS_TempBool[1] = (FACSYS_Target[FACSYS_TempInteger[1]] is dead)
-------- First we clear unit's target if needed --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
FACSYS_TempBool[1] Equal to True
Then - Actions
Set FACSYS_Target[FACSYS_TempInteger[1]] = No unit
Set FACSYS_IsEngaged[FACSYS_TempInteger[1]] = False
Else - Actions
-------- Here we order it to attack it's target if it's alive and the unit isn't attacking, just to make sure. --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
FACSYS_TempBool[1] Equal to False
FACSYS_TempOrder Not equal to FACSYS_OrderAttack
Then - Actions
Unit - Order FACSYS_Unit[1] to Attack FACSYS_Target[FACSYS_TempInteger[1]]
Else - Actions
-------- Then we check If unit's current order is hold position this function will prevent it from following the target after it leaves it's attack range --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
FACSYS_Target[FACSYS_TempInteger[1]] Not equal to No unit
Then - Actions
Set FACSYS_Loc[2] = (Position of FACSYS_Target[FACSYS_TempInteger[1]])
Set FACSYS_TempReal[2] = (Distance between FACSYS_Loc[1] and FACSYS_Loc[2])
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
FACSYS_TempReal[2] Greater than FACSYS_TempReal[1]
FACSYS_HoldPosition[FACSYS_TempInteger[1]] Equal to True
Then - Actions
Set FACSYS_Target[FACSYS_TempInteger[1]] = No unit
Set FACSYS_IsEngaged[FACSYS_TempInteger[1]] = False
-------- Here we will handle Attack-Move and Patrol orders accordindly --------
-------- This system simulates patrols through attack-move orders from one point to the other --------
-------- So in order to make it work correctly we will need to set a boolean to define if a Attack-Move comes from a patrol order or if it is a legit Attack-Move order --------
-------- First we check to see if the current order of the unit is attack and if it's not patrolling --------
-------- Creating regions to detect when the unit has reached one point of it's patrol so it is then ordered to Attack-Move to the other point --------
-------- Here we set a boolean to later check if unit has already been assigned to a faction or not, so we don't spend memory picking the same unit --------
-------- Add to a overall specific group so we won't have to get units in playable map area as it will cost us memory --------
Unit Group - Add FACSYS_Unit[1] to FACSYS_AssignedGroup
-------- You can always change the criteria on how the unit will be handled and which faction and group will it join, in this case I choose the race --------
-------- Set up for the allaince --------
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
A fairly simple system that can be useful to users. Unfortunately, this system only supports orders like patrol, smart, hold position, etc. It would be great if the system supported spells and such, but I know that would either be impossible or really...
You are lacking a proper map description. Your map description should contain the code, credits, and how to import instructions. You should also have the import instructions inside the map
Seperate the needed triggers and demo triggers. Since you are lacking a "how to import," it would be really hard to tell what is needed if a user was not experienced
Loc2 is only created inside an If block, yet you remove the location outside of it. You should be removing the location inside the same block to prevent the chance of removing a null location
With that in mind, you also use Loc2 inside the unit group enumeration. This creates a location leak since you dont remove the first Loc2 reference
Loc2 leaks a lot inside that unit group enumeration
Suggestions
You call the same function multiple times a lot in your code. I recommend you store these into variables and reference the variable instead (ex: Custom value of a unit)
Periodic trigger should be configurable
Position of (Picked unit) -> Position of (FACSYS_Unit1)
(Position of (Picked unit) -> Position of (FACSYS_Unit2)
You are lacking a proper map description. Your map description should contain the code, credits, and how to import instructions. You should also have the import instructions inside the map
Seperate the needed triggers and demo triggers. Since you are lacking a "how to import," it would be really hard to tell what is needed if a user was not experienced
Loc2 is only created inside an If block, yet you remove the location outside of it. You should be removing the location inside the same block to prevent the chance of removing a null location
With that in mind, you also use Loc2 inside the unit group enumeration. This creates a location leak since you dont remove the first Loc2 reference
Loc2 leaks a lot inside that unit group enumeration
Suggestions
You call the same function multiple times a lot in your code. I recommend you store these into variables and reference the variable instead (ex: Custom value of a unit)
Periodic trigger should be configurable
Position of (Picked unit) -> Position of (FACSYS_Unit1)
(Position of (Picked unit) -> Position of (FACSYS_Unit2)
Needs Fixed
° Added the code into map description
° Separated the needed triggers from the useless
° Loc2 is now removed inside the same block it was added
Suggestions
° Made all functions work with variables and reduced the number of "()" to 0
° I need to know what do you mean configurable on the periodic trigger, please.
° Corrected Locations putting them into variables to reffer later
° Corrected Matching Unit issue
° Corrected "Units within range issue", now it uses:
Unit Group Correction
Events
Conditions
Actions
Set FACSYS_TempReal = (Current acquisition range of FACSYS_Unit1)
Set FACSYS_TempGroup = (Units within FACSYS_TempReal of FACSYS_Loc1)
Custom script: set bj_wantDestroyGroup = true
Unit Group - Pick every unit in FACSYS_TempGroup and do (Actions)
TempGroup leaks. set bj_wantDestroyGroup = true needs to be before the unit group is created
Suggestions
In FACSYS Faction Setup, you store the custom value of the triggering unit into a variable, but you don't use it
Periodic timeout should be configurable
Why do you compare the custom value of a unit to the distance to it's target?
You can store the current order of the unit into a variable instead of constantly calling the function
Instead of converting the string orders into regular orders all the time, you can just store them into global variables and reference them when you need to
Suggestions:
° In FACSYS Faction Setup I use it (FACSYS_TempInteger) to set the "GroupInteger" variable of the unit itself throught the TempInteger, which is set on the begining of the trigger
Set FACSYS_GroupInteger[FACSYS_TempInteger] = 1
° The issue with custom value of unit has been corrected and set to "point value" of unit, you need to change the point value of all units you're going to use, in the object editor change the value of the point value of unit to be equal to it's attack range (as described on the instructions).
° "Current order" have been stored
° I don't know if it solves the "Units whithing range" leak problem but I'm now using
Set FACSYS_TempReal = (Current acquisition range of FACSYS_Unit1)
Set FACSYS_TempGroup = (Units within FACSYS_TempReal of FACSYS_Loc1)
Unit Group - Pick every unit in FACSYS_TempGroup and do (Actions)
In FACSYS Faction Setup I use it (FACSYS_TempInteger) to set the "GroupInteger" variable of the unit itself throught the TempInteger, which is set on the begining of the trigger
You don't have to fix it. It's just something I like to point out. If you really want to try to fix it, you have to use GroupEnumUnitsInRange() or GroupEnumUnitsInRect()
You don't have to fix it. It's just something I like to point out. If you really want to try to fix it, you have to use GroupEnumUnitsInRange()
or GroupEnumUnitsInRect()
Ermm I said that units within range leaks, but youre still using it. Actually, now that I look at it, you fill your group up using Units within range, and then you fill it again using GroupEnumUnitsInRange lol. Regardless, this is a tiny issue. I'll review this sometime this weekend.
I can see that working the first time, but not anytime after that given you destroy the group. Recycle the group instead of constantly creating and destroying it.
Note that GroupEnums do clear the group directly before they re-fill it. So you don't need to care yourself to clear it before.
Also btw, more BJs are leaking references, it's pretty uncool to fix them all in GUI.
I told him he didnt have to do it and that I point it out just for the hell of it, but he insists that he does it thats why I always put it under Suggestions and not Needs Fixed.
Every function that internaly declares local agents leaks a reference, because none of them are nulled by blizzard. And there are some of them.
It's 'only' a reference leak, mostly, though, which is less important than a object leak: Memory Leaks - but if you personaly care much, then JASS, JASS, JASS. ^^
Very nice system, I really liked the idea, it sure would come in handy in a lot of situations.
Just two things I want to point out:
1. Why you're using the Value-Point of the units to check it's Attack Range? Isn't there a better method? This 'pre-configuration' sure would waste alot of time if you have alot of unit types to work with.
2. Fix your description, it's treat not threat.
Very nice system, I really liked the idea, it sure would come in handy in a lot of situations.
Just two things I want to point out:
1. Why you're using the Value-Point of the units to check it's Attack Range? Isn't there a better method? This 'pre-configuration' sure would waste alot of time if you have alot of unit types to work with.
2. Fix your description, it's treat not threat.
Use the Index event provided by Bribe to catch when a unit enters a map
Elapsed game time is 0.00 seconds -> Map Initialization
Set FACSYS_Loc2 = (Position of (Picked unit)) -> (Position of FACSYS_Unit2)
It's really pointless to be constantly storing variables like Order(holdposition), Order(stop), and Order(smart) whenever an event occurs. Store them into global variables on map init and reference them instead
You don't have to empty a group before destroying it
Use the index event of the Unit Indexer instead of "A unit enters (Playable map area)." There is a possibility that when users import this into their map, the Faction Setup will run before the unit is actually indexed
Suggestions
Elapsed game time is 0.00 seconds -> Map Initilization
I don't know how many times I have to tell this to you, but STORE constant orders (holdposition, stop, smart, attack, etc) into global variables instead of storing it every timeout or ever event run...
You don't have to empty a group before destroying it
Very nice idea! Is this system limited to 2 factions or there may be more?
I just tested demo map, and found a bug: if you make a ghoul enemy of other ghouls and abomination - they start to fight, then when make this ghoul back allied, he stops, but other ghouls continue attacking.
I think @KILLCIDE suggests something like this
Created
Events
Game - UnitIndexEvent becomes Equal to 1.00
Conditions
FACSYS_Grouped[UDex] Equal to False
Actions
Set FACSYS_Unit1 = UDexUnits[UDex]
Also please consider upgrade from Unit Indexer to "Unit Event" by Bribe GUI Unit Event v2.2.1.0
I just tested demo map, and found a bug: if you make a ghoul enemy of other ghouls and abomination - they start to fight, then when make this ghoul back allied, he stops, but other ghouls continue attacking.
This is caused because I didn't added a "stop" action on the demo triggers. I will add the "Issue unit to stop" action on the triggers of the Demo category when I upload next, thanks for pointing that out!
I just tested demo map, and found a bug: if you make a ghoul enemy of other ghouls and abomination - they start to fight, then when make this ghoul back allied, he stops, but other ghouls continue attacking.
I think @KILLCIDE suggests something like this
Why did I do this? Simplification and efficiency. The current code for Unit Event is about half of what the combined length of the old Unit Event and Unit Indexer were.
UnitIndexEvent Becomes Equal to 2.00 now fires the instant the unit is removed, not after an arbitrary amount of time. It also does not bug when a paused unit is removed from a transport, so I no longer need a timer to detect when they are unloaded.
I also no longer need a timer to detect when a corpse is unloaded from a transport, as I am now using Jesus4Lyf's "enter region" trick to detect when those are unloaded.
I just thought about order attack to unit who is channeling a spell. You may want to not interrupt spells like Starfall, tranquility etc, but you have to track units for 'begin casting / stop casting ability' and set a bolean flag for each unit, but thats up to you if you want to implement such a thing
Use the index event of the Unit Indexer instead of "A unit enters (Playable map area)." There is a possibility that when users import this into their map, the Faction Setup will run before the unit is actually indexed
I don't know how many times I have to tell this to you, but STORE constant orders (holdposition, stop, smart, attack, etc) into global variables instead of storing it every timeout or ever event run...
8100 FACSYS_Target_Cleaning groups is not acceptable in my opinion, its better to create / destroy groups dynamically
also make optimalization like (cut some ITE):
FACSYS Order Check Copy
Events
Unit - A unit Is issued an order targeting an object
Conditions
(Issued order) Equal to FACSYS_OrderSmart
(Target unit of issued order) Not equal to No unit
Actions
better to check some conditions instead of running actions each time any unit is issued order
good luck
8100 FACSYS_Target_Cleaning groups is not acceptable in my opinion, its better to create / destroy groups dynamically
also make optimalization like (cut some ITE):
FACSYS Order Check Copy
Events
Unit - A unit Is issued an order targeting an object
Conditions
(Issued order) Equal to FACSYS_OrderSmart
(Target unit of issued order) Not equal to No unit
Actions
better to check some conditions instead of running actions each time any unit is issued order
good luck
It sounds great, I would need to implement something like this in my map but since it uses point value its incompatible, I use it for map specific purposes.
I'll be implementing a hashtable to use instead of point value, but I'll only cover playable races (Human, Orc, Night Elf and Undead), then the user can inject their own unit values if they wish to.
it's worth noting that neutral hostile and extra neutral are natural enemies between each other (they will attack each other and can cast spells on each other)
it's worth noting that neutral hostile and extra neutral are natural enemies between each other (they will attack each other and can cast spells on each other)
yeah but there the user have only 2 factions to work with, what if he needs more? let's say a RPG map with 30 towns, then the user can make every town have it's own faction: 30 factions across de map. If you wish you can work with neutral hostile, but then you would need to setup player alliances between players and hostiles or the hostile units would auto-attack player units.
Use the index event of the Unit Indexer instead of "A unit enters (Playable map area)." There is a possibility that when users import this into their map, the Faction Setup will run before the unit is actually indexed
Suggestions
Elapsed game time is 0.00 seconds -> Map Initilization
I don't know how many times I have to tell this to you, but STORE constant orders (holdposition, stop, smart, attack, etc) into global variables instead of storing it every timeout or ever event run... -DONE!
You don't have to empty a group before destroying it -DONE!
° Use the index event of the Unit Indexer instead of "A unit enters (Playable map area)." There is a possibility that when users import this into their map, the Faction Setup will run before the unit is actually indexed - DONE!
° Elapsed game time is 0.00 seconds -> Map Initilization - DONE!
° I don't know how many times I have to tell this to you, but STORE constant orders (holdposition, stop, smart, attack, etc) into global variables instead of storing it every timeout or ever event run... -DONE!
° You don't have to empty a group before destroying it -DONE!
It sounds great, I would need to implement something like this in my map but since it uses point value its incompatible, I use it for map specific purposes.
A fairly simple system that can be useful to users. Unfortunately, this system only supports orders like patrol, smart, hold position, etc. It would be great if the system supported spells and such, but I know that would either be impossible or really complicated. Thanks for the well documented code.
Needs Fixed
Nothing
Suggestions
Include "How to Import" instructions
Any triggers you run in the code will be disabled when you import them between maps. I recommend you store the triggers into variables so that it is easier to fix all the disabled actions instead of having to go through the code and re-enable each one.
It is with great joy that I recieve my first approval on the hive, I want to tell that all Suggestions have also been met on the last file I've sent (after the last review). Thanks a lot @Bribe for your Unit Indexer System, thanks @BloodSoul for the "Patrol Object" JASS function, thanks www.hiveworkshop.com, the staff and all users from the forums who helped in a way or another to achieve it. Thanks, @KILLCIDE for the review. This is for everyone, enjoy!
A very nice and useful system for mapmakers who use all player slots and still want more in order to distinguish allies from enemies; I've seen a significant amount of users asking for something like this.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.