[General] Trouble with Hashtable adding abilities to units

Level 9
Joined
Jun 13, 2010
Messages
365
Hi and thanks for taking your time to look at my post <3

My map will contain 50+ custom unit types, which each has 9 abilities (upgrades) to choose from (unique abilities). When a player chooses an upgrade for their given unit type, it will only apply to the specific players specific unit type (ex Footman for Player 1). When choosing the upgrade, all Footmen for player 1 will be added the specific ability (under conditions of max 3 total abilities and resource costs etc.) and future Footmen will have it as well for player 1 only when bought.

So far the upgrade system works, but some abilities are not added and when you buy a new unit, only the last bought ability is added and the new ones are not saved.

For this I have created (with help from chatgpt) a Hashtable system "TFT_UnitUpgradeTable" using this storing system using 999 and 998:

How many upgrades the players unit type has (max 3): Save ((Player number x 1000) + Custom value of Unit Type) of 999 in TFT_UnitUpgradeTable.

If the players unit type already has the upgrade 1=true, 0=not: ((Save Player number x 1000) + Custom value of Unit Type) of 998 in TFT_UnitUpgradeTable.

Does the players unit type has less than 3 (999) and not learned the specific ability (998) it is supposed to add the ability to add the ability.

999 is saved as variable TFT_AbilityUpgradesTableIndex.
998 is saved as variable TFT_AbilitySelectionTableIndex.

I will try to showcase my triggers here so far:

This trigger fires when Upgrade Ability on the Unit type is selected. It creates and targets a dummy unit with 9 abilities altered to the unit types abilities for selection.
  • TFT Upgrade Unit
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Upgrade
    • Actions
      • Set VariableSet TempUnit = (Triggering unit)
      • Set VariableSet TempUnitType = (Unit-type of TempUnit)
      • Set VariableSet TempPlayer = (Owner of TempUnit)
      • Set VariableSet TempInteger = (Player number of TempPlayer)
      • Set VariableSet TFT_UpgradeUnitType[TempInteger] = TempUnit
      • Unit - Remove TFT_UpgradeUnitArray[TempInteger] from the game
      • Set VariableSet TFT_UpgradeUnitArray[TempInteger] = No unit
      • Set VariableSet TempPoint = (Position of TempUnit)
      • Unit - Create 1 Upgrade Unit for TempPlayer at TempPoint facing Default building facing degrees
      • Custom script: call RemoveLocation(udg_TempPoint)
      • Set VariableSet TFT_UpgradeUnitArray[TempInteger] = (Last created unit)
      • Selection - Select TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add Q to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add W to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add E to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add A to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add S to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add D to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add Z to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add X to TFT_UpgradeUnitArray[TempInteger]
      • Unit - Add C to TFT_UpgradeUnitArray[TempInteger]
      • -------- --------
      • -------- ADD INFO TO ABILITIES --------
      • -------- --------
      • -------- --------
      • -------- --------
      • Custom script: set udg_TFT_IntUnitValue = udg_TempUnitType
      • Set VariableSet Roll_Integer = 1
      • Set VariableSet Roll_Start = 1
      • Set VariableSet Roll_Max = 9
      • For each (Integer Roll_Integer) from Roll_Start to Roll_Max, do (Actions)
        • Loop - Actions
          • Set VariableSet TFT_IntAbilitySlot = Roll_Integer
          • Set VariableSet TFT_IntFieldType = 1
          • Set VariableSet TFT_IntKey = ((TFT_IntAbilitySlot x TFT_IntFactor) + TFT_IntFieldType)
          • Set VariableSet TempString = (Load TFT_IntKey of TFT_IntUnitValue from TFT_UnitUpgradeInfoTable.)
          • Ability - Set Icon of TFT_UpgradeDummyAbilities[TFT_IntAbilitySlot] to TempString
          • Set VariableSet TFT_IntFieldType = 2
          • Set VariableSet TFT_IntKey = ((TFT_IntAbilitySlot x TFT_IntFactor) + TFT_IntFieldType)
          • Set VariableSet TempString = (Load TFT_IntKey of TFT_IntUnitValue from TFT_UnitUpgradeInfoTable.)
          • Ability - Set Tooltip of TFT_UpgradeDummyAbilities[TFT_IntAbilitySlot] to TempString for level 0
          • Set VariableSet TFT_IntFieldType = 3
          • Set VariableSet TFT_IntKey = ((TFT_IntAbilitySlot x TFT_IntFactor) + TFT_IntFieldType)
          • Set VariableSet TempString = (Load TFT_IntKey of TFT_IntUnitValue from TFT_UnitUpgradeInfoTable.)
          • Ability - Set Extended Tooltip of TFT_UpgradeDummyAbilities[TFT_IntAbilitySlot] to TempString for level 0
