1. How about inside each item_pruchase file, check if the scepter is inside the item list and if not, update a table member on Alchemist.

Inside the item_purchase files just do GetTeamMembers() then iterate through all available members using GetTeamMember(). If that hero is Alchemist, add it.

Code:
-- item_purchase_lina

local function NotifyAlchemist ()
local playerList = GetTeamPlayers(GetTeam());

for teamPlayerID, gamePlayerID in pairs(playerList) do
if (IsTeamPlayer(gamePlayerID) == true) then
heroName = GetSelectedHeroName(gamePlayerID);

if (heroName == "npc_dota_hero_alchemist")  then
if (GetTeamMember(teamPlayerID).ScepterCount == nil) then
GetTeamMember(teamPlayerID).ScepterCount = 1;
else
GetTeamMember(teamPlayerID).ScepterCount = (ScepterCount + 1);
end
end
end
end
end
Obviously you can use a table that you can then, call table.insert on
The advantage here is that then you can call the Think() on your Alchemist or in his item list using GetBot().ScepterCount.

The trick here is to find a good way to call the notify function above to avoid infinite calls. Minor note: the above example is just that: an example. Since it's a local function it will only be called from inside the file so make sure you either make it global OR add it as a table member to access it from other files using require().

If you need help, let us know.

2. thanks nomad, i kind of need help just understanding that.
im with you, but i dont use separate purchases for each bot, this would also work the same in a purchase generic file i gather, because each bot checks itself and adds its name to "alchemist.ScepterNeededTable" for eg.?

3. i tried your example and kept getting main chunk errors, so i tried again using what i generally know how to do with what you said mixed in.

Code:
if DotaTime() < 0 then
local TotalScepterCount = 0;
local numPlayer =  GetTeamPlayers(GetTeam());
if not string.find(npcBot:GetUnitName(), "npc_dota_hero_alchemist") then
if not HasSomeBuild("item_ultimate_scepter") then
print(npcBot:GetUnitName().. " - Needs Scepter -  ")
for i = 1, #numPlayer do
if string.find(GetTeamMember(i):GetUnitName(), "npc_dota_hero_alchemist") then
GetTeamMember(i).TotalScepterCount = GetTeamMember(i).TotalScepterCount + 1
table.insert(GetTeamMember(i).NeedsScepter, npcBot:GetUnitName())
utils.print_r(GetTeamMember(i).NeedsScepter.. "  Needs scepter")
end
end
end
end
end
[ W VScript ]: Script Runtime Error: ...ame\dota\scripts\vscripts\bots\item_purchase_ge neric.lua:1086: bad argument #1 to 'find' (string expected, got boolean)
[ W VScript ]: stack traceback:
[ W VScript ]: [C]: in function 'find'
[ W VScript ]: ...ame\dota\scripts\vscripts\bots\item_purchase_ge neric.lua:1086: in main chunk

is the error im getting when calling method :GetUnitName() on GetTeamMember(i), yet any other method - GetNeabryHeroes, etc. work just fine.

4. @Yavimaya

Code:
if npcBot:GetUnitName()=="npc_dota_hero_alchemist" then
npcBot.HeroesInNeed={};
for i=1,5,1 do
local ally=GetTeamMember(i);
if ally~=nil and ally.NeedsScepter~=nil and ally.NeedsScepter then
table.insert(npcBot.HeroesInNeed,ally); -- or insert ally:GetUnitName()
end
end
else
npcBot.NeedsScepter = not HasSomeBuild("item_ultimate_scepter");
end

5. thats script looks like exactly what i need Platinum, thank you heaps, you too nomad.

so just to be clear, that goes into its own function that i can just call once at the start of game and then from alchs script i can just call on the table for number or people and name of them (once i insert ally:getunitname())?

edit again: thank you, that is perfect, once i put the prints in can see more clearly how it all works.
sorry im just terrible at making stuff from scratch.

6. Originally Posted by Yavimaya
i tried your example and kept getting main chunk errors, so i tried again using what i generally know how to do with what you said mixed in.

