Interested in style-using NPCs?
PostPosted: Fri Dec 19, 2008 1:28 am
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:
Description of Approach
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)
Description of Approach
- If our last strike was a styled hit, attempt to use follow-up style
- Assign weights to some of the more common & important style effects - 1000 for stun, 500 for HealOverTime, 200 for StrengthDebuff
- Adjust weights based on target status: if target is already stunned, a style with a stun becomes less important
- Discount any styles that we can't execute: stealth, follow-up style, dual-wield, positional
- Apply weights to available styles, effectively giving them priorities - stun, DD, str/con debuff get high priority
- Factor in the style's growth rate to its weight (priority)
- Choose the best style (or one of the best, if the ones at the top are close)
- 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;
}
- 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;
}