blizzhackers / kolbot

d2bs: core + d2bot (noah) + kolbot libs (kolton) | January 2020's clone of https://github.com/kolton/d2bot-with-kolbot/ + further development
246 stars 159 forks source link

Problem with Config.ClearPath and Mfleader #50

Closed DarkHorseDre closed 3 years ago

DarkHorseDre commented 4 years ago

Brief: Config.ClearPath 0xF (and 0x7 I think) also include bosses (Andariel et al), which means that when the bot travels through the catacombs to andy (for example) it will kill all non-normal mobs.. when it gets to Andy (or other boss) it will kill her as part of clearpath and not as part of the boss script.

this results in the bot opening tp for party but not announcing bot to kill. in manager window attack.js prints the message: " Error in Andariel (attack.js #232) Attack.kill: Target not found (Area: 37, Ping:16, Game: )"

I think the problem is spectype. when i looked at this a year or so ago the spectype for a unique and superunique were not exclusive so the bot could not distinguish between them with 0xF or 0x7 (or even 0x6 or 0x4)

Anyone have any ideas of how to reliably distinguish between champ, uniques and super uniques/bosses? (short of excluding the id's of every boss when using clear path!!)

jmichelsen commented 4 years ago

To account for this in some of the other scripts, I've just added a try/catch around the "kill boss" part, so that if the boss is missing, the bot doesn't freak out and exit. I can submit a PR with this for bosses, and condition it based on clearpath

DarkHorseDre commented 4 years ago

Oh no its not that - the bot doesn't spaz out - it just continues to the next script.

the problem is mobs do not seem to have the correct spectype. specifically - bosses seem to have the same as uniques.

this is a problem as the bot will kill the boss and then fail to engage the scripts.mfhelpers whom join and help kill the boss. This results in 1) the boss being killed with 1 player nearby instead of the full set, 2) leader killing with no support, 3) no xp for helpers, etc

to reproduce (as per OP): set clearpath to 0xF or 0x7, set toon as mfleader, set a boss script

outcome: the bot usually (not always) kills the boss before the code is ran that opens tp and gives kill command (andy is a good example as she always has uniques nearby, which exacerbates the aforementioned issues)

jmichelsen commented 4 years ago

Oh I see. I've been looking for an excuse to better utilize the new teams module. Mf helper would be a good choice. We could make the leader tp dynamically based on monster count+ perceived safety.

Also.. The spectype should be fixed if that's incorrect :)

DarkHorseDre commented 4 years ago

nice :)

So I was thinking of my last discussion of this over a year ago and went looking for it to reference to you, but then found this more recent and more enlightening thread which seems to point to an answer: https://github.com/kolton/d2bot-with-kolbot/issues/2040

Can you validate?

I agree the documentation is baaaaad but the link to Noahs work is one I'd never seen and gives answers, but still leaves questions.

If I'm correct, to kill champs and "normal" uniques (not super uniques/bosses) the mask would be 0x3 (or 0xB to include minions)? (I swear I tried 0x3 before!)

If so, then no changes are needed except to the recommended options in the char config, and update to documentation to provide the list in the link above (and as pasted below)

spectype monster types to kill
0x0 all
0x1 "Normal" Boss
0x2 Champion
0x3 "Normal" Boss + Champion
0x4 Boss
0x5 "Normal" Boss + Boss
0x6 Champion + Boss
0x7 "Normal" Boss + Champion + Boss
0x8 Minion
0x9 "Normal" Boss + Minion
0xA Champion + Minion
0xB "Normal" Boss + Champion + Minion
0xC Boss + Minion
0xD "Normal" Boss + Boss + Minion
0xE Champion + Boss + Minion
0xF "Normal" Boss + Champion + Boss + Minion
5noop commented 4 years ago

the problem is mobs do not seem to have the correct spectype

Pretty sure spectype is defined in d2 itself. I don't remember having seen a spectype 1, bosses from elite packs are 4 and other unique mobs are 4 (like Andariel) or 5. Which means you'll probably have to find another way to detect Andariel from another boss.