Now you select the ability you want in the dummy unit menu, checking for the saved data in the hashtable. If it matches the conditions it will run TFT Apply Abilities in the second trigger below.

  • TFT Upgrade Ability Chosen
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Upgrade Unit
      • Or - Any (Conditions) are true
        • Conditions
          • (Ability being cast) Equal to Q
          • (Ability being cast) Equal to W
          • (Ability being cast) Equal to E
          • (Ability being cast) Equal to A
          • (Ability being cast) Equal to S
          • (Ability being cast) Equal to D
          • (Ability being cast) Equal to Z
          • (Ability being cast) Equal to X
          • (Ability being cast) Equal to C
    • Actions
      • Set VariableSet TempAbility = (Ability being cast)
      • Set VariableSet TempUnit = (Triggering unit)
      • Set VariableSet TempPlayer = (Owner of TempUnit)
      • Set VariableSet TempInteger = (Player number of TempPlayer)
      • Set VariableSet TempInteger1 = (Custom value of TFT_UpgradeUnitType[TempInteger])
      • Set VariableSet TempInteger2 = ((TempInteger x TFT_UpgradePlayerFactor) + TempInteger1)
      • Set VariableSet TempInteger3 = (Load TempInteger2 of TFT_AbilityUpgradesTableIndex from TFT_UnitUpgradeTable.)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • TempAbility Equal to Q
        • Then - Actions
          • Set VariableSet TempInteger4 = 1
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempAbility Equal to W
            • Then - Actions
              • Set VariableSet TempInteger4 = 2
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • TempAbility Equal to E
                • Then - Actions
                  • Set VariableSet TempInteger4 = 3
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • TempAbility Equal to A
                    • Then - Actions
                      • Set VariableSet TempInteger4 = 4
                    • Else - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • TempAbility Equal to S
                        • Then - Actions
                          • Set VariableSet TempInteger4 = 5
                        • Else - Actions
                          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                            • If - Conditions
                              • TempAbility Equal to D
                            • Then - Actions
                              • Set VariableSet TempInteger4 = 6
                            • Else - Actions
                              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                • If - Conditions
                                  • TempAbility Equal to Z
                                • Then - Actions
                                  • Set VariableSet TempInteger4 = 7
                                • Else - Actions
                                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                    • If - Conditions
                                      • TempAbility Equal to X
                                    • Then - Actions
                                      • Set VariableSet TempInteger4 = 8
                                    • Else - Actions
                                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                                        • If - Conditions
                                          • TempAbility Equal to C
                                        • Then - Actions
                                          • Set VariableSet TempInteger4 = 9
                                        • Else - Actions
      • Set VariableSet TempInteger5 = (TempInteger2 + TempInteger4)
      • Set VariableSet TempInteger6 = (Load TempInteger5 of TFT_AbilitySelectionTableIndex from TFT_UnitUpgradeTable.)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Tooltip of TempAbility for level 0) Equal to Unknown
        • Then - Actions
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TempInteger6 Greater than or equal to 1
            • Then - Actions
              • Player Group - Add TempPlayer to TempPlayerGroup
              • Game - Display to TempPlayerGroup the text: You have chosen thi...
              • Player Group - Remove all players from TempPlayerGroup.
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • TempInteger3 Greater than or equal to TFT_UpgradesMax[TempInteger]
                • Then - Actions
                  • Player Group - Add TempPlayer to TempPlayerGroup
                  • Game - Display to TempPlayerGroup the text: (You already have selected the maximum number of abilities for this unit type: ( + ((String(TFT_UpgradesMax[TempInteger])) + )))
                  • Player Group - Remove all players from TempPlayerGroup.
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (TempPlayer Current gold) Less than TFT_UpgradeCost[TempInteger]
                    • Then - Actions
                      • Player Group - Add TempPlayer to TempPlayerGroup
                      • Game - Display to TempPlayerGroup the text: (Insufficient gold ( + ((String(TFT_UpgradeCost[TempInteger])) + ).))
                      • Player Group - Remove all players from TempPlayerGroup.
                    • Else - Actions
                      • Player - Set TempPlayer.Current gold to ((TempPlayer Current gold) - TFT_UpgradeCost[TempInteger])
                      • Set VariableSet TempString = (Load ((TempInteger4 x TFT_IntFactor) + 2) of TempInteger1 from TFT_UnitUpgradeInfoTable.)
                      • Player Group - Add TempPlayer to TempPlayerGroup
                      • Game - Display to TempPlayerGroup the text: (You have learned |cffffcc00 + (TempString + |r for this unit type.))
                      • Player Group - Remove all players from TempPlayerGroup.
                      • Hashtable - Save (TempInteger3 + 1) as TempInteger2 of TFT_AbilityUpgradesTableIndex in TFT_UnitUpgradeTable.
                      • Hashtable - Save 1 as TempInteger5 of TFT_AbilitySelectionTableIndex in TFT_UnitUpgradeTable.
                      • Set VariableSet TFT_UnitAddAbility = TempUnit
                      • Trigger - Run TFT Apply Abilities <gen> (checking conditions)

  • TFT Apply Abilities
    • Events
    • Conditions
    • Actions
      • Set VariableSet TFT_EnteringPlayer = (Owner of TFT_UnitAddAbility)
      • Set VariableSet TFT_EnteringIntegerPlayer = (Player number of TFT_EnteringPlayer)
      • Set VariableSet TFT_EnteringUnitCustomValue = (Custom value of TFT_UnitAddAbility)
      • Set VariableSet TFT_EnteringUnitTypePlayerID = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
      • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitTypePlayerID)
      • Set VariableSet Roll_Integer = 1
      • Set VariableSet Roll_Max = 1
      • Set VariableSet Roll_Max = 9
      • For each (Integer Roll_Integer) from Roll_Start to Roll_Max, do (Actions)
        • Loop - Actions
          • Set VariableSet TFT_EnteringUnitTypeAbilityID = (Load (TFT_EnteringUnitTypeKey + Roll_Integer) of TFT_AbilitySelectionTableIndex from TFT_UnitUpgradeTable.)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TFT_EnteringUnitTypeAbilityID Equal to 1
            • Then - Actions
              • -------- FOOTMAN --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Unit-type of TFT_UnitAddAbility) Equal to Footman
                • Then - Actions
                  • Set VariableSet TempAbility = TFT_AbilityTypesFootman[Roll_Integer]
                • Else - Actions
            • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Level of TempAbility for TFT_UnitAddAbility) Greater than or equal to 1
            • Then - Actions
            • Else - Actions
              • Unit - Add TempAbility to TFT_UnitAddAbility

I split them up to be able to run Apply Abilities trigger for all units when chosen and for future units when entering the map.

