• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Trigger] Make a shop autoselect diffrent unit?

Status
Not open for further replies.
Level 9
Joined
May 31, 2010
Messages
366
Is there a way to make a shop, where u can buy items (the building it self is owned by u), cycle trough nearby units?.
i mean u have that target ability to 'choose a hero', is it possible to make the shop do this automatically to a diffrent valid target randomly in range?

either every x sec.
or (what id prefer more)
after the unit bought an item it doesnt count as a "valid target" anymore and the shop moves on with selecting

any ideas? could not find any similiar topics (or maybe i just suck at finding the right keyword)

thx, in advance :D
 
Level 12
Joined
Jun 15, 2016
Messages
472
It is possible to get a similar affect using the following method. Let's say, for simplicity's sake, that you want to do this only on one shop.

You will need the following variables: Shop_Units [a unit array], Shop_Max [integer], Selected_Index [integer], Shop_Radius [a region].

The trigger should go someway like this: every time a unit enters or exits the Shop_Radius region, add or remove it from the Shop_Units array accordingly. This is explained very well in this tutorial about dynamic indexing. Basically what you want to have is some ordered list of units around the shop, and this is the way to do it. The Shop_Max variable will indicate how many units are around the shop at every time.

Once there are units around the shop (i.e. Shop_Max > 0), you can set the first unit as the buying unit, with the Selected_Index variable representing which unit in the Shop_Units array is currently the buyer. Once the unit actually buys the item, go to the next unit in the Shop_Units array (and raise Selected_Index by 1). If you have finished the list (i.e. Selected_Index == Shop_Max) go back to the first unit in the list. The only "edge case" I can think of here is if your buying unit exits the Shop_Radius region and is removed for Shop_Units, in that case simply check if the unit which exited the region is the selected unit (i.e. triggering unit == Shop_Units[Selected_Index]) and if the check amounts to True, raise Selected_Index by 1 (of course again if Selected_Index == Shop_Max, then reset Selected_Index to the start of the list). Simply make sure you do that before the whole dynamic indexing thing.

However! note that even though that method should work without any bugs, using dynamic indexing may cause some unwanted behavior due to using dynamic indexing. these are some really pathological edge cases, so it's in a spoiler

Let's say, for example, that the first unit to enter Shop_Radius region is a Paladin hero, it is indexed as Shop_Units[1]. After that, another 2 priests and another 5 footmen also enter Shop_Radius, and are indexed as Shop_Units[2] and Shop_Units[3] for the priests and Shop_Units[4] through Shop_Units[8] for the footmen. Now, the Paladin bought something and left Shop_Radius, so now Selected_Index == 2 and the first priest is selected, also the Paladin has been de-indexed and the last unit in Shop_Units takes the first place (meaning Shop_Units[1] is a footman now). After that, the Paladin gets back into the Shop_Radius region, and it is now the last unit in Shop_Units. After that, the second priest leaves Shop_Radius, therefore it is de-indexed and the last unit in Shop_Units takes it's place - now the Paladin is not the last in line, but rather one after the first priest (i.e. Shop_Units[3] is now the Paladin). So, if the priest buys an item, the next selected unit will be the Paladin, and not one of the footmen.

If you read all that and the scenario described there is actually a problem, you can start with getting the method described outside the spoiler (up there) first. Then you can try making some small changes to the dynamic indexing method to make it keep the order of units.
 
Level 9
Joined
May 31, 2010
Messages
366
hmm... i see how this might work, but how do i make the shop select the unit? (i cant find any order for the spell "select hero")
also the player is able to build multiple shops if he wants to, so i think this would need some extra modification to the described system?
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
You can avoid the pathological problems and remove the need to index by using two unit groups.

  1. Add all to A
  2. FirstOfGroup() on A, call it U
  3. Make U active at the shop
  4. ... eventually U buys an item ...
  5. Remove U from A
  6. Add U to group B
  7. If A is empty, add all units of B to A and clear B (remove all units)
  8. Repeat to step 2.
If new units come in range just also add them to A. If they leave range remove them from both A and B.
 
Level 9
Joined
May 31, 2010
Messages
366
You can avoid the pathological problems and remove the need to index by using two unit groups.

  1. Add all to A
  2. FirstOfGroup() on A, call it U
  3. Make U active at the shop
  4. ... eventually U buys an item ...
  5. Remove U from A
  6. Add U to group B
  7. If A is empty, add all units of B to A and clear B (remove all units)
  8. Repeat to step 2.
If new units come in range just also add them to A. If they leave range remove them from both A and B.
this sounds fairly easy to do