(short of excluding the id's of every boss when using clear path!!)

This or editing andy script is probably your best bet. You could also use ClearPath in cata lvl 2 and 3 but not 4.

Btw the invalid label is a missclick ? Cause it still looks like a valid issue to me.

DarkHorseDre commented 4 years ago

the problem is mobs do not seem to have the correct spectype

Pretty sure spectype is defined in d2 itself. I don't remember having seen a spectype 1, bosses from elite packs are 4 and other unique mobs are 4 (like Andariel) or 5. Which means you'll probably have to find another way to detect Andariel from another boss.

(short of excluding the id's of every boss when using clear path!!)

This or editing andy script is probably your best bet. You could also use ClearPath in cata lvl 2 and 3 but not 4.

Btw the invalid label is a missclick ? Cause it still looks like a valid issue to me.

Thanks bro <3

I think you and I discussed this ages ago now I read this!

I think you are right, as back then I read the spectypes with: 'print(getUnit(101).spectype);' and compared corpsefire with a random unique and I think they were the same.

for andy you are right, but the other bosses that have uniques nearby have the same problem, eg corpsefire). This is exacerbated by the fact that its now a config/pathing level function, so harder to control (well would have to disable per boss script/script section I'm guessing?)

I recall trying to find an alternative but not completing..

*yes invalid was missclick, thanks :)

Fa-b commented 4 years ago

looked at d2bs:

        case UNIT_SPECTYPE:
        DWORD SpecType;
        SpecType = NULL;
        if (pUnit->dwType == UNIT_MONSTER && pUnit->pMonsterData) {
            if (pUnit->pMonsterData->fMinion & 1)
                SpecType |= 0x08;
            if (pUnit->pMonsterData->fBoss & 1)
                SpecType |= 0x04;
            if (pUnit->pMonsterData->fChamp & 1)
                SpecType |= 0x02;
            if ((pUnit->pMonsterData->fBoss & 1) && (pUnit->pMonsterData->fNormal & 1))
                SpecType |= 0x01;
            if (pUnit->pMonsterData->fNormal & 1)
                SpecType |= 0x00;
            vp.setInt32(SpecType);
            return JS_TRUE;
        }
        break;

So spectype 0x1 can never actually happen alone.. it will always be 0x5 in that case because of the bitwise OR. If you have seen both 0x4 and 0x5 for bosses, we should try to find out what differentiates those...

Edit:

went a little deeper even, this object comes from D2 client:

struct MonsterData {
    BYTE _1[22]; // 0x00
    struct {
        BYTE fUnk : 1;
        BYTE fNormal : 1;
        BYTE fChamp : 1;
        BYTE fBoss : 1;
        BYTE fMinion : 1;
    }; // 0x16
    BYTE _2[5];
    BYTE anEnchants[9]; // 0x1C
    WORD wUniqueNo;     // 0x26
    DWORD _5;           // 0x28
    struct {
        wchar_t wName[28];
    }; // 0x2C
};

So there is another flag called fUnk (probably unknown) would be interesting to check that too

DarkHorseDre commented 4 years ago

@Fa-b This is great work and insight man! I assume this is C++ or something (I don't code so makes no little to me lol) - but it looks as if there is no distinction between unique and superunique (boss)?

or is that what this line is trying to do, via combing fnormal and fBoss? is it saying "if unit has definition of fboss AND normal mob, spectype = 0x01"?

if ((pUnit->pMonsterData->fBoss & 1) && (pUnit->pMonsterData->fNormal & 1))

Is pMonsterData a file/table of monster stats?

Is it as simple as the game doesnt store the difference between superunique and unique so d2bs created spectype, and may have made a mistake?

DarkHorseDre commented 4 years ago

Edit:

for the edit - wow! I still dont see a reference to unique vs superunique..

Fa-b commented 4 years ago

I still dont see a reference to unique vs superunique..

That we have to figure out now, it should be easy enough to make a d2bs change for testing only.. The last line:

if (pUnit->pMonsterData->fNormal & 1)
        SpecType |= 0x00;

doesn't make sense anyway.

Is it as simple as the game doesnt store the difference between superunique and unique so d2bs created spectype, and may have made a mistake?

There is not really a mistake IMO, just the way spectype is defined in D2BS we loose the differentiation between 'object is null' and 'monster type is normal'.. not sure if it matters tbh.

Edit:

or is that what this line is trying to do, via combing fnormal and fBoss? is it saying "if unit has definition of fboss AND normal mob, spectype = 0x01"?

It is not saying spectype = 0x01, it is saying spectype |= 0x01 which is bitwise OR. Meaning: 1) start spectype = 0 2) It is boss so make spectype = spectype OR 0x04 = 0x04 // same as 0b00000100 3) It is also normal so make spectype = spectype OR 0x01 = 0x05 // same as 0b00000101 4) It is still normal too so make spectype = spectype OR 0x00 = 0x05 // no change at all

DarkHorseDre commented 4 years ago

Fair enough - I cant read the code properly :)

Do you see a way of identifying a superunique vs unique in this code?

ps - just checked in SP travi - 2 random uniques = 4, and council member = 5

Fa-b commented 4 years ago

thats interesting, so maybe generally questrelated uniques will be also normal

DarkHorseDre commented 4 years ago

ok so some more testing - champs = 6, corpsefire (non quest superunique) = 5

DarkHorseDre commented 4 years ago

Edit:

or is that what this line is trying to do, via combing fnormal and fBoss? is it saying "if unit has definition of fboss AND normal mob, spectype = 0x01"?

It is not saying spectype = 0x01, it is saying spectype |= 0x01 which is bitwise OR. Meaning:

  1. start spectype = 0
  2. It is boss so make spectype = spectype OR 0x04 = 0x04 // same as 0b00000100
  3. It is also normal so make spectype = spectype OR 0x01 = 0x05 // same as 0b00000101
  4. It is still normal too so make spectype = spectype OR 0x00 = 0x05 // no change at all

T4T! ok so it seems like binary addition which is what I expected (plus the matching you mention) - I guess I dont understand how mob can be normal and "boss" - even from kolton code I saw the term "boss" as vague.

didnt the code you posted before say boss + normal = 0x01? (but it never occurs)

Fa-b commented 4 years ago
again, it is not addition but bitwise OR: left bit right bit OR'ed
0 0 0
0 1 1
1 0 1
1 1 1

As opposed to bitwise AND:

left bit right bit AND'ed
0 0 0
0 1 0
1 0 0
1 1 1
Next, it is using if ... if ... if as opposed to if ... else if ... else if, meaning they will all be evaluated. So:
00000000
if (pUnit->pMonsterData->fBoss & 1 OR 00000100
if ((pUnit->pMonsterData->fBoss & 1) &&
(pUnit->pMonsterData->fNormal & 1))
OR 00000001
if (pUnit->pMonsterData->fNormal & 1) OR 00000000
= 00000101

That's the reason why 0x01 never happens.

DarkHorseDre commented 4 years ago

Oh, no I meant in terms of 0x01 = normal, 0x04 being boss, and 0x01 + 0x04 = 0x05 (normal boss + boss) as per the table posted above from a while back, but I prolly got that wrong too - I'll read up :)

Fa-b commented 4 years ago

you will realize that the person who posted that table was me, and I simply checked the api of d2bs rather than looking through code if it makes sense back then... 0x01 is not a possible option from the code.

DarkHorseDre commented 4 years ago

nice :)

So I was thinking of my last discussion of this over a year ago and went looking for it to reference to you, but then found this more recent and more enlightening thread which seems to point to an answer: kolton/d2bot-with-kolbot#2040

Can you validate?

I agree the documentation is baaaaad but the link to Noahs work is one I'd never seen and gives answers, but still leaves questions.

If I'm correct, to kill champs and "normal" uniques (not super uniques/bosses) the mask would be 0x3 (or 0xB to include minions)? (I swear I tried 0x3 before!)

If so, then no changes are needed except to the recommended options in the char config, and update to documentation to provide the list in the link above (and as pasted below)

spectype monster types to kill 0x0 all 0x1 "Normal" Boss 0x2 Champion 0x3 "Normal" Boss + Champion 0x4 Boss 0x5 "Normal" Boss + Boss 0x6 Champion + Boss 0x7 "Normal" Boss + Champion + Boss 0x8 Minion 0x9 "Normal" Boss + Minion 0xA Champion + Minion 0xB "Normal" Boss + Champion + Minion 0xC Boss + Minion 0xD "Normal" Boss + Boss + Minion 0xE Champion + Boss + Minion 0xF "Normal" Boss + Champion + Boss + Minion

this table

Fa-b commented 4 years ago

mhm, have you looked yet :)

DarkHorseDre commented 4 years ago

Anyway, are you confirming that the spectype for a normal unique should be 0x01? do you know what it should be (despite the logic issue in the code you've found)?

Fa-b commented 4 years ago

as @5noop pointed out, the spectype is basically a reformat of MonsterData which comes directly from d2 client.. I don't think anybody other than blizzard dev will be able to tell you why it is the way it is, and why some have 2 flags set and others only 1.. it might simply be because different developers decided differently while working on monsters back in the dev time of d2... maybe there is a real differentiation.. right now you have corpsefire and council members as 0x05 which are superuniques according to this.

Edit: I think I get now what you mean... No, I think the naming "Normal" Boss is done because it is used that way in spectype... otherwise if there was a single flag from d2 showing normal uniques, they would show as 0x00 for you now.

DarkHorseDre commented 4 years ago

Thanks bro - I think you see the reasons for my confusion (being a n44p and also the inconsistencies you mention which are not immediately evident/understandable to most).

I was told this is correct but knew something was wrong even if I didn't know why!

I'm glad you looked into it as it means we may get a solution, or at least avoid using spectype (in some instances)

I'm also gonna look at what you guys have written as I realise there are a few things I need to learn! :)

Nishimura-Katsuo commented 4 years ago

fNormal is misnamed, and actually should be named fSuper as it indicates that a monster is super unique

DarkHorseDre commented 4 years ago

fNormal is misnamed, and actually should be named fSuper as it indicates that a monster is super unique

Awesome find!

So we should have normal, champ boss and minion - so a boss will be a boss like Andy, a champ is a blue champ pack, so what would a superunique like corpsefire be classed as?

Nishimura-Katsuo commented 4 years ago

fBoss actually just means unique monster, so fBoss + fNormal (super) would be set, so super uniques would have spectype 0x5 whereas normal uniques would have spectype 0x4, and act bosses are regular spawns with fBoss set, so they come up as uniques anyway, and you'll want to identify those by specific class id to differentiate them... they have to have an entry in SuperUniques.txt (see https://api.blizzhackers.dev/json/d2/SuperUniques.json for a quick reference) to have fNormal (super) set

DarkHorseDre commented 4 years ago

Thanks for that Nishimura - that json is pretty interesting!

So its like this?

monsdata struct desc spectype
fboss unique/act boss 0x4
fboss + normal super unique 0x5

So it seems even when working correctly, the bot will see an act boss the same as other uniques - this would mean that the clearpath by spectype would need to exclude act boss class id's right?

I tried to implement before Config.Clearpath was added, but seems it has the same issue.

Not sure if I described it here or elsewhere, example: start andy function --> clearpath from cata2 wp (clearing champs and uniques, which still may be an issue, but for this example, just: clearpath 0x4) to cata 4, then stop clear path --> return to andy function where the bot identifies andy by classid and kills.

As it stands, clearpath kills andy, then andy script doesn't find her and errors out.

DarkHorseDre commented 4 years ago

looked at d2bs:

        case UNIT_SPECTYPE:
        DWORD SpecType;
        SpecType = NULL;
        if (pUnit->dwType == UNIT_MONSTER && pUnit->pMonsterData) {
            if (pUnit->pMonsterData->fMinion & 1)
                SpecType |= 0x08;
            if (pUnit->pMonsterData->fBoss & 1)
                SpecType |= 0x04;
            if (pUnit->pMonsterData->fChamp & 1)
                SpecType |= 0x02;
            if ((pUnit->pMonsterData->fBoss & 1) && (pUnit->pMonsterData->fNormal & 1))
                SpecType |= 0x01;
            if (pUnit->pMonsterData->fNormal & 1)
                SpecType |= 0x00;
            vp.setInt32(SpecType);
            return JS_TRUE;
        }
        break;

So spectype 0x1 can never actually happen alone.. it will always be 0x5 in that case because of the bitwise OR. If you have seen both 0x4 and 0x5 for bosses, we should try to find out what differentiates those...

Edit:

went a little deeper even, this object comes from D2 client:

struct MonsterData {
    BYTE _1[22]; // 0x00
    struct {
        BYTE fUnk : 1;
        BYTE fNormal : 1;
        BYTE fChamp : 1;
        BYTE fBoss : 1;
        BYTE fMinion : 1;
    }; // 0x16
    BYTE _2[5];
    BYTE anEnchants[9]; // 0x1C
    WORD wUniqueNo;     // 0x26
    DWORD _5;           // 0x28
    struct {
        wchar_t wName[28];
    }; // 0x2C
};

So there is another flag called fUnk (probably unknown) would be interesting to check that too

@Nishimura-Katsuo Also, I saw you post on github the monsterdata code, but the fNormal issue you found doesn't exist within what Fab posted earlier in this thread. just FYI as this is beyond me :)

Nishimura-Katsuo commented 4 years ago

I was just posting to clarify what clear path can or can't do based on the spectype. Act bosses aren't differentiated because there's nothing special set in the spec type for them. They need to be filtered by classid, so clearpath should probably filter out non-trivial things like act bosses, ubers, and special monsters that are tricky (like tomb vipers) or have high hp (like izual).

DarkHorseDre commented 4 years ago

Thanks man - I was told that before but wanted to see if the root issue(s) with spectype would help avoid needing to check against all boss classid's - but I will do it now :)