The ability variables for dummy unit and for unit types are preset here:

  • TFT Upgrade Ability Index
    • Events
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet TFT_UnitUpgradeTable = (Last created hashtable)
      • Hashtable - Create a hashtable
      • Set VariableSet TFT_UnitUpgradeInfoTable = (Last created hashtable)
      • -------- ABILITY INFO --------
      • Set VariableSet TFT_UpgradeDummyAbilities[1] = Q
      • Set VariableSet TFT_UpgradeDummyAbilities[2] = W
      • Set VariableSet TFT_UpgradeDummyAbilities[3] = E
      • Set VariableSet TFT_UpgradeDummyAbilities[4] = A
      • Set VariableSet TFT_UpgradeDummyAbilities[5] = S
      • Set VariableSet TFT_UpgradeDummyAbilities[6] = D
      • Set VariableSet TFT_UpgradeDummyAbilities[7] = Z
      • Set VariableSet TFT_UpgradeDummyAbilities[8] = X
      • Set VariableSet TFT_UpgradeDummyAbilities[9] = C
      • Trigger - Run TFT Upgrade Ability Index Human <gen> (checking conditions)
      • -------- Footman --------
      • Set VariableSet TFT_AbilityTypesFootman[1] = Defend (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[2] = Taunt (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[3] = Enchanted (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[4] = Retaliate (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[5] = Bulwark (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[6] = Oathbound (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[7] = Outpost (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[8] = Shield Bash (Footman)
      • Set VariableSet TFT_AbilityTypesFootman[9] = To Arms! (Footman)

This is for adding abilities when a unit enters the map:
  • TFT Entering Units
    • Events
      • Unit - A unit enters (Entire map)
    • Conditions
      • ((Triggering unit) is A structure) Not equal to True
      • ((Triggering unit) is A Hero) Not equal to True
      • ((Owner of (Triggering unit)) is in TFT_PlayerGroup.) Equal to True
      • And - All (Conditions) are true
        • Conditions
          • (Unit-type of (Triggering unit)) Not equal to Upgrade Unit
          • (Unit-type of (Triggering unit)) Not equal to Dummy (AbilityDummy)
          • (Unit-type of (Triggering unit)) Not equal to Dummy (Recast)
          • (Unit-type of (Triggering unit)) Not equal to To the Death!
    • Actions
      • Set VariableSet TFT_EnteringUnit = (Triggering unit)
      • Set VariableSet StatUnit = TFT_EnteringUnit
      • Custom script: set udg_TFT_EnteringInteger1 = GetUnitTypeId(udg_TFT_EnteringUnit)
      • Unit - Set the custom value of TFT_EnteringUnit to TFT_EnteringInteger1
      • Trigger - Run Calculate Unit Base Stats <gen> (ignoring conditions)
      • Set VariableSet TFT_UnitAddAbility = TFT_EnteringUnit
      • Trigger - Run TFT Apply Abilities <gen> (checking conditions)
      • Trigger - Run Calculate Unit Apply Stat Upgrades <gen> (ignoring conditions)
      • Trigger - Run Calculate Unit Stat Retriever <gen> (ignoring conditions)
      • Unit - Add Upgrade to TFT_EnteringUnit


I am having trouble with this because I think I have made a more detailed system than intended and I am not used to be working both MUI and hashtables...

I hope someone may be able to help me. I have attached the map. Thanks in advance!
 

Attachments

  • Custom Hero Team Defense (testing new).w3m
    175.7 KB · Views: 3
I definitely recommend using debug messages where possible--often times this comes down to using the wrong key in the hashtable. :thumbs_up: For example, in "TFT Apply Abilities", I would add this debug message in the loop (after assigning a value to TFT_EnteringUnitTypeAbilityID):
  • Set VariableSet TFT_EnteringUnitTypeAbilityID = (Load (TFT_EnteringUnitTypeKey + Roll_Integer) of TFT_AbilitySelectionTableIndex from TFT_UnitUpgradeTable.)
  • Game - Display to (All players) the text: (EnteringUnitTypeAbilityID: + (String(TFT_EnteringUnitTypeAbilityID)))
From here, when you trigger the upgrade, you'll notice it is printing 0 (which shouldn't be the case since you just upgraded one of the abilities). So the "load" is either not loading from the correct keys, or you're not saving the value correctly. In this case, I think the problem lies in the key since you're adding the PlayerID when you probably mean to add the EnteringUnitCustomValue:
  • -------- Before --------
  • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitTypePlayerID)
  • -------- After --------
  • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
Once you make that change, you should start seeing it print out "1" in your debug message (and it'll start adding the abilities correctly for future units).

So if it was loading from the wrong key, why was it adding the "last" ability before this change? That was actually just a sneaky side-effect/bug. Your code sets TempAbility in "TFT Upgrade Ability Chosen", and then you also use that variable in "TFT Apply Abilities" to add the ability (even if it fails the condition when loading the value from the hashtable). So it'll add the "last cast ability" even though the hashtable didn't hold the correct value. Typically, I'd recommend using unique variables for triggers to avoid these types of bugs. But I also recommend setting the ability variable to 0 in the "else" case, just to make sure it doesn't add an ability that it isn't supposed to:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • TFT_EnteringUnitTypeAbilityID Equal to 1
    • Then - Actions
      • -------- FOOTMAN --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of TFT_UnitAddAbility) Equal to Footman
        • Then - Actions
          • Set VariableSet TempAbility = TFT_AbilityTypesFootman[Roll_Integer]
        • Else - Actions
    • Else - Actions
      • Custom script: set udg_TempAbility = 0
So then the last issue is that it adds the upgrade ability to all future units, but not all the current units of that type. For example, if I train a footman, and then upgrade Defend--it'll add Defend to all the future footmen that get spawned, but not all the current footmen. This happens for two reasons:
  1. When you cast the upgrade ability, TFT_UnitAddAbility is actually pointing to the "Upgrade Unit" (not the original footman), since the "Upgrade Unit" is the one that casts the ability.
  2. Even if you fix that issue above^, it still would only add the ability for the original footmen (but not the rest of the footmen on the map). So instead, you could just do a "Unit Group - Pick every unit in (Playable map area) of type <Footman> belonging to <Player>", set TFT_UnitAddAbility to the picked unit, and then run "TFT ApplyAbilities <gen>"


With those changes, I think your code should work as expected. But I do think it is still a bit complicated for your purposes (and I think all the "Temp" variables can get quickly confusing, e.g. the TempIntegers haha). If I were designing it from scratch, I'd probably go for something a bit simpler. Here are a few suggestions:
  1. Instead of having the "Upgrade Unit", I would consider just putting a spell-book on the unit with the list of abilities that they need.

    That way the player can easily cancel out back to the original unit. It also simplifies some of the logic around who is "casting" the upgrade ability (e.g. like in the bug above where TFT_UnitAddAbility is referring to the "Upgrade Unit" instead of the original unit). It might also simplify some of the logic for having to deal with a generic Q/W/E/R/etc. abilities for all units. You're likely to end up with one mega-trigger that is hard to manage.
  2. I would recommend separating the "Upgrade" abilities from the actual abilities. For example, "Research Defend" should probably be its own ability and "Defend" should be its own separate ability that gets granted as a result. Otherwise it can get really hairy to manage.
  3. It is quite hard to get the exact abstraction you want in GUI (sadly). As you can see, it gets really complicated quite fast--you sometimes still end up making specific logic that breaks the abstraction (e.g. the "if <Type> equal to Footman then use the TFT_AbilityTypesFootman variable"), especially since GUI's hashtable API is so limited. As you start to add more units, you're probably going to end up having to add a lot more unit-type-specific variables and a bunch of if-conditions.

    Whenever the abstraction breaks and you start going from generic code to unit-type-specific code, it is worth taking a step back and seeing if things could be simplified. I can try to give some suggestions on how to simplify it a little later if you'd like. But in general, a good approach is to not prematurely optimize: it is often better to first get things working for a few units with highly specific code, and then to refactor it to something more general! :) That way you can see the whole picture before having to code the long-lasting solution.
  4. I think there might be alternatives to how you use the hashtable/keys to simplify things. For example, ability IDs/unit-type IDs are technically just integers, but GUI doesn't quite make it easy to access that. But with some custom scripts/helper functions, the code can probably be simplified quite a bit! (again, I can give a separate example later if it'd help!)
 
Level 9
Joined
Jun 13, 2010
Messages
365
I definitely recommend using debug messages where possible--often times this comes down to using the wrong key in the hashtable. :thumbs_up: For example, in "TFT Apply Abilities", I would add this debug message in the loop (after assigning a value to TFT_EnteringUnitTypeAbilityID):
  • Set VariableSet TFT_EnteringUnitTypeAbilityID = (Load (TFT_EnteringUnitTypeKey + Roll_Integer) of TFT_AbilitySelectionTableIndex from TFT_UnitUpgradeTable.)
  • Game - Display to (All players) the text: (EnteringUnitTypeAbilityID: + (String(TFT_EnteringUnitTypeAbilityID)))
From here, when you trigger the upgrade, you'll notice it is printing 0 (which shouldn't be the case since you just upgraded one of the abilities). So the "load" is either not loading from the correct keys, or you're not saving the value correctly. In this case, I think the problem lies in the key since you're adding the PlayerID when you probably mean to add the EnteringUnitCustomValue:
  • -------- Before --------
  • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitTypePlayerID)
  • -------- After --------
  • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
Once you make that change, you should start seeing it print out "1" in your debug message (and it'll start adding the abilities correctly for future units).

So if it was loading from the wrong key, why was it adding the "last" ability before this change? That was actually just a sneaky side-effect/bug. Your code sets TempAbility in "TFT Upgrade Ability Chosen", and then you also use that variable in "TFT Apply Abilities" to add the ability (even if it fails the condition when loading the value from the hashtable). So it'll add the "last cast ability" even though the hashtable didn't hold the correct value. Typically, I'd recommend using unique variables for triggers to avoid these types of bugs. But I also recommend setting the ability variable to 0 in the "else" case, just to make sure it doesn't add an ability that it isn't supposed to:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • TFT_EnteringUnitTypeAbilityID Equal to 1
    • Then - Actions
      • -------- FOOTMAN --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of TFT_UnitAddAbility) Equal to Footman
        • Then - Actions
          • Set VariableSet TempAbility = TFT_AbilityTypesFootman[Roll_Integer]
        • Else - Actions
    • Else - Actions
      • Custom script: set udg_TempAbility = 0
So then the last issue is that it adds the upgrade ability to all future units, but not all the current units of that type. For example, if I train a footman, and then upgrade Defend--it'll add Defend to all the future footmen that get spawned, but not all the current footmen. This happens for two reasons:
  1. When you cast the upgrade ability, TFT_UnitAddAbility is actually pointing to the "Upgrade Unit" (not the original footman), since the "Upgrade Unit" is the one that casts the ability.
  2. Even if you fix that issue above^, it still would only add the ability for the original footmen (but not the rest of the footmen on the map). So instead, you could just do a "Unit Group - Pick every unit in (Playable map area) of type <Footman> belonging to <Player>", set TFT_UnitAddAbility to the picked unit, and then run "TFT ApplyAbilities <gen>"


With those changes, I think your code should work as expected. But I do think it is still a bit complicated for your purposes (and I think all the "Temp" variables can get quickly confusing, e.g. the TempIntegers haha). If I were designing it from scratch, I'd probably go for something a bit simpler. Here are a few suggestions:
  1. Instead of having the "Upgrade Unit", I would consider just putting a spell-book on the unit with the list of abilities that they need.

    That way the player can easily cancel out back to the original unit. It also simplifies some of the logic around who is "casting" the upgrade ability (e.g. like in the bug above where TFT_UnitAddAbility is referring to the "Upgrade Unit" instead of the original unit). It might also simplify some of the logic for having to deal with a generic Q/W/E/R/etc. abilities for all units. You're likely to end up with one mega-trigger that is hard to manage.
  2. I would recommend separating the "Upgrade" abilities from the actual abilities. For example, "Research Defend" should probably be its own ability and "Defend" should be its own separate ability that gets granted as a result. Otherwise it can get really hairy to manage.
  3. It is quite hard to get the exact abstraction you want in GUI (sadly). As you can see, it gets really complicated quite fast--you sometimes still end up making specific logic that breaks the abstraction (e.g. the "if <Type> equal to Footman then use the TFT_AbilityTypesFootman variable"), especially since GUI's hashtable API is so limited. As you start to add more units, you're probably going to end up having to add a lot more unit-type-specific variables and a bunch of if-conditions.

    Whenever the abstraction breaks and you start going from generic code to unit-type-specific code, it is worth taking a step back and seeing if things could be simplified. I can try to give some suggestions on how to simplify it a little later if you'd like. But in general, a good approach is to not prematurely optimize: it is often better to first get things working for a few units with highly specific code, and then to refactor it to something more general! :) That way you can see the whole picture before having to code the long-lasting solution.
  4. I think there might be alternatives to how you use the hashtable/keys to simplify things. For example, ability IDs/unit-type IDs are technically just integers, but GUI doesn't quite make it easy to access that. But with some custom scripts/helper functions, the code can probably be simplified quite a bit! (again, I can give a separate example later if it'd help!)

First of all I just want to say I really appreciate your help and even being this thorough! I honestly didn't expect a response since it felt like a major thing for anyone to invest time in. Thank you a lot!

I did as you said:
  • Set VariableSet TFT_EnteringUnitTypePlayerID = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
  • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
Now it works. It's always these small stupid mistakes you end up spending countless hours on. xD
But why am I making the same variable twice now then? Since the equation is the same for both PlayerID and the Key. What am I doing...

I also added the TempAbility to null as you mentioned:
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • TFT_EnteringUnitTypeAbilityID Equal to 1
    • Then - Actions
      • -------- FOOTMAN --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of TFT_UnitAddAbility) Equal to Footman
        • Then - Actions
          • Set VariableSet TempAbility = TFT_AbilityTypesFootman[Roll_Integer]
        • Else - Actions
    • Else - Actions
      • Custom script: set udg_TempAbility = 0

I wanted the trigger "TFT Apply Abilities" to be able to both apply the ability as it does now, but also be used for units already present in the map (which it doesn't do for now, because (as you stated) it doesn't pick the units already present in the map.

Would I just do that by adding a check in the end of the same trigger to add all units in the map to a group and check if the specific unit type for player has the ability as level 1 or higher and if not add the ability to it. Like so:
  • TFT Apply Abilities
    • Events
    • Conditions
    • Actions
      • Set VariableSet TFT_EnteringPlayer = (Owner of TFT_UnitAddAbility)
      • Set VariableSet TFT_EnteringIntegerPlayer = (Player number of TFT_EnteringPlayer)
      • Set VariableSet TFT_EnteringUnitCustomValue = (Custom value of TFT_UnitAddAbility)
      • Set VariableSet TFT_EnteringUnitTypePlayerID = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
      • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
      • Set VariableSet Roll_Integer = 1
      • Set VariableSet Roll_Max = 1
      • Set VariableSet Roll_Max = 9
      • For each (Integer Roll_Integer) from Roll_Start to Roll_Max, do (Actions)
        • Loop - Actions
          • Set VariableSet TFT_EnteringUnitTypeAbilityID = (Load (TFT_EnteringUnitTypeKey + Roll_Integer) of TFT_AbilitySelectionTableIndex from TFT_UnitUpgradeTable.)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TFT_EnteringUnitTypeAbilityID Equal to 1
            • Then - Actions
              • -------- FOOTMAN --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Unit-type of TFT_UnitAddAbility) Equal to Footman
                • Then - Actions
                  • Set VariableSet TempAbility = TFT_AbilityTypesFootman[Roll_Integer]
                • Else - Actions
            • Else - Actions
              • Custom script: set udg_TempAbility = 0
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Level of TempAbility for TFT_UnitAddAbility) Greater than or equal to 1
            • Then - Actions
            • Else - Actions
              • Unit - Add TempAbility to TFT_UnitAddAbility
          • Set VariableSet TFT_AbilityAddGroup = (Units in (Playable map area))
          • Unit Group - Pick every unit in TFT_AbilityAddGroup and do (Actions)
            • Loop - Actions
              • Set VariableSet TFT_AbilityAddGroupPicked = (Picked unit)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Custom value of TFT_AbilityAddGroupPicked) Equal to TFT_EnteringUnitCustomValue
                  • (Owner of TFT_AbilityAddGroupPicked) Equal to TFT_EnteringPlayer
                  • (TFT_AbilityAddGroupPicked is alive) Equal to True
                  • (Level of TempAbility for TFT_AbilityAddGroupPicked) Less than or equal to 0
                • Then - Actions
                  • Unit - Add TempAbility to TFT_AbilityAddGroupPicked
                • Else - Actions
          • Custom script: call DestroyGroup (udg_TFT_AbilityAddGroup)
But I am afraid it isn't working though (might it be "set udg_TempAbility = 0" that sets it to none and then afterwards it doesn't recall the TempAbility?) - and I also want to make sure it doesn't cause lag issues, as I don't quite fully understand how much these actions will cause performance issues or if they are piece of cake to do for the PC.

So I might take you up on your offer - any suggestions? :)

Again thanks! You helped me a great deal so far already!
 
Last edited:
good q, you can do it that way--but that would be quite expensive to do since that trigger also runs any time a unit enters the map. Instead, I would keep the functionality of "TFT Apply Abilities" as-is (to work on a single unit parameter), and then create a separate trigger "TFT Apply Abilities All Units" to handle applying it to everyone. But to do this, we'll need to do a few steps:
  1. The reason that your code isn't working is because the unit casting the research ability is the "Upgrade Unit", not the "Footman". So the code is grabbing the wrong unit-type. To keep things simple, I recommend opening up the "TFT Upgrade Unit" trigger and then adding a line to set the custom value of the upgrade unit to match the original unit. That way you can know which unit-type the upgrade is for:
    • Unit - Create 1 Upgrade Unit for TempPlayer at TempPoint facing Default building facing degrees
    • Unit - Set the custom value of (Last created unit) to (Custom value of (Triggering unit))
  2. Your code will probably work now, but I recommend you remove your changes so we can move that functionality into its own trigger (for performance reasons).
  3. Next, let's make our helper trigger that applies abilities for all units of a given type.
    • TFT Apply Abilities All Units
      • Events
      • Conditions
      • Actions
        • Custom script: set udg_TempUnitType = GetUnitUserData(udg_TFT_UnitAddAbility)
        • Set VariableSet TFT_AbilityAddGroup = (Units owned by (Owner of TFT_UnitAddAbility) of type TempUnitType)
        • Unit Group - Pick every unit in TFT_AbilityAddGroup and do (Actions)
          • Loop - Actions
            • Set VariableSet TFT_UnitAddAbility = (Picked unit)
            • Trigger - Run TFT Apply Abilities <gen> (ignoring conditions)
        • Custom script: call DestroyGroup(udg_TFT_AbilityAddGroup)
    In this trigger, we'll grab the units of a particular type owned by the correct player, and then run "TFT Apply Abilities" on each of the units. This way, we get to re-use most of the logic, but we get to apply it to all units of a type instead. The first line assigns TempUnitType to the custom value of the input unit (TFT_UnitAddAbility). This way it'll work regardless of whether TFT_UnitAddAbility is a Footman or an Upgrade Unit (since we made the change in #1 to "inherit" the custom value of the original footman). Feel free to add any other conditions in an if-then-else in the "Loop - Actions" (e.g. checking that the unit is alive).
  4. Lastly, we'll need to update "TFT Upgrade Ability Chosen" to run the "All Units" trigger instead of the single-unit trigger:
    • Set VariableSet TFT_UnitAddAbility = TempUnit
    • Trigger - Run TFT Apply Abilities All Units <gen> (checking conditions)
After this, it should correctly add the abilities to all existing units--and to individual units whenever they enter the map. When I tested it in-game it worked, but I noticed there were still some bugs with the tooltips and such. :grin: That might be its own issue to look at separately. The icons seem correct though.
 
Level 9
Joined
Jun 13, 2010
Messages
365
Your code will probably work now, but I recommend you remove your changes so we can move that functionality into its own trigger (for performance reasons).
Okay great! By removing the changes, are you referring to the Unit Group actions at the bottom?
  • TFT Apply Abilities
    • Events
    • Conditions
    • Actions
      • Set VariableSet TFT_EnteringPlayer = (Owner of TFT_UnitAddAbility)
      • Set VariableSet TFT_EnteringIntegerPlayer = (Player number of TFT_EnteringPlayer)
      • Set VariableSet TFT_EnteringUnitCustomValue = (Custom value of TFT_UnitAddAbility)
      • Set VariableSet TFT_EnteringUnitTypePlayerID = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
      • Set VariableSet TFT_EnteringUnitTypeKey = ((TFT_EnteringIntegerPlayer x TFT_UpgradePlayerFactor) + TFT_EnteringUnitCustomValue)
      • Set VariableSet Roll_Integer = 1
      • Set VariableSet Roll_Max = 1
      • Set VariableSet Roll_Max = 9
      • For each (Integer Roll_Integer) from Roll_Start to Roll_Max, do (Actions)
        • Loop - Actions
          • Set VariableSet TFT_EnteringUnitTypeAbilityID = (Load (TFT_EnteringUnitTypeKey + Roll_Integer) of TFT_AbilitySelectionTableIndex from TFT_UnitUpgradeTable.)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • TFT_EnteringUnitTypeAbilityID Equal to 1
            • Then - Actions
              • -------- FOOTMAN --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Unit-type of TFT_UnitAddAbility) Equal to Footman
                • Then - Actions
                  • Set VariableSet TempAbility = TFT_AbilityTypesFootman[Roll_Integer]
                • Else - Actions
            • Else - Actions
              • Custom script: set udg_TempAbility = 0
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Level of TempAbility for TFT_UnitAddAbility) Greater than or equal to 1
            • Then - Actions
            • Else - Actions
              • Unit - Add TempAbility to TFT_UnitAddAbility
          • Set VariableSet TFT_AbilityAddGroup = (Units in (Playable map area))
          • Unit Group - Pick every unit in TFT_AbilityAddGroup and do (Actions)
            • Loop - Actions
              • Set VariableSet TFT_AbilityAddGroupPicked = (Picked unit)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Custom value of TFT_AbilityAddGroupPicked) Equal to TFT_EnteringUnitCustomValue
                  • (Owner of TFT_AbilityAddGroupPicked) Equal to TFT_EnteringPlayer
                  • (TFT_AbilityAddGroupPicked is alive) Equal to True
                  • (Level of TempAbility for TFT_AbilityAddGroupPicked) Less than or equal to 0
                • Then - Actions
                  • Game - Display to (All players) the text: (String(TFT_EnteringUnitCustomValue))
                  • Unit - Add TempAbility to TFT_AbilityAddGroupPicked
                • Else - Actions
          • Custom script: call DestroyGroup (udg_TFT_AbilityAddGroup)
But keep this part?:

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Level of TempAbility for TFT_UnitAddAbility) Greater than or equal to 1
    • Then - Actions
    • Else - Actions
      • Unit - Add TempAbility to TFT_UnitAddAbility
I did as the above and I can see it working. :) Thanks for all your help! I Would definitely had spend days maybe even weeks trying to wrap my brain around this.

I believe the icons and tooltips were working with no problems before - some are named Unknown with the ? icon because I haven't finished them yet. :)
By picking an Ability matching conditions named Unknown, nothing will happen to ensure no bugs or false money spend until i finish them.

Edit: I see now that the Knight is granted the To Arms! ability from Footman lol. Maybe some code from the Hashtable is interferring...?
 
Last edited:
Yep that was exactly it!

Not sure about the "To Arms!" case--that wasn't happening on my test map. 🤔 But it could be another small coding error. I think one of the biggest failings of the hashtable API in GUI is that they don't let you easily use IDs as keys (without using some JASS to re-interpret it as an integer). And having to click through all the menus and adjust the keys is quite painful, so it is easy to make mistakes. I'll attach the edits I made to your map down below if you want to try it and see if there are any differences.

One thing that could cause problems though is that the way you have your keys might cause some collisions (i.e. you might accidentally overwrite things when you don't mean to). I think ChatGPT might've suggested doing "player-number * X + unit-id" to try to combine multiple identifying keys into one--but that may not necessarily be unique. Especially when you also add the ability index (e.g. + Roll_Integer) into it without some transformations. It is a bit hard to explain concisely, but that method of combining things typically relies on the indices starting from 0 (or 1) and then multiplying it by some maximum size you need. But if you have IDs (which are just really large numbers computed from the rawcode), those aren't going to fall neatly into that pattern. And then if you add an offset without also adding a dimension to it (e.g. + Roll_Integer * Y), you run a risk of running into issues there. It might work fine for now, but it might get problematic when you start testing with multiple players and it can be a pain to debug. I have some suggestions below that address it in my sample map (essentially, use the unit-ID as the parent key and then use the other parts as the child key).

I did get around to making a sample map with some of the changes I suggested. To test it, make sure you enable JassHelper (go to the Trigger Editor > JassHelper > Enable JassHelper). It is similar to what you have in a lot of ways, but the main differences are:
  1. It uses spell-books (which simplifies some of the casting logic, and means you can configure things more easily in the object editor per unit, rather than code)
  2. It mixes in a fair amount of JASS via custom scripts. It makes working with the hashtables a lot easier. Instead of having to assign custom values to units in order to retrieve their unit-type as an integer, you can just directly use their unit-type as a key in JASS (or ability-codes for that matter!)--since all those IDs are actually just integers under-the-hood. For example, here is how I keep track of which ability should be granted for a particular upgrade.
    • -------- Configuration --------
    • Set VariableSet UpgradeInputAbility[1] = Upgrade Roar (Footman)
    • Set VariableSet UpgradeOutputAbility[1] = Roar
    • Set VariableSet UpgradeInputAbilityGoldCost[1] = 5
    • -------- --------
    • -------- Later on in the trigger, in a loop... --------
    • -------- --------
    • -------- --------
    • -------- Saves the output ability in the hashtable under the keys: <Input Ability, 0> --------
    • -------- This way we can quickly look-up the ability to grant the unit when the upgrade is cast --------
    • -------- --------
    • Custom script: call SaveInteger(udg_UpgradeHashtable, udg_UpgradeInputAbility[udg_UpgradeLoopInt], 0, udg_UpgradeOutputAbility[udg_UpgradeLoopInt])
    It can be a bit hard to get used to at first, but it is definitely worth mixing it in to save yourself time! If you're ever unsure about how to put in a certain value as an argument to a function, you can always just make a new trigger with the thing you want the JASS equivalent of > Edit > Convert to Custom Text (since ultimately, GUI gets converted to JASS). Or you can simply assign a global variable to the value you want and then reference it in JASS by putting udg_ and then the name of the variable (e.g. udg_UpgradeHashtable). And for reference, the functions for saving an integer or loading an integer from a hashtable in JASS are listed below (that way you know what each argument means):
    JASS:
    native  SaveInteger                        takes hashtable table, integer parentKey, integer childKey, integer value returns nothing
    native  LoadInteger                    takes hashtable table, integer parentKey, integer childKey returns integer
  3. I also created some helper functions for some of the more complex upgrade-saving logic. For example, in your code you had a rather complex parent key (player-number * X + unit-id + index), because you wanted to track an ability as researched for a given <player number + unit-id + index> combination. The code below tweaks it slightly so it stores the keys as <unit-type (parent key), playerId * X + index (child key)>. This avoids the problem mentioned above since the player ID and indices start from 0 or 1 and have a fixed range--so that leads to predictable keys that won't collide with one another. The code is relatively straightforward, it just tracks a few values in a hashtable. But by putting it into a function, you can easily call it in GUI without having to fiddle with as many windows/variables/complexity. For example, this allows you to essentially track information like this:

    < 'hfoo' (Footman), Player 1, 0 > => 2 upgrades researched
    < 'hfoo' (Footman), Player 1, 1 > => Ability "To Arms!"
    < 'hfoo' (Footman), Player 1, 2 > => Ability "Defend"

    < 'hfoo' (Footman), Player 2, 0 > => 1 upgrade researched
    < 'hfoo' (Footman), Player 2, 1 > => Ability "Defend"

    This organization allows us to quickly identify how many upgrades have been researched for a unit-type for a given player, and which abilities were researched. That way when a unit enters the map, we can simply loop through them to grant the unit its abilities.

    Here is the JASS code below:
    JASS:
    /**
    *
    * Contains simple helper functions to make saving and loading abilities easier.
    *
    */
    
    library UpgradeHashtableUtilities
        globals
            private hashtable hash = InitHashtable()
    
            // This can be anything, it just has to be a number greater than the maximum
            // number of upgrades per unit
            private constant integer MAX_UPGRADES = 1000
        endglobals
    
        /**
         * Returns the number of upgrades researched for the <unit-type, player>.
         */
        function GetUpgradeCount takes integer unitCode, player p returns integer
            local integer pid = GetPlayerId(p) * MAX_UPGRADES
            return LoadInteger(hash, unitCode, pid)
        endfunction
    
        /**
         * Marks an ability <abilCode> as researched for the given <unit-type, player>.
         */
        function SaveUpgrade takes integer unitCode, player p, integer abilCode returns nothing
            local integer pid = GetPlayerId(p) * MAX_UPGRADES
            local integer upgradesSoFar = GetUpgradeCount(unitCode, p)
            call SaveInteger(hash, unitCode, pid + upgradesSoFar + 1, abilCode)
            call SaveInteger(hash, unitCode, pid, upgradesSoFar + 1)
        endfunction
    
        /**
         * Loads the ability that was researched for the given <unit-type, player, index>.
         * The index (e.g. between 1 and 9) can be used to grab the 1st, 2nd, 3rd etc. ability
         * that was researched for this unit.
         *
         * Returns 0 if no upgrade exists.
         */
        function LoadUpgrade takes integer unitCode, player p, integer index returns integer
            local integer pid = GetPlayerId(p) * MAX_UPGRADES
            return LoadInteger(hash, unitCode, pid + index)
        endfunction
    endlibrary

    And here is an example of how it is used to simplify how we "save" an upgrade:
    • -------- --------
    • -------- Deduct the gold and mark the output ability as researched --------
    • -------- --------
    • Player - Set (Triggering player).Current gold to (((Triggering player) Current gold) - UC_GoldCost)
    • Custom script: call SaveUpgrade(udg_UC_UnitType, GetTriggerPlayer(), udg_UC_OutputAbility)
  4. The configuration is a bit more simplified. Instead of having generic Q/W/E... etc. abilities and tweaking it all in code, you can just create the abilities you need in the object editor and put them in the spell-book for that unit. This works even if you want to re-use abilities (for example, in the sample map both the footmen and the knight share two abilities that they can research independently). For example, here is the configuration in the sample map:
    • Actions
      • -------- --------
      • -------- FOOTMAN --------
      • -------- --------
      • Set VariableSet UpgradeInputAbility[1] = Upgrade Roar (Footman)
      • Set VariableSet UpgradeOutputAbility[1] = Roar
      • Set VariableSet UpgradeInputAbilityGoldCost[1] = 5
      • Set VariableSet UpgradeInputAbility[2] = Upgrade Bash (Footman)
      • Set VariableSet UpgradeOutputAbility[2] = Bash
      • Set VariableSet UpgradeInputAbilityGoldCost[2] = 5
      • Set VariableSet UpgradeInputAbility[3] = Upgrade Flurry (Footman)
      • Set VariableSet UpgradeOutputAbility[3] = Flurry
      • Set VariableSet UpgradeInputAbilityGoldCost[3] = 5
      • Set VariableSet UpgradeInputAbility[4] = Upgrade Redemption (Footman)
      • Set VariableSet UpgradeOutputAbility[4] = Redemption
      • Set VariableSet UpgradeInputAbilityGoldCost[4] = 5
      • -------- --------
      • -------- KNIGHT --------
      • -------- --------
      • Set VariableSet UpgradeInputAbility[5] = Upgrade Taunt (Knight)
      • Set VariableSet UpgradeOutputAbility[5] = Taunt
      • Set VariableSet UpgradeInputAbilityGoldCost[5] = 5
      • Set VariableSet UpgradeInputAbility[6] = Upgrade Ensnare (Knight)
      • Set VariableSet UpgradeOutputAbility[6] = Ensnare
      • Set VariableSet UpgradeInputAbilityGoldCost[6] = 5
You can check it out below and play around with it (SampleHeroDefense.w3m). But overall, I think the simplicity of the system in there will help you spend more time on making fun spells and unit ideas than having to mess with a bunch of hashtable programming bugs. :thumbs_up:
 

Attachments

  • SampleHeroDefense.w3m
    66.9 KB · Views: 1
  • Custom Hero Team Defense (testing new).w3m
    176.2 KB · Views: 1
Last edited:
Level 9
Joined
Jun 13, 2010
Messages
365
the simplicity of the system in there will help you spend more time on making fun spells and unit ideas than having to mess with a bunch of hashtable programming bugs.
I totally get that. I like to learn new stuff but when it is a struggle to finish it kills the mood. I am actually making this map for a get together for my friends where we typically play some Warcraft. Then I almost always bring a map and we complain about how buggy it is. xD

So I really would like to remake this to what you suggest, but as I only have 2 weeks until the get together, I am afraid I won't make it if I have to recreate the Chatpgt system. :D Or maybe I am just overthinking it. I have already made a few more spells and stuff since your last reply, but I guess I could just copy paste them to your map version and take a look at what you did.

I think I get what you are explaining as far as Jass and hashtables. I get the idea in total, but recreating it and fully understanding it turns my brain lol.

Will it be a long job to recreate both spellbook, hashtable storage etc? :)

And again thank you for helping me this much, I couldn't be more grateful!
Btw is there some way for me to +rep or something for your help?
 
Last edited:
I totally get that. I like to learn new stuff but when it is a struggle to finish it kills the mood. I am actually making this map for a get together for my friends where we typically play some Warcraft. Then I almost always bring a map and we complain about how buggy it is. xD

So I really would like to remake this to what you suggest, but as I only have 2 weeks until the get together, I am afraid I won't make it if I have to recreate the Chatpgt system. :D Or maybe I am just overthinking it. I have already made a few more spells and stuff since your last reply, but I guess I could just copy paste them to your map version and take a look at what you did.

I think I get what you are explaining as far as Jass and hashtables. I get the idea in total, but recreating it and fully understanding it turns my brain lol.

Will it be a long job to recreate both spellbook, hashtable storage etc? :)

And again thank you for helping me this much, I couldn't be more grateful!
Btw is there some way for me to +rep or something for your help?
That sounds awesome. :pir: And no sweat--it is definitely hard to pivot when you've already written so much.

If I were you, I'd first take a look at the lil sample demo I had (SampleHeroDefense.w3m) just to see if it even feels good for your purposes when you play it. If the spellbook concept doesn't feel quite right (compared to having a dedicated "Upgrade Unit" and renaming things for upgrades), then that might be a good reason to stick with your current system.

But if it feels promising, then I don't think it'd take too long to re-create the spellbooks and copy over the system (maybe a few hours?). The spellbooks are pretty simply configured in the object editor, so you can just make one for each unit and add the abilities you like:
1745523449193.png

Feel free to tweak the minimum/maximum to 9 or whatever (try to keep the min/max the same so that it orders the spells same way it is ordered in the "Spell List"). So if you wanted to try it, I'd do the following:
  1. First make a copy of your map (that way you can go back to where you were if things go south)
  2. In the sample map, copy the whole "Upgrade System" folder and paste it into the copy of your map, it should save without issues.
  3. For each spell you want a unit to be able to research, you'll want to make an "Upgrade" ability (e.g. "Upgrade Oathbound", "Upgrade To Arms!", etc. you can name it however you'd like). You can probably just copy one of the ones from the sample map and then paste it into your map. And then I just copy and paste that same ability and tweak the name/icon/button position/base order ID (since they are based off "Channel"). Maybe start off with just a few to make sure you can integrate the system properly (e.g. try 4 abilities first, so that you can test the research limit and such).
  4. Once you have your upgrade abilities, you can add the spellbook ability to contain them. Either copy the "Upgrade (Footman)" from the sample map, or you can make your own based off the item ability "Spell Book", and add the abilities you want. Then you can add that spellbook ability to your desired unit (e.g. Footman) in the object editor.
  5. Next, I would disable your current system just so it doesn't conflict with the new one (I would just disable all the triggers in "Ability Upgrades"--except the ones for stats).
  6. Finally, you can just tweak the "Upgrade Configuration" trigger with your abilities, e.g.:
    • -------- --------
    • -------- FOOTMAN --------
    • -------- --------
    • Set VariableSet UpgradeInputAbility[1] = Upgrade Defend (Footman)
    • Set VariableSet UpgradeOutputAbility[1] = Defend
    • Set VariableSet UpgradeInputAbilityGoldCost[1] = 5
    • Set VariableSet UpgradeInputAbility[2] = Upgrade Oathbound (Footman)
    • Set VariableSet UpgradeOutputAbility[2] = Oathbound
    • Set VariableSet UpgradeInputAbilityGoldCost[2] = 5
Then try it out and see if it works okay! I can also help if you need to tweak anything to fit your map. I think it is mostly tedious to set-up initially since you already have a lot of spells, but after that I think it'll be easier to maintain since you don't really need to touch the system after that. You just need to use the object editor and then add a few lines in the configuration from then on. :thumbs_up:

EDIT: Oh and the +rep system was replaced by experience, which is granted from resources/likes etc. But no need to worry about it, I thought your map had a cool concept so I just was happy to play around with it and see how I could help!
 
Level 9
Joined
Jun 13, 2010
Messages
365
That sounds awesome. :pir: And no sweat--it is definitely hard to pivot when you've already written so much.

If I were you, I'd first take a look at the lil sample demo I had (SampleHeroDefense.w3m) just to see if it even feels good for your purposes when you play it. If the spellbook concept doesn't feel quite right (compared to having a dedicated "Upgrade Unit" and renaming things for upgrades), then that might be a good reason to stick with your current system.

But if it feels promising, then I don't think it'd take too long to re-create the spellbooks and copy over the system (maybe a few hours?). The spellbooks are pretty simply configured in the object editor, so you can just make one for each unit and add the abilities you like:
View attachment 530714
Feel free to tweak the minimum/maximum to 9 or whatever (try to keep the min/max the same so that it orders the spells same way it is ordered in the "Spell List"). So if you wanted to try it, I'd do the following:
  1. First make a copy of your map (that way you can go back to where you were if things go south)
  2. In the sample map, copy the whole "Upgrade System" folder and paste it into the copy of your map, it should save without issues.
  3. For each spell you want a unit to be able to research, you'll want to make an "Upgrade" ability (e.g. "Upgrade Oathbound", "Upgrade To Arms!", etc. you can name it however you'd like). You can probably just copy one of the ones from the sample map and then paste it into your map. And then I just copy and paste that same ability and tweak the name/icon/button position/base order ID (since they are based off "Channel"). Maybe start off with just a few to make sure you can integrate the system properly (e.g. try 4 abilities first, so that you can test the research limit and such).
  4. Once you have your upgrade abilities, you can add the spellbook ability to contain them. Either copy the "Upgrade (Footman)" from the sample map, or you can make your own based off the item ability "Spell Book", and add the abilities you want. Then you can add that spellbook ability to your desired unit (e.g. Footman) in the object editor.
  5. Next, I would disable your current system just so it doesn't conflict with the new one (I would just disable all the triggers in "Ability Upgrades"--except the ones for stats).
  6. Finally, you can just tweak the "Upgrade Configuration" trigger with your abilities, e.g.:
    • -------- --------
    • -------- FOOTMAN --------
    • -------- --------
    • Set VariableSet UpgradeInputAbility[1] = Upgrade Defend (Footman)
    • Set VariableSet UpgradeOutputAbility[1] = Defend
    • Set VariableSet UpgradeInputAbilityGoldCost[1] = 5
    • Set VariableSet UpgradeInputAbility[2] = Upgrade Oathbound (Footman)
    • Set VariableSet UpgradeOutputAbility[2] = Oathbound
    • Set VariableSet UpgradeInputAbilityGoldCost[2] = 5
Then try it out and see if it works okay! I can also help if you need to tweak anything to fit your map. I think it is mostly tedious to set-up initially since you already have a lot of spells, but after that I think it'll be easier to maintain since you don't really need to touch the system after that. You just need to use the object editor and then add a few lines in the configuration from then on. :thumbs_up:

EDIT: Oh and the +rep system was replaced by experience, which is granted from resources/likes etc. But no need to worry about it, I thought your map had a cool concept so I just was happy to play around with it and see how I could help!
I will definitely do that and thanks so much for the overview. What I have made so far is a system I am having a bit difficulty with grasping my self already. xD

I'm glad it woke some interest. The idea behind it is somewhat a mix between Battlegrounds from Hearthstone, TFT from League of Legends and the Legion TD defense map. I hope to make different builds possible, like major arrow rain builds (using a missile system), summon builds, spell casters, thorns, giant brutes etc.

Also whilst making the TFT variant, I am making sure it will not interact with Heroes, as I plan to make a Hero mode - so one player may play the TFT variant while another player is a controllable Hero, to further experiment with combos. I consider making a tower defense (with destructible towers) too, but that will be way in the future. xD

Hope it makes sense.

Firstly I am gonna make this thing playable and finish the abilities for triggers and make sure it works. Then I may be able to change the Upgrade Unit and spellbook systems etc. If I use all my time altering the system, it will not be playable without abilities ofc. :)
I think it is mostly tedious to set-up initially since you already have a lot of spells
I am afraid of this though, if I want to change it later - but I guess I will just have to deal with that at the time. xD

EDIT: And you are most welcome with all the help you wish to although you have already done more than I would expect from anyone. :)
 
Last edited:
Top