just 2 questions:
how do i make the shop target a unit? it seems there is no order for a shop to select an unit
how would it interact with multiple shops? i mean yes storing all shops in an unit variable with an array and make the groups indexed by that array's number too you could have the 1:1 reference but then if the unit is buying an item how do i get the reference of wich group i have to remove it from? if dont index groups by an array units would be inside the group but out of reach of the shop that the trigger runs of from right?
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
You need 2 groups per shop (2 arrays total) and all of the shops in an array with an index for each one that is the same as the groups. So Shops[8], GroupA[8], and GroupB[8] are all together.

When a unit buys an item, check if it's a part of any of the groups one by one, by looping through them. Units being able to leave without buying an item complicates this a little bit. How important is that to you?
  • For each (integer A) from 1 to MAX_SHOPS do (Actions) //could be 0 to MAX-1 if you want instead, your call
    • Loop - Actions
      • If (conditions) then (actions) else (actions)
        • If - Conditions
          • Or - Any conditions are true
            • (Triggering Unit) is in GroupsA[(Integer A)] equal to true
            • (Triggering Unit) is in GroupsB[(Integer A)] equal to true
        • Then - Actions
          • -------- Do shit regarding buying at shop (Integer A) --------
        • Else - Actions
          • -------- Unit wasn't buying at once of these special shops or an error happened --------
As for selecting the unit, there's probably a library for that someone made somewhere. I believe "select unit" is also an ability you can find in the World Editor (all shops have it) and you can just order the shop to use that ability on the correct unit. If I'm wrong about that existing, there is definitely some order you can give to the shop to make it select the unit you want... it's probably just a number order, not something with a string you can see. Try changing selected units with this trigger active and then issuing that number as an order to the shop:
  • Events
    • Unit - A Unit is issued an order <<targeting a unit>> //the last bit may be optional
  • Conditions
  • Actions
    • Custom script: call BJDebugMsg("Order: "+I2S(GetIssuedOrderId()))
 
Level 12
Joined
Jun 15, 2016
Messages
472
how do i make the shop target a unit? it seems there is no order for a shop to select an unit

I searched the site and it seems the order for shop changing user is this neutralinteract=852566, from the order repo. By the way, the answer has been asked a few times already, see here and here. Please search the site for such things next time, there are a lot answered problems lying around here, and the hive search has gotten markedly better.

Units being able to leave without buying an item complicates this a little bit.

Why is that a problem. Units leaving the shop range can be removed easily by GroupRemoveUnit and whatever the GUI counterpart is. If the unit is the currently selected one, he can remove it, call first of group again, and worst case scenario the group is empty then move to group B or whatever is next. This is actually better then my idea as far as I can tell.
 
Level 9
Joined
May 31, 2010
Messages
366
Code:
                Custom script:   Custom script: call IssueTargetOrder (udg_seller, "neutralinteract=852566", udg_buyer)

i tried this for a test, issue is: editor tells me that there is smth wrong with the cust script and i cannot save
i tried a few diffrent things and it just doesnt work.
i have to say im really bad a cust script and jass (basically 0 knowledge about it) i tried to just give it the order id as order doesnt work either.

im using the "Shared shop" ability from the default buildings you can build yourself and therefore the button is not "select hero" its "change user" (or smth sry my we isnt in english)

**sry for my late reply but i was busy with personal stuff the last week :/
 
Level 9
Joined
May 31, 2010
Messages
366
tried without quotes:
Code:
call IssueTargetOrder(udg_seller, 852566, udg_buyer)
gives me that error: Cannot convert integer to string parameter order in call to IssueTargetOrder (idk but i dont have integer or string variable in there o_O)
 
the used native "IssueTargetOrder" can't be used for that purpose: you'll need one of the IssueNeutral natives proably this one:
JASS:
IssueNeutralTargetOrder          takes player forWhichPlayer,unit neutralStructure, string unitToBuild, widget target returns boolean

to use the Id numbers you have to use the ById natives, without ById it will request Strings.
 
Last edited:
Level 9
Joined
May 31, 2010
Messages
366
the used native "IssueTargetOrder" can't be used for that purpose: you'll need one of the IssueNeutral natives proably this one:
JASS:
IssueNeutralTargetOrder          takes player forWhichPlayer,unit neutralStructure, string unitToBuild, widget target returns boolean

to use the Id numbers you have to use the ById nativesm, without ById it will request Strings.


ahhh isee what u mean but i really dont know what to do.

this is the code i need to paste? "IssueNeutralTargetOrder takes player forWhichPlayer,unit neutralStructure, string unitToBuild, widget target returns boolean"

if so what does that line do and where do i put the "buyer" and "seller" unit variables in (also where do i put the order? o.o)
 
you need to fill it with values:
  • forWhichPlayer the shopping player.
  • neutralStructure the shop.
  • unitToBuild the wanted command.
  • target onto who.
It should look like this.
JASS:
call IssueNeutralTargetOrderById( GetOwningPlayer(udg_buyer), udg_seller, 852566 , udg_buyer)
This native orders "neutral unit" "forWhichPlayer" to do "unitToBuild" onto "target".
 