Code:
if DotaTime() < 0 then
local TotalScepterCount = 0;
local numPlayer =  GetTeamPlayers(GetTeam());
if not string.find(npcBot:GetUnitName(), "npc_dota_hero_alchemist") then
if not HasSomeBuild("item_ultimate_scepter") then
print(npcBot:GetUnitName().. " - Needs Scepter -  ")
for i = 1, #numPlayer do
if string.find(GetTeamMember(i):GetUnitName(), "npc_dota_hero_alchemist") then
GetTeamMember(i).TotalScepterCount = GetTeamMember(i).TotalScepterCount + 1
table.insert(GetTeamMember(i).NeedsScepter, npcBot:GetUnitName())
utils.print_r(GetTeamMember(i).NeedsScepter.. "  Needs scepter")
end
end
end
end
end
[ W VScript ]: Script Runtime Error: ...ame\dota\scripts\vscripts\bots\item_purchase_ge neric.lua:1086: bad argument #1 to 'find' (string expected, got boolean)
[ W VScript ]: stack traceback:
[ W VScript ]: [C]: in function 'find'
[ W VScript ]: ...ame\dota\scripts\vscripts\bots\item_purchase_ge neric.lua:1086: in main chunk

is the error im getting when calling method :GetUnitName() on GetTeamMember(i), yet any other method - GetNeabryHeroes, etc. work just fine.
Just for further info on your part:
- you don't need string.find on GetUnitName() vs an actual hero internal name. Using ~= or == works fine and is actually better in terms of performance; use string.find ONLY for patterns or portions of text
- string.find is mostly used for patterns rather than portions of text; you will find that string.find("shell32.dll, ".") return 1 when in fact it should return 8; you would have to add 2 more params: string.find("shell32.dll", ".", 1, true) - 1 meaning "start at position 1" and true meaning "it's not a regex, but a plain text match". But as said, don't use it if you don't have to
- depending on when you call that code piece, it will always run while DotaTime() < 0; what I mean is, YES, it won't always add scepters since you added the "if not HasSomeBuild", however, it will run the prior part, meaning GetTeamPlayers() and string.find() for the full duration of almost 2 minutes. While nothing exciting happens in that time and they are not the most expensive functions in the world, keep that in mind for future code design
- you probably got the "bad argument #1 to find" because you used npcBot but didn't assign it with npcBot = GetBot() anywhere - or at least I don't see it in that snippet; GetTeamMember() returns a handle similar to that of GetBot() which is why you say one works and the other doesn't

Good luck !

GetBot should have been assigned at the start of the file (it was run within ItemPurchaseThink()), but you are correct, it was a stupid place to put it, Platinums is now up top of the itempurchase file and only runs once. much better.
im really not sure about the bad argument, as far as i could see i was passing it all the variables it needed, but as ive said before, im a bit slow lol.
back in school, in maths, a teach would walk me through an example, then change the numbers for me to do it and i was back to square one. :sad:

Edit:
seems up top of ItemPurchase or ItemAbilityGeneric arent the right places, table is printing as empty unless i reload.
Where might one put this or what condition might i put in so it only runs once, but after the tableItemsToBuy is filled? - Ignore that, ill just use a variable to stop it running more than once.

8. update:
after running many test runs, im not 100% sure it is working properly, underlord put himself into the table in one game when his build wanted an ulti scepter.
in the same game, antimage printed that he wont buy a scepter, but didnt add himself to table, also wisp who never buys scepter didnt put himself into the table (wisp is just for testing)
many other tests it has been hard to determine if it worked because starting items are all the same for some people, but it generally looked to work every other time.

sceptTable.jpg
this is the result after making it loop twice, first loop historically has printed an empty table.

update2:
ive stripped out the "HasSomeBuild" part and decided to manually put the npcBot.NeedsScepter into each hero build file.
its not as elegant or versitile as i would have liked, but it removes the problems that were occurring.

thanks again.

now, how do i call the count of the table? #npcBot.HeroesInNeed > 0 doesnt seem to work. thats the only way i know of to get the table length.
also would "table.remove(npcBot.HeroesInNeed, feedsceptor_target:GetPlayerID())" be the way to remove the right hero from the table, since it wants a number? (feedscepter_target:GetUnitName() doesnt work)

9. Originally Posted by Yavimaya
update:
now, how do i call the count of the table? #npcBot.HeroesInNeed > 0 doesnt seem to work. thats the only way i know of to get the table length.
also would "table.remove(npcBot.HeroesInNeed, feedsceptor_target:GetPlayerID())" be the way to remove the right hero from the table, since it wants a number? (feedscepter_target:GetUnitName() doesnt work)
npcBot.HeroesInNeed only is available for alchemist. Before #npcBot.HeroesInNeed > 0, make sure it is not nil first. Also in the part where you check whether a hero has aghs, also look for aghs buff (so that you don't give aghs to someone twice).

10. There are three (unofficial !! that means I categorize them as being three based on their behavior so they are not actual names ) types of tables in Lua:
[LIST][*] a cleanly indexed table: this is the kind of table where you don't put anything in the {} table manually or add it via members, but use table.insert(); using it this way makes the table always start at index 1
Therefore, an example would be:
Code:
local example_table = {} -- this is a not-null empty table; that means running if (example_table ~= nil) will return true, but #example_table returns 0 because there is nothing in it

-- to populate it, we use insert from the table library:
table.insert(example_table, "foo");
table.insert(example_table, "bar");

-- this means we know have a table that has index [1] as "foo" and index [2] as "bar"; this also means each new insert adds a new sequential index (that means it will add each element at 1, 2, 3, 4 ... n where n would be the last time you called table.insert()  )
-- in order to get a proper indexed table you must only use table.insert(); if you do example_table[99] = "test" (which is legal) will not guarantee the sequence
There are 2 ways to use indexed tables:
Code:
for key, value in pairs(example_table) do
print(key .. " " .. value); -- we know that we only assigned strings as values
end
It's fine and it might output 1 foo and 2 bar; but there are cases where it might output 2 bar and 1 foo (that means the order is not necessarily done or guaranteed via the index). table.sort doesn't guarantee that iterating will be done in its proper order either
To get over this issue we can use the second way:
Code:
for key, value in ipairs(example_table) do
print(key .. " " .. value); -- we know that we only assigned strings as values
end
This guarantees it uses the indexes sequence. I had this issue with item purchases and took me some time to find the issue.
Indexed tables can also use the # operator to get the number of elements and will always work. Please note that # doesn't really get the total element count, but the last sequence, if I remember correctly; therefore table.insert() is the recommended method to ensure a correct # result (or when using it on tables returned from C++ functions such as GetTeamMembers() or GetNearbyCreeps() and so on)

To access an element you use their index: example_table[1] (which would return "foo" in our case)
Removing it is done via its index: table.remove(example_table, 1) will remove "foo" and now example_table[1] will be "bar".
Updating it is done explicitly: example_table[1] = "foo2"; So since we removed "foo" above, [1] will now be "foo2" instead of "bar".
[*] non-indexed tables; most of the time used with strings (this assumes you use strings as keys for all values)
The first issue is that # won't work on those tables (or, if it does work, it might have unexpected or inconsistent results in some cases).

Code:
local example_table = {}

example_table["element1"] = "foo";
example_table["element2"] = "bar";
Updating it is done the same way:

Code:
example_table["element2"] = "new bar";
So element_table["element2"] will not contain "new bar" instead of "foo".

To remove it:
Code:
example_table["element1"] = nil;
Please note that if we removed it from the example, the table will now have a value in example_table["element2"]; element2 DID NOT replace element1 !!. Therefore example_table["element1"] prints nothing, example_table["element2"] will print "new bar" (since we renamed it earlier)

The way to get the number of elements is by looping them:
Code:
function GetTableLength (tTable)
local length = 0;

if (tTable ~= nil) then
for _ in pairs(tTable) do
length = length + 1;
end
end

return length;
end

print("the element count is " .. GetTableLength(example_table));
[*] inconsistent tables (no idea how to call these) are those tables that have either a string or a number as keys at the same time (so not one or the other). I really, really, really, really don't recommend doing this. It's difficult to maintain and traverse. The only way to get the leement count is with an iterating function like above.

Long story short, the most reliable (and recommended) way to get the count is to use an iterating function like in the example above. Honestly, I don't know why Lua doesn't add a proper method, but whatever.

So, depending on what kind of table it is you can use # or an iterating function. From the picture I see that it uses indexed keys so # should be ok, just keep in mind that the element on the next position replaces the element on the deleted position. That means, to delete lion you can use table.remove(my_table, 3). But afterwards, if you want to delete shadow shaman you must use table.remove(my_table, 3), not table.remove(my_table, 4).

As for, why # doesn't work, it depends on whom you call it upon. It doesn't add that member to ALL bots. It's only visible to the one that was added. Ah, seems Platinum beat me to this
I think it will help if you show us the code.

EDIT:
Originally Posted by Yavimaya
also would "table.remove(npcBot.HeroesInNeed, feedsceptor_target:GetPlayerID())" be the way to remove the right hero from the table, since it wants a number? (feedscepter_target:GetUnitName() doesnt work)
The table key index IS NOT the player id. So don't use GetPlayerID() in table.remove. GetPlayerID() is also zero-indexed, while tables (normally) aren't.

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•