1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Join the 6th Melee Mapping Contest for a chance to have your map featured in this year's Hive Cup!
    Dismiss Notice
  4. Shoot to thrill, play to kill. Sate your hunger with the 33rd Modeling Contest!
    Dismiss Notice
  5. Do you hear boss music? It's the 17th Mini Mapping Contest!
    Dismiss Notice
  6. Let your favorite entries duke it out in the 15th Techtree Contest Poll.
    Dismiss Notice
  7. Weave light to take you to your highest hopes - the 6th Special Effect Contest is here!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Detecting if a Building is Under Construction

Discussion in 'World Editor Help Zone' started by Damage, Jul 30, 2020.

  1. Damage

    Damage

    Joined:
    Aug 31, 2009
    Messages:
    648
    Resources:
    6
    Maps:
    4
    Spells:
    2
    Resources:
    6
    This seems like something that should be simple, and yet I can't find a simple solution.

    I want to detect if a building is under construction. That's it.

    I looked around online and found a thread on thehelper.net from 2010: Checking if a building is currently under Construction

    His method seems really simple, but when I try the method, the "GetUnitAbility(...)" always returns 1 - whether the building is finished or still under construction.


    The reason I want to achieve this is because in my map there is a building called a Repair Bay. It automatically searches for nearby damaged buildings, picks one at random, and attempts to heal it at the cost of resources. The problems is that the Repair bay automatically picks buildings which are under construction too (because their health != their max health). It's unnecessary to heal them, as they will get full health the moment their construction is finished anyway.

    So - how do I check for if a building has finished construction or not, without having to resort to an annoying workaround (e.g. using custom value when the building enters the map, and changing it on construction finished)?
     
  2. DoomBlade

    DoomBlade

    Joined:
    Feb 5, 2018
    Messages:
    205
    Resources:
    0
    Resources:
    0
    What about a BUFF like devotion aura?
    Unit starts construction - add ability (whatever works the best)
    Unit finishes construction - remove ability, remove buff

    Healing bay - heal target (conditions have buff then -- nothing, else heal)
     
  3. Damage

    Damage

    Joined:
    Aug 31, 2009
    Messages:
    648
    Resources:
    6
    Maps:
    4
    Spells:
    2
    Resources:
    6
    This is the same as the workaround, no?
    Detecting when it enters, adding ability, detecting when it finishes, remove ability.

    My point is to do this without a workaround, so I don't have quite so many triggers and event registries to go through.
     
  4. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    1,853
    Resources:
    0
    Resources:
    0
    Edit: Just saw that you explicitly stated that you don't want to use a Unit Indexer, but you also said "using custom value when the building enters the map", which is totally unnecessary.

    You'll have to use triggers.

    I would use a Unit Indexer as you can handle this with a single Boolean variable -> isConstructing[] = True/False.

    The triggers would be similar to what DoomBlade suggested.

    A unit begins construction -> Set Variable isConstructing[custom value of triggering unit] = True

    A unit finishes construction -> Set Variable isConstructing[custom value of triggering unit] = False

    Then anytime you want to check the state of a structure, you simply plug in it's custom value into isConstructing and you will get True or False. Example:
    • Events:
    • A unit dies
    • Conditions:
    • isConstructing[custom value of triggering unit] Equal to True
    • Actions:
    • Set Variable isConstructing[custom value of triggering unit] equal to False
    • Display text message "A constructing structure has been destroyed"

    GUI Unit Indexer 1.4.0.0

    Edit: Don't forget to set isConstructing to False when the structure dies. This ensures that the variable is reset for future uses.
     
    Last edited: Jul 31, 2020
  5. HerlySQR

    HerlySQR

    Joined:
    Jun 26, 2020
    Messages:
    208
    Resources:
    0
    Resources:
    0
    I think the best solution is this:
    • Events
      • Unit - A unit begins a construction
    • Conditions
    • Actions
      • Unit Group - Add Constructing Unit to Unit_Group

    • Events
      • Unit - A unit finishes a construction
    • Conditions
    • Actions
      • Unit Group - Remove Constructed Unit from Unit_Group

    • Events
      • Unit - A unit begins the cast of an ability
    • Conditions
      • Ability being cast Equal to Search
    • Actions
      • Unit Group - Pick Every in Unit_Group and "Do the search"
     
  6. Damage

    Damage

    Joined:
    Aug 31, 2009
    Messages:
    648
    Resources:
    6
    Maps:
    4
    Spells:
    2
    Resources:
    6
    I mean, you're clearly not reading my first post at all.
    I know you can achieve it using that method. My question is - can it be done WITHOUT using multiple events and setting flags / adding to groups?

    If not, I'll just use a simplified method of checking when a unit finishes construction, and then adding an ability with no icon. The search will then check for that ability. It's not ideal though, because it adds another event listener, which I'm trying to avoid if possible.

    For now at least, I've done it as follows (C# Code):
    Code (C#):


                trigger = CreateTrigger();
                TriggerAddAction(trigger, FinishedConstruction);
                for (var i = 0; i < 3; i++)
                {
                    TriggerRegisterPlayerUnitEvent(trigger, Player(i), EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, null);
                }


            public static void FinishedConstruction()
            {
                // Needed for repair bay logic.
                var building = GetTriggerUnit();
                if (GetUnitAbilityLevel(building, ABILITY_FINISHED_CONSTRUCTION) == 0)
                {
                    UnitAddAbility(building, ABILITY_FINISHED_CONSTRUCTION);
                }
            }
     
    The repairbay then just checks for the "Finished Construction" ability.
    Ideally, I'd like to do it without having to create events / triggers specifically for it.
     
    Last edited: Aug 2, 2020
  7. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,517
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    The "constructing" could also be set to "false" when building dies, or construction gets canceled.
    It might be no problem at first if there are checks for dead unit next to constructing check, but especially at later usage, when also UnitIndexer recylces some indices, then there could come unexpected results, if it still hold "constructing" as "true".
     
  8. Damage

    Damage

    Joined:
    Aug 31, 2009
    Messages:
    648
    Resources:
    6
    Maps:
    4
    Spells:
    2
    Resources:
    6
    There's no problem with the building being dead, the search logic filters out dead stuff already.
     
  9. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    1,853
    Resources:
    0
    Resources:
    0
    With Lua you can avoid the Unit Indexer entirely and just plug the units handle into the the Index.
    Code (Lua):

    --Create the table with a default value of False
    isConstructing = __jarray(False)

    --This would run in response to: EVENT_PLAYER_UNIT_CONSTRUCT_START
    local u = GetTriggerUnit()
    isConstructing[u] = True
     
    @IcemanBo
    True, forgot about that. I'll add it to the post.
     
    Last edited: Jul 31, 2020
  10. Drake53

    Drake53

    Joined:
    Jan 1, 2018
    Messages:
    350
    Resources:
    0
    Resources:
    0
    You can use [code=csharp] to enable C# syntax highlighting.
     
  11. Damage

    Damage

    Joined:
    Aug 31, 2009
    Messages:
    648
    Resources:
    6
    Maps:
    4
    Spells:
    2
    Resources:
    6
    Wouldn't this create an ever expanding array that eats up more and more memory as buildings are placed? Or am I wrong here?

    Assuming you set the index back to false again later, you've now created 2 listeners (Building starts constructing, building cancels / dies) - so you've actually made it worse than the original.

    Also, wouldn't it be the reverse implementation? Setting "isConstructed" to be __jarray(false) and then setting it to true when the building finishes construction. The method you described would set it to true the moment construction began, and it would stay true permanently after that (meaning there's no distinction between a building under construction, and one that has finished construction).
     
  12. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    1,853
    Resources:
    0
    Resources:
    0
    Not exactly sure about the technical side of it, I know Lua will automatically garbage collect but I don't know how that works with a Table. I was told by others to do this instead of using a traditional Unit Indexer, but i'm not sure if performance/efficiency was taken into consideration.

    You shouldn't need to set anything to False since the unit handles won't be reused.
     
  13. Damage

    Damage

    Joined:
    Aug 31, 2009
    Messages:
    648
    Resources:
    6
    Maps:
    4
    Spells:
    2
    Resources:
    6
    Lua only garbage collects if the table is no longer referenced or accessible. Assuming somewhere later, the Repair bay is going to check the Array for index (unit handle) I'm guessing it would not clear it out.
     
  14. Drake53

    Drake53

    Joined:
    Jan 1, 2018
    Messages:
    350
    Resources:
    0
    Resources:
    0
    Since you're using C# you can use a HashSet<unit> to keep track of buildings that are under construction.
    Code (C#):
        public static class ConstructingUnitTracker
        {
            private static readonly HashSet<unit> _constructingUnits = new HashSet<unit>();

            public static void Initialize()
            {
                var triggerAdd = CreateTrigger();
                TriggerAddCondition(triggerAdd, Condition(() =>
                {
                    _constructingUnits.Add(GetTriggerUnit());
                    return false;
                }));
               
                var triggerRemove = CreateTrigger();
                TriggerAddCondition(triggerRemove, Condition(() =>
                {
                    _constructingUnits.Remove(GetTriggerUnit());
                    return false;
                }));

                const int PlayerCount = 3;
                for (var i = 0; i < PlayerCount; i++)
                {
                    TriggerRegisterPlayerUnitEvent(triggerAdd, Player(i), EVENT_PLAYER_UNIT_CONSTRUCT_START, null);
                    TriggerRegisterPlayerUnitEvent(triggerRemove, Player(i), EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null);
                    TriggerRegisterPlayerUnitEvent(triggerRemove, Player(i), EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, null);
                }
            }

            public static bool IsUnitUnderConstruction(unit whichUnit) => _constructingUnits.Contains(whichUnit);
        }
    I tried with a static constructor at first, but that only runs when the class gets referenced the first time, so might as well use a method for it.
    To use it, just call 'ConstructingUnitTracker.Initialize();' somewhere at map init, and 'ConstructingUnitTracker.IsUnitUnderConstruction(whichUnit);' to check if a building is under construction.

    EDIT: Forgot about cases where a building dies or gets removed. For death, you can replace EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL with EVENT_PLAYER_UNIT_DEATH, since cancelling construction also triggers the death event. For remove, I don't think there's an event for that, so you'd probably have to remove the unit from the HashSet manually if you use RemoveUnit anywhere in your code.
     
    Last edited: Aug 2, 2020
  15. Damage

    Damage

    Joined:
    Aug 31, 2009
    Messages:
    648
    Resources:
    6
    Maps:
    4
    Spells:
    2
    Resources:
    6
    Though I really like that solution, it also creates 2 Listeners - One for the building starting construction, one for when it finishes.

    I'm beginning to think that my method is only one that uses a single listener...