Level 9
Joined
May 31, 2010
Messages
366
ahhh now i understand (if u r used to gui these jass keywords are really confusing .. xD)
gives no error on saving, thank you :D
just seems like its the worng order id for the "shared shop" "target unit" thingy, ill look through that list posted above and see if i can find the order id

edit: nope order id is fine.... smth else seems to not work ... :/

edit2:


Code:
switch
    Event
        Unit - A unit aquires an item
    Condition
    Action
        Set seller = (Selling unit)
        Unitgroup - Pick every unit in (Units within 450.00 of (Position of seller) matching (((Owner of (Picked unit)) equal (Owner of seller)) and (((Picked unit) unequal (Buying unit)) and ((Item carried by (Buying unit) in slot 1) equal No Item)))) and do (Actions)
            Loop - Action
                Set buyer = (Picked unit)
                Custom script:   call IssueNeutralTargetOrderById( GetOwningPlayer(udg_buyer), udg_seller, 852566 , udg_buyer)

so this is what i got, just to make it switch for testing but it doesnt switch..
"seller" is the shop (build by player not neutral) and "buyer" should be the next unit to buy an item after the previus target bought one

**tried to translate most of it into english sorry if i missed smth
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,037
Whenever you see "blah blah blah MATCHING (more blah)" you have to use (matching unit) inside those comparisons. So your Unit Group - Pick needs to have its Picked Units changed to Matching Units. Not in the Loop - Actions below, those stay as picked unit.

Your code also only checks for no item in slot 1, but not any other slots. So putting a potion in slot 1 and having the 5 others free, your trigger would not catch it.
 
Level 9
Joined
May 31, 2010
Messages
366
Whenever you see "blah blah blah MATCHING (more blah)" you have to use (matching unit) inside those comparisons. So your Unit Group - Pick needs to have its Picked Units changed to Matching Units. Not in the Loop - Actions below, those stay as picked unit.

Your code also only checks for no item in slot 1, but not any other slots. So putting a potion in slot 1 and having the 5 others free, your trigger would not catch it.
Code:
Unitgroup - Pick every unit in (Units within 450.00 of (Position of seller) matching (((Owner of (Matching unit)) equal (Owner of seller)) and (((Matching unit) unequal (Buying unit)) and ((Item carried by (Buying unit) in slot 1) equal No Item)))) and do (Actions)
tried it like that (with matching unit) doesnt work either :/

yes units only have 1 slot so its fine that it only checks that slot
(maybe to the idea for better understanding of what im trying to do: you can recruit units and modify them by buying items to enhance them individually for your needs, if you have an army of 50 units this gets really tiring. especially when your defending your base and you are in quick need of units, this takes up just too much time, to have to reselect them individually or make them move away so it auto-reselects because they are out of range)


Does the Event "Aquier Item" really support "selling Unit" and does it point to a valid unit?
good point, ill do some testing

edit: yep it needs "a unit sells an item (from a shop)"

now works, only issue is that the condition within the pick does not seem to work, even if the unit has an item in slot 1 it is targeted again even if there is a valid target next to it
 
Last edited:
Level 9
Joined
May 31, 2010
Messages
366
"item carried by buying unit in slot 1" should be "item carried by matching unit in slot 1"
only works if i put a wait of at least 0.01 sec between "set seller" and the unitgroup loop
works fine if there is only one players, if there are 2 players buying on the same time it wont. i experimented with a unit group (adding all units that received an item and blocking them from being selected) with similiar results.
ill just do 12 triggers for each player with "seller" and "buyer" as an indexed array for each player, since a player is only able to buy at one shop at a time within 0.01 seconds this absolutely be fine (i hope at least xD)
 
Level 39
Joined
Feb 27, 2007
Messages
5,037
Right... the delay you need has nothing to do with setting the variable (that happens instantly before the next line of code executes). It's because a unit can only execute one order at a time and you can only have 1 unit selected for buying at a time, so you can't select ALL the other units simultaneously when the Group Loop runs. You can only select ONE new unit every time that trigger 'happens'. You custom script is telling your seller to reselect your buyer, but instead you want to pick an new buyer right? I changed one of the variables in the CS line. You also leak a group and a location every time you do it.

  • Set TempPlayer = Owner of seller
  • Set TempPoint = Position of seller //for the location leak
  • Custom script: set bj_wantDestroyGroup = true //this automatically cleans the group leak in the next line
  • Set newbuyer = Random Unit from (Units within 450.00 of TempPoint matching ((Owner of (Matching Unit) equal to TempPlayer) and (Item in carried by (Matching Unit) in slot 1 equal to No Item)))
  • Custom script: IssueNeutralTargetOrderById( udg_TempPlayer, udg_seller, 852566 , udg_newbuyer) //newbuyer
  • Custom script: call RemoveLocation(udg_TempPoint)
 
Status
Not open for further replies.
Top