Dancer AI

Does anyone have any experience with the Dance AI patch? I tried it out, and the AI still won’t dance. Is there a specific way the unit AI needs to be set up? Or does it not work with skill system?

I had a look at it one time and it seems complex. They’ll still prefer to attack you if their stats are better than the enemies around them, I think… something like that, anyway.

Did you ensure that they can’t do anything else useful during their turn?

1 Like

I did some more experimenting with it, and it looks like the AI will only dance for units that have both moved and taken some sort of action (attack, use a staff etc). It won’t dance for units that have only moved or only acted, which led to me thinking it wouldn’t dance at all as the test unit I was expecting it to dance for was only moving.

So it’s more limited than I was expecting, but I at least have an idea of how it works. Thanks for looking into it in any case.

I think you should try building the source code with small blocks commented out.

I don’t think it’s meant to behave how you describe. Maybe the problem is in here, for some reason?


if (!(unit->state & US_UNSELECTABLE))
				continue; // We don't want to dance for someone that hasn't moved yet!

			if (UNIT_ATTRIBUTES(unit) & (CA_DANCE | CA_PLAY))
				continue; // not dancing for other dancers

From poking around a bit, I think this where it’s checking whether the enemy units have moved and performed an action, but I’m not following how r1 is arrived at to make the comparison before the branch.

I don’t think looking at the disassembly is likely to be that useful. If you must look at the asm instead of the spurce C code, then I think you should build from the source and have it generate a .s file for you, as it will include generated comments on which lines of code are doing what.

Hmm, maybe it’s better that way, that enemy Dancers only refresh only those who did an action aside from Move. At least it gives players time to react.

Hey Permafrost, a long, long time ago I implemented DancerAI through Febuilder to my hack. I want to say that I had an issue in the beginning with it as well, though for one reason or another, it got solved (though I can’t remember why) and the dancers refreshed any unit who needed it regardless of their action or inaction.

I know it’s not too much help being that I can’t remember how or why it started working, but as a reference point (and I promise I’m not trying to shill my own hack haha), my hack uses DancerAI seldomly. If you were to download the Illuminated patch and go to Chapter 16 through Febuilder (Titled Dubious Dunes), there is an enemy dancer who will Dance for an enemy (who only moves and nothing else) on Turn 1.

I know it’s not a huge help, but maybe there is something in the AI that you may find. Sorry I can’t help more. I ended up mostly removing it from my hack because I found it much harder to balance than I thought it would be haha :man_shrugging:

2 Likes

I appreciate you dropping by to try to give a clue to what’s going on. I think that I stumbled on the solution by accident while looking over the settings in your hack. The only differences in AI setting were that you had changed the recovery mode AI, which I rarely bother with, but experimenting with that didn’t do anything. However, I increased the Dancer’s movement to make testing easier, and then it started working. After playing around a bit more, it looks like the Dancers will only dance for enemies that don’t both move and attack when the Dancer’s movement is greater than the movement of the target.

It’s weird, but it’s a limitation that’s fairly simple to work within. As far as I can tell, the Dancer in your hack dances for the Mercenary she spawns next to because you gave Dancers six movement. I would imagine if you want Dancers to work for standard unmounted promoted enemies you would need to give them seven move.

1 Like

That’s a really funky thing in the AI, but if it works, it works! Glad you were able to get something working

1 Like

@StanH you might wish to look into this sometime. Dancer ai is a cool hack, btw!

This might be tied to the fact that movement order is a tiebreaker for the order in which AI units move. In general, the order in which AI units move is:
Units with only 1 range weapons THEN Units with a weapon that can attack at 2 range THEN Units with no weapon.

Within a given weapon category, the units act from lowest move to highest move. So if you have a steel lance armor and a iron lance wyvern, the armor will always move first, since he has less move (there are some other less common factors that can also influence move order, but I’m ignoring those for simplicity).

You can occasionally see this lead the AI in vanilla to make some dumb decisions. For example, if you have a Bishop with a lightning tome and a physic staff, and a Nomad with a bow, you may see the Nomad attack and get injured by a counterattack while the Bishop does nothing instead of healing him. This is because the Bishop has a lower move and is in the same weapon class, so the Bishop acts first, does nothing because there’s no one to heal yet, and then the Nomad goes and gets hurt after the Bishop has passed his turn (note that red units that pass their turn don’t turn gray, so you can’t tell this is happening from the UI).

I suspect this is what is happening in your hack. The dancer is acting before the other unit, and thus when the dancer takes their turn there is no one to dance for.

Another tiebreaker for the order in which AI units act is their order in which units are listed in memory (so units at the bottom of the unit list in FEBuilder will move after other units with the same move, barring reinforcements). So if you want your dancers to act last, make them unarmed, give them high move, and put them at the end of the unit list for the chapter.

1 Like

Actually looking at the relevant portion of Stan’s decomp of FE8 (https://github.com/StanHash/fe8/blob/stan-main/src/cp_order.c ), there’s apparently a specific rule for dancers that gives them -149 to movement order priority. Since units with lower priority values move first, this makes them very likely to move before the units they’re supposed to be dancing, even if they’re unarmed.

int GetUnitAiPriority(struct Unit* unit)
{
int priority = UNIT_MOV(unit);

u16 lead = GetUnitLeaderPid(unit);

if (UNIT_ATTRIBUTES(unit) & (UNIT_ATTR_DANCE | UNIT_ATTR_PLAY))
    return priority - 149;

if (!(unit->aiFlags & AI_UNIT_FLAG_0))
{
    priority += lead << 8;

    if (UNIT_ATTRIBUTES(unit) & UNIT_ATTR_2)
        return priority + 60;

    if ((unit->pinfo->id == lead) || (UNIT_ATTRIBUTES(unit) & UNIT_ATTR_LORD))
        return priority + 87;

    priority = priority + GetUnitBattleAiPriority(unit);
}

return priority;

}

1 Like

Could someone try this out, then?

// Dancer AI Priority Fix
// by Vesly, Adthor 

PUSH 
ORG $3996A 
SHORT 0x3095 
POP 

This just changes return priority - 149; for dancers to return priority + 149;, I believe. It has not been tested yet.

That’s some interesting info about the AI’s movement order, but I don’t think that’s what’s at work here. If the the Dancer’s AI is set to something where it will move without needing a target such as Move Toward Enemy, it can be seen moving after other units. It would even run up to an enemy that had moved and then not dance.

Assuming that I installed it correctly, I didn’t notice any difference in the AI behavior with this change.

If AI1 (Dance) fails to do anything, it will try AI2 (Move Towards Enemy).

I was just saying that movement order isn’t the issue because the AI can be seen moving the dancer last whether it dances or not.

I think AI2 occurs after AI1, so it tells us nothing about the initial order. Say its AI1 acts before any other units and decides no one needs refreshing. Then other units perform attacking/staff actions. Then it tries AI2 which is move towards enemies. It will do that (and try to stay nearby other enemies while doing so). Then other enemies that couldn’t attack will move.

You could test the order of them by having a simple chapter with maybe 3 enemies, checking their ram address in febuilder units debugger, and breaking on the functions. AI scripts generally seem to take a unit’s ram address + ~0x45 or 0x47 iirc (AI1 or AI2 counter) as their parameter in r0. You can use this to tell which one is currently thinking during the break points, even if you don’t understand anything else that is going on with no$gba.

Ex. functions
AIScript12_Move_Towards_Enemy, 0x803ce18
AiSetDecision, 0x8039C20

You can see these addresses in fe8.sym