Page 1 of 2

Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 1:28 am
by Kakuri
I've been working on some logic to get NPCs making somewhat intelligent use of styles. I've got a decent bit of logic worked out, but there are glitchy issues that remain.

I've been developing it as a static class so that it can be used by any inheritor of GameNPC in the GetStyleToUse() method. Perhaps it's wiser to integrate it directly into GameNPC, or an inheritor of GameNPC.

Some problems:
  • When first called, there is a NullReferenceException and delay as the mob recovers from it. After that, it works fairly well for choosing styles.
  • There seem to be timing issues - it doesn't seem to figure out quick enough when it can use a follow-up or reactive style
  • Positionals - often ignored; when used, it's very difficult to get the npc to move into position for them (i.e. after a stun)
I haven't cleaned up the code yet - there's a lot of commented stuff left in so you can see what I thought up first and abandoned.

Description of Approach
  1. If our last strike was a styled hit, attempt to use follow-up style
  2. Assign weights to some of the more common & important style effects - 1000 for stun, 500 for HealOverTime, 200 for StrengthDebuff
  3. Adjust weights based on target status: if target is already stunned, a style with a stun becomes less important
  4. Discount any styles that we can't execute: stealth, follow-up style, dual-wield, positional
  5. Apply weights to available styles, effectively giving them priorities - stun, DD, str/con debuff get high priority
  6. Factor in the style's growth rate to its weight (priority)
  7. Choose the best style (or one of the best, if the ones at the top are close)
I've also got the following code in an NPC to try and handle positioning & weapon-swapping. It's currently a little insane... hilarious to watch. Try giving an NPC a shield, LW, slam, and some LW positional styles. He will slam you, move into position, and hit you about twice a second, pretty much destroying you. Maybe WalkTo( x, y, 750) with high speed glitches him into swinging faster??
Code: Select all
protected override DOL.GS.Styles.Style GetStyleToUse()
{
Style style = base.GetStyleToUse();

GameLiving target = this.TargetObject as GameLiving;

if ( target == null || style == null )
return style;

// Maneuvering will likely fail if the target is up against a wall or something
// Let it fail? Or maneuver closer (say, 5 units instead of 65)?
if ( style.OpeningRequirementType == Style.eOpening.Positional )
{
float angle = target.GetAngleToTarget( this );
int movetoX;
int movetoY;

switch ( style.OpeningRequirementValue )
{
case (int)Style.eOpeningPosition.Front:
if ( !( angle >= 315 || angle < 45 ) )
{
target.GetSpotFromHeading( 65, out movetoX, out movetoY );
this.StopAttack();
this.WalkTo( movetoX, movetoY, target.Z, 750 );
}
break;
case (int)Style.eOpeningPosition.Side:
if ( !( angle >= 45 && angle < 150 ) && !( angle >= 210 && angle < 315 ) )
{
GetSpotFromSpotHeading( target.X, target.Y, (ushort)( target.Heading + ( 110.0 * (4096.0/360.0) ) ), 65, out movetoX, out movetoY );
this.StopAttack();
this.WalkTo( movetoX, movetoY, target.Z, 750 );
}
break;
case (int)Style.eOpeningPosition.Back:
if ( !( angle >= 150 && angle < 210 ) )
{
GetSpotFromSpotHeading( target.X, target.Y, (ushort)( target.Heading + 2048 ), 65, out movetoX, out movetoY );
this.StopAttack();
this.WalkTo( movetoX, movetoY, target.Z, 750 );
}
break;
}

this.TurnTo( target );
this.StartAttack( this.TargetObject );
}

// One might think it excessive to do this every single time a style is used, but
// sometimes (like when coming out of a stun) mobs forget to wield any weapon at all
switch ( style.WeaponTypeRequirement )
{
case (int)eObjectType.CelticSpear:
case (int)eObjectType.LargeWeapons:
case (int)eObjectType.MaulerStaff:
case (int)eObjectType.PolearmWeapon:
case (int)eObjectType.Scythe:
case (int)eObjectType.Spear:
case (int)eObjectType.Staff:
case (int)eObjectType.TwoHandedWeapon:
this.SwitchWeapon( eActiveWeaponSlot.TwoHanded );
this.UpdateNPCEquipmentAppearance();
break;
default:
this.SwitchWeapon( eActiveWeaponSlot.Standard );
this.UpdateNPCEquipmentAppearance();
break;
}

return style;
}
And these are some extra position-calculating functions used above:
Code: Select all
public void GetSpotFromSpotHeading( int fromX, int fromY, ushort pHeading, int distance, out int tx, out int ty )
{
double angle = pHeading / HEADING_CONST;
double targetX = fromX - Math.Sin( angle ) * distance;
double targetY = fromY + Math.Cos( angle ) * distance;

if ( targetX > 0 )
tx = (int)targetX;
else
tx = 0;
if ( targetY > 0 )
ty = (int)targetY;
else
ty = 0;
}

public ushort GetHeadingToSpot( int fromX, int fromY, int tx, int ty )
{
float dx = (long)tx - fromX;
float dy = (long)ty - fromY;

double heading = Math.Atan2( -dx, dy ) * HEADING_CONST;

if ( heading < 0 )
heading += 4096;

return (ushort)heading;
}

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 4:46 am
by Blues
You do
Code: Select all
if ( target == null || style == null )
return style;
if the style is null you return the style, doesnt work

in line 131 you do a check for the styleuser, you have done that before already, and also used that styleuser at that point.

This all looks very interestiing, especially the idea to make the npcs move to a position where they can do positional styles. But for me that would be to much code to execute it on each attack, i´d simplify it more and throw some things out. But thats just my preference.

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 7:26 am
by Dinberg
I think the problem with positionals is that StopAttack probably clears the style que, so that it forgets the style it was just about to use. As such, this would mean it could only perform a positional if it happened to already been in the position for the style at the time.

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 9:22 am
by Graveen
interesting.

Can you make this as a brain, Kakuri ?
I'm very interested for DoL in the case this is available as a brain, thus you can freely affect at choosen mobs.

/cheer !

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 11:04 am
by Argo
i am astonished every time i see with what ideas Kakuri is comin up. I wish i could program code like this. Kaku, you got my deepest respect man. You are my hero :D

respectful regards
Argoras

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 1:35 pm
by Dinberg
Same, I am very impressed. I was thinking though, could much of the weights calculation be done at the startup/on construction of the brain? This would help speed up think cycles. A temporary weighting category could then also be factored, to add additional priorities.

I'd be against letting npcs run around to use positionals at their will - unfortunately daoc wasnt designed with this in mind, and it looks odd for npcs to move like this. But if you will, I'd recommend using an ArriveAtTarget event to trigger the melee callback and take the melee swing when they arrive, though the approach would only really work for stationary targets.

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 3:33 pm
by Kakuri
If the style is null you return the style, doesnt work
The way GameNPCs currently work is that they return null for style (if they have no associated styles, which is generally the case), so it should be fine.
in line 131 you do a check for the styleuser, you have done that before already, and also used that styleuser at that point.
I am pretty much going insane by NullReferenceExceptions on variables that ARE NOT NULL and adding repeated sanity checks - I'm not crazy, C# is! :twisted:
This all looks very interestiing, especially the idea to make the npcs move to a position where they can do positional styles. But for me that would be to much code to execute it on each attack, i´d simplify it more and throw some things out. But thats just my preference.
It's really not that much code, and it's actually already done for every single style that's executed by StyleProcessor.CanUseStyle().

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 3:38 pm
by Kakuri
I think the problem with positionals is that StopAttack probably clears the style que, so that it forgets the style it was just about to use. As such, this would mean it could only perform a positional if it happened to already been in the position for the style at the time.
Yeah, I hope someone can help figure this out. I originally didn't have StopAttack, but the mob would only move about a third of the distance and then try another attack (which would fail since he wasn't in position), then move 1/3 again and attack (and fail), then move again and finally be in position, but by this point even a 9s slam stun has worn off and I'm facing him again, yet he tries & fails with the positional style again.

So we need some way to get the mob to briefly STOP attacking, move to position, THEN resume attacking. I even tried MoveTo (which is basically instant) instead of WalkTo, but the mob combat/attack state seems to get reset by MoveTo. I think its HP may have even been reset to full.

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 3:42 pm
by Kakuri
Can you make this as a brain, Kakuri ?
I'm very interested for DoL in the case this is available as a brain, thus you can freely affect at choosen mobs.
To be honest, I hardly understand brains at this point! I went looking through brains for style handling, didn't find any, and eventually found that GamePet has some random style selection in its GetStyleToUse function, so I proceeded with that as my starting point.

I do agree it would be nice to optionally add this to specific mobs (which is why I made it a static class), but a brain would be more flexible. I guess I'll look into it...

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 3:50 pm
by Kakuri
Same, I am very impressed. I was thinking though, could much of the weights calculation be done at the startup/on construction of the brain? This would help speed up think cycles. A temporary weighting category could then also be factored, to add additional priorities.
At this point, there really isn't much to the weight calculation (only 9 effects taken into consideration), but perhaps it would speed it up a bit to not repeat calculations (like the effect the style's growth rate has on weight). One part of the weight calculation that has to be done every time is the adjustments that are done after examining the effects on the target - if they already have the effect (or immunity), then the style gets a lower weight.
I'd be against letting npcs run around to use positionals at their will - unfortunately daoc wasnt designed with this in mind, and it looks odd for npcs to move like this. But if you will, I'd recommend using an ArriveAtTarget event to trigger the melee callback and take the melee swing when they arrive, though the approach would only really work for stationary targets.
Most mobs don't even have any styles associated with them. This is intended for special cases, like boss fights, keep lords, maybe even other keep guards. Positional styles are only considered if the target is stunned, or is not targetting the mob (then we can assume the target is not /facing us and it's possible we might be able to get in position - perhaps a check for "not moving" would be a good addition).

Re: Interested in style-using NPCs?

PostPosted: Fri Dec 19, 2008 4:09 pm
by Graveen
You have some example of brain where you know - look at scout brain, dragon brain, they are interesting.

i agree the npc using positional styles when target stunned is interesting, i could even say it is the point interesting me: having bosses handling the snare/stun - run - heal effect or stun - side - high gr style + high gr style.

The wreck to avoid is the actual keep/tower lord: infinite answer to a given situation (ie infinite heal), but with immunity and positionnal styles it seems regulated 'de fait'.

Re: Interested in style-using NPCs?

PostPosted: Sat Dec 20, 2008 12:50 am
by Dinberg
At this point, there really isn't much to the weight calculation (only 9 effects taken into consideration), but perhaps it would speed it up a bit to not repeat calculations (like the effect the style's growth rate has on weight). One part of the weight calculation that has to be done every time is the adjustments that are done after examining the effects on the target - if they already have the effect (or immunity), then the style gets a lower weight.
That's along the lines of what I was thinking ^^. Anything that is static (in the unchanging sense) we could pre-calculate, such as growth rate, bonus to defense etc, though arguably some of that could be seen as conditional aswell.

Maybe have three lists, each related to a different fighting style (eFightingStyle perhaps?). This could contain priorities for when the opponent is stunned, when nothing is directly attacking the npc (so to use high attack, low def styles in priority) and for when the npc is under heavy offense (prefers high def styles). It would be silly to suggest three lists of this way for every npc though, and perhaps an NPC style manager would be required for this particular setup. A bonus is that the mgr could be scripted, and on load would parse the DB styles to create these priority lists for each of the specs available; public static commands could then return required information fast during runtime, perhaps sorted by hashtable. For example, GetPreferredStyle(eFightingStyle, Specline).

Still, I'm not sure how the 'back up' chain would be constructed in this way, in the case of being unable to exectute styles. Perhaps it would instead use something along the lines of a GetPreferredStyle(eFightingStyle, specline, int priority) where 0 is the highest priority.
Code: Select all
int i = 0;
bool canHasStyle = false;
Style s = GetPrefferedStyle(m_fStyle, m_currentWeaponSpec, i);
while (!canHasStyle)
{
if (s == null)
break; //no style available to use, or we would already equal it. Return, or allow handling of nulls below.
if (!CanExecuteStyle(s))
{
i++;
s = GetPrefferedStyle(m_fStyle, m_currentWeaponSpec, i);
}
else
canHasStyle = true;
}
Note that the above would still require an additional check to prevent an infinite loop of if a line has no available styles. Perhaps in light of this, it would be better to have int i = GetNumberOfStyle(specline), and then decrease i until < 0. Priorities would need to be reversed if this was chosen.

EDIT: changed it a bit.

At any rate, I suppose what would be ideal is to move as much processing as can be into the loading of the server, so that runtime suffers a much smaller penalty due to increase thinking for style computation. CanExecuteStyle(s) would be only a few lines long, literally switching the various attack results to observe a simple true/false on whether we can use it. GetPrefferedStyle could simple read from a hashtable of index [eFightingStyle + specline.Name + priority], which should also be quite nippy.

Re: Interested in style-using NPCs?

PostPosted: Sat Dec 20, 2008 2:05 am
by Kakuri
One thing that I would like the code to be capable of is handling whatever list of styles the mob has available. You can assign any styles you like to a mob, not necessarily all in the same spec line. I would like it to be able to mimic player tactics like using 1H+shield, then slamming and switching to a Large Weapon or Polearm for example. It currently handles weapon switching pretty well - if the best style is 1H or shield, it actives the lefthand weapon; if the best style is any 2H weapon, it actives the 2H weapon. It will go back and forth from 1H to 2H during a fight.

So... I'd like to see how much time it currently takes to evaluate all styles a mob has each round. There's a lot of code, but also a lot of branching and no heavy math, so each iteration should really not hit that much code or take much time. It seems like there's some lag or concurrency issues with getting the AttackData... but maybe it is with the style choosing code and more optimization is needed.

Re: Interested in style-using NPCs?

PostPosted: Sat Dec 20, 2008 9:16 am
by Dinberg
I was thinking you could run the check for each weapon, perhaps the spec manager would return an int for the priority of the style - in this way, multiple spec lines could be observed, allowing for many different weapons to be accounted for. But remember that the melee timer is one of the more often run voids, so as a result it does need to be fast :D

Re: Interested in style-using NPCs?

PostPosted: Sat Dec 20, 2008 9:30 am
by Graveen
remember in the nptemplate you have the usable styles - and this is a good system tweak actual AI.

In the npcequipment you can affect weapons, but they are the only visible and this is not suitable in case of "stun/lw".

But the brain can consider each mob having:
- the mastering of complete styleline described in NPCT,
- a shield in addition of the weapon used in NPCE (and it's complete line)