These details are CRITICAL; DO NOT skip them or your issue may not be reviewed.
GAME BUILD #: latest as of 2024/april/26
GAME PLATFORM: Steam
OPERATING SYSTEM: Windows 10
ISSUE EXPERIENCED
When using the kbUnitIsType function, it returns false, even when I know that the unit unitID is of unit type unitType. I know it does, because I chatted it out.
Notice that this function DOES work in TAD! It worked in WoL too, at least a few years ago.
FREQUENCY OF ISSUE
How often does the issue occur? CHOSE ONE; DELETE THE REST!
100% of the time / matches I play (ALWAYS)
REPRODUCTION STEPS
List CLEAR and DETAILED STEPS we can take to reproduce the issue ourselves… Be descriptive!
Here’s the steps to reproduce the issue:
Create a trigger that uses kbUnitIsType
EXPECTED RESULT
What was SUPPOSED to happen if the bug you encountered were not present?
kbUnitIsType(i,k) should return true when unit with index i is of type k
IMAGE
I’ll attach the code instead, so you can test it yourself
<Condition name="Army UnitType Count Compare">
<Param name="SrcArmy" dispName="$$22348$$Army" VarType="group">default</Param>
<Param name="TargetProtoName" dispName="Target Protounit" varType="unittype">All</Param>
<Param name="Op" dispName="$$22297$$Operator" VarType="operator">==</Param>
<Param name="Count" dispName="$$22321$$Number" VarType="long">1</Param>
<Command>trUnitSelectClear();</Command>
<Command>for (all=0; <=10000){ if (kbGetUnitTypeName(all)=="%TargetProtoName%") break;}</Command>
<Command>for(counter=-1;<0){}</Command>
<Command>for(un=-2;<-1){}</Command>
<Command>trArmySelect("%SrcArmy%");</Command>
<Command>for(a=trGetNumberSelectedUnitIDs();>=999999){break;}</Command>
<Command>if (a != 0){</Command><!--if we have selected units--> <!-- != means !=-->
<Command>for (i=0;<=a){</Command><!--for every unit we have selected-->
<Command>un=trGetSelectedUnitID(i);trChatSend(1,"works="+kbUnitIsType(un,all));</Command><!--ID of selected unit-->
<Command>if(kbUnitIsType(un,all)){</Command><!--if our unit is of unitType-->
<Command>counter=counter+1;</Command><!--the counter rises by one-->
<Command>}</Command><!--end if(kbUnitIsType(un,all)-->
<Command>}</Command><!--end for(i=0;<=a)-->
<Command>}</Command><!--end if (a!=0)-->
<Expression>counter %Op% %Count%</Expression>
<Command>trUnitSelectClear();</Command>
I tested using kbGetProtoUnitName instead of kbGetUnitTypeName and varType=“protounit”
instead of varType=“unittype” and sort of works, but I never get the exact amount of units in OG AoE3. If I set <=10, for example, the condition stops at 12, but if I set <=15, I get 16, a few times, and then, it stops working, so I don’t know.
I think he has all as an integer from 0 - 10000? I’ve never interacted with the integer values of unitType, I just always input them as something #### ############## or cUnitTypeAbstractWarShip. Then again I interact with these functions with the AI so maybe they behave differently for me?
Reading this is making me happy I work with AI, this formatting is painful to read.
for (all=0; <=10000){ if (kbGetUnitTypeName(all)=="%TargetProtoName%") break;}
I’m trying to get the count of a unitType in a selection
The things that this didn’t give any issue in OG. It worked perfectly. But now in DE it gives me issues (I know I already told you via PM, but I repeat it so people don’t think I’m crazy).
In that case it may be worth investigating what the integer values actually are for the unit types. I know some integer values have their own carve outs, like the player relations, and the devs will use that do determine whether a function has passed a player number or a player relation. The values of unit types may have shifted as the devs added more units and unit types.
all exist in the context of the rule, which means that if I define all as int all=-1 or something, if I add the same Effect in the same trigger, the game will complain that a is already declared. This doesn’t happen if I declare it in a for loop
What’s this? I’m looking for the amount of units of unitType, like AbstractVillager or something inside an internal selection, like AI or triggers, not UI selection.
of course it does because you define that in a ‘global’ context, you need to use seperate variable names as in ‘all_1’ ‘all_2’ etc.
Did you try using the method it told you? and see if that works. I haven’t dabbled into xs scripting but all languages i know would make that ‘all’ variable a local one, that lives inside the for loop only.
What’s this? I’m looking for the amount of units of unitType, like AbstractVillager or something inside an internal selection, like AI or triggers, not UI selection.
so you want to know how many unit of the same type are there in the selected unit group, but how would the ‘main’ selection work. You can’t select 2 times.
Yes. And the variable defined in the loop is accesible in the whole rule, so two effects of the same type throw an error. It’s been years since it has been done that way, btw, defining a variable inside a for loop. Check the ‘advanced trigger pack’, for example https://aoe3.heavengames.com/downloads/showfile.php?fileid=1218
Note than in Python the variables declared in a for loop are accesible outside of the loop
New test:
I made an army of just hussars and chatted out the unitType of all un=trGetSelectedUnitID(i);trChatSend(2, "UnitTypeUn="+kbGetUnitTypeName(all));
Note that the only units the scenario had were a barracks and 5 hussars. The chat I get is UnitTypeUn=Hussar,
RoomOfTheEvil, why don’t you believe me? Try it yourself
EDIT: THIS IS ######################## RIDICULOUS!!!
IF I ADD ANOTHER TRIGGER BUT WITH ANOTHER ARMY, IT WILL WORK FOR ONE OF THE ARMIES BUT NOT THE OTHER ONE!!!
@CanineCrown7153 It should be the other way around (<= instead of >=) EDIT: nevermind, I’m an idiot. < translates to <. I’m so sorry
I will have to actually open the game to see what’s wrong.
@CanineCrown7153 could you please take a look at the trigtemp.xs file? Or actually send it in this thread so we all can take a look.
Also, if you are using AOE3DE, the far superior alternative is to use cUnitType because triggers are super laggy in multiplayer mode and you can’t afford to abuse this loop.
@RoomOfTheEvil
In XS, variables that are defined in a function or a rule are scoped to the entire function or rule, even if they are defined inside a block (e.g. an if statement or, in this case, a for loop):
rule ExampleRule_1 {
for(foo=-1;>0){}
trChatSend(0, "foo=" + foo); // Outputs "foo=-1"
}
rule ExampleRule_2 {
if (false) {
int bar = -1;
}
int bar = 0; // This will cause a compile error because bar is already defined in this scope
}
And that is a fantastic thing because it allows you to insert the same condition or effect multiple times in the same trigger without having to worry about compile errors.
I mean, you probably already know about it but for the others who don’t, if you have an effect like this:
I’m pretty sure they are the same. Actually, when you create a plugin with UHC, you are given the addresses of all the XS functions available in the game so you won’t have to reimplement them yourself (you would not be able to reproduce them 1:1 anyway, lol)
With this, you can make an AI-only function available in triggers, and vice-versa. That’s how I transitioned from this:
Behind the scenes, it simply uses trUnitSelect and trUnitMoveToPoint, lol
But this way, I can move units like an army instead of moving them individually (without a formation).
Hey. Two things. It seems cUnitType won’t work in AoE3 base
This is the Condition
<Condition name="Army UnitType Count Compare">
<Param name="SrcArmy" dispName="$$22348$$Army" VarType="group">default</Param>
<Param name="TargetProtoName" dispName="Target Protounit" varType="unittype">All</Param>
<Param name="Op" dispName="$$22297$$Operator" VarType="operator">==</Param>
<Param name="Count" dispName="$$22321$$Number" VarType="long">1</Param>
<Command>trUnitSelectClear();</Command>
<!-- <Command>for (all=0; <=100000){ if (kbGetUnitTypeName(all)=="%TargetProtoName%") break;}</Command> -->
<Command>for(counter=-1;<0){}</Command>
<Command>for(un=-2;<-1){}</Command>
<Command>trArmySelect("%SrcArmy%");</Command>
<Command>for(a=trGetNumberSelectedUnitIDs();>=999999){break;}</Command>
<Command>if (a != 0){</Command><!--if we have selected units--> <!-- != means !=-->
<Command>for (i=0;<=a){</Command><!--for every unit we have selected-->
<Command>un=trGetSelectedUnitID(i);</Command><!--ID of selected unit-->
<Command>if(kbUnitIsType(un,cUnitType%TargetProtoName%)){</Command><!--if our unit is of unitType-->
<Command>counter=counter+1;</Command><!--the counter rises by one-->
<Command>}</Command><!--end if(kbUnitIsType(un,all)-->
<Command>}</Command><!--end for(i=0;<=a)-->
<Command>}</Command><!--end if (a!=0)-->
<Expression>counter %Op% %Count%</Expression>
<Command>trUnitSelectClear();</Command>
<!--this functions must be protected with life and limb kbUnitQuerySetArmyID(long queryID, int armyID) kbArmyGetID(string name)-->
</Condition>
Yeah legacy doesn’t support any of the auto-generated constants unfortunately.
Forgot to mention, all knowledge base functions require a context, even the ones that should not require context according to common sense (like kbGetMapCenter() or kbGetMapXSize()). I bumped into the same issue when I was working on AoP’s triggers (fantastic mod, by the way!) and WoL’s “Deathly Hollows” map (fantastic mod too)