Skip to content

Commit

Permalink
Update TrainerBot to interact with anything surrounding it
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanmoffat committed May 9, 2021
1 parent a4748cd commit b9ff4cf
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 60 deletions.
192 changes: 132 additions & 60 deletions EOBot/TrainerBot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,92 +70,147 @@ protected override async Task DoWorkAsync(CancellationToken ct)
_characterRepository = c.Resolve<ICharacterRepository>();
_characterActions = c.Resolve<ICharacterActions>();
var mapCellStateProvider = c.Resolve<IMapCellStateProvider>();
var mapStateProvider = c.Resolve<ICurrentMapStateProvider>();
var handler = c.Resolve<IOutOfBandPacketHandler>();
_itemData = c.Resolve<IEIFFileProvider>().EIFFile;
_npcData = c.Resolve<IENFFileProvider>().ENFFile;
_spellData = c.Resolve<IESFFileProvider>().ESFFile;
var charInventoryRepo = c.Resolve<ICharacterInventoryRepository>();
var walkValidator = c.Resolve<IWalkValidationActions>();

var healItems = new List<IInventoryItem>();
var healSpells = new List<IInventorySpell>();

int attackCount = 0;
int attackCount = 0, cachedPlayerCount = 0;
bool time_to_die = false;

MapCoordinate? priorityCoord = null;
while (!TerminationRequested)
{
handler.PollForPacketsAndHandle();

var character = _characterRepository.MainCharacter;
var charRenderProps = character.RenderProperties;

var nextX = charRenderProps.GetDestinationX();
var nextY = charRenderProps.GetDestinationY();
var currentCellState = mapCellStateProvider.GetCellStateAt(charRenderProps.MapX, charRenderProps.MapY);
var cellState = mapCellStateProvider.GetCellStateAt(nextX, nextY);
var currentPositionCellState = mapCellStateProvider.GetCellStateAt(charRenderProps.MapX, charRenderProps.MapY);

if (!time_to_die)
if (cachedPlayerCount != mapStateProvider.Characters.Count)
{
if ((attackCount < CONSECUTIVE_ATTACK_COUNT && !currentCellState.NPC.HasValue) || cellState.NPC.HasValue)
cachedPlayerCount = mapStateProvider.Characters.Count;
if (cachedPlayerCount > 0)
{
if (cellState.NPC.HasValue && character.Stats[CharacterStat.HP] > character.Stats[CharacterStat.MaxHP] * .1)
{
await Attack(cellState);
attackCount++;
}
else if (cellState.Items.Any())
{
await PickUpItems(cellState);
}
else if (healSpells.Any() && character.Stats[CharacterStat.HP] < character.Stats[CharacterStat.MaxHP]
&& character.Stats[CharacterStat.TP] > character.Stats[CharacterStat.MaxTP] * .5)
{
await CastHealSpell(healSpells);
}
else if (healItems.Any() && character.Stats[CharacterStat.HP] < character.Stats[CharacterStat.MaxHP] * .3)
{
await UseHealItem(healItems, targetHealthPercent: .6);
}
else if (character.Stats[CharacterStat.Weight] >= character.Stats[CharacterStat.MaxWeight])
{
await SitDown();
time_to_die = true;
}

healItems = charInventoryRepo.ItemInventory
.Where(x => _itemData.Data.Any(y => y.ID == x.ItemID && y.Type == ItemType.Heal))
.ToList();

healSpells = charInventoryRepo.SpellInventory
.Where(x => _spellData.Data.Any(y => y.ID == x.ID && y.Type == SpellType.Heal))
.ToList();
Console.WriteLine($"[WRN ] {cachedPlayerCount,7} - Players on map - You may not be able to train here");
}
else
{
if (!currentCellState.NPC.HasValue)
Console.WriteLine($"[MOVE] Walking due to consecutive attacks: {attackCount}");
else
Console.WriteLine($"[ATTK] Killing NPC at player location");
}

var originalDirection = charRenderProps.Direction;
await Walk();
await Face(originalDirection.Opposite());
var coords = new MapCoordinate[]
{
new MapCoordinate(charRenderProps.MapX + 1, charRenderProps.MapY),
new MapCoordinate(charRenderProps.MapX - 1, charRenderProps.MapY),
new MapCoordinate(charRenderProps.MapX, charRenderProps.MapY + 1),
new MapCoordinate(charRenderProps.MapX, charRenderProps.MapY - 1)
};

// kill NPC if it was in our starting point
while (currentCellState.NPC.HasValue && !TerminationRequested)
{
await Attack(currentCellState);
if (priorityCoord != null)
{
// ensure if there's an attack in progress that we finish killing that NPC before moving around
var tmp = new[] { priorityCoord.Value };
coords = tmp.Concat(coords.Except(tmp)).ToArray();
}

handler.PollForPacketsAndHandle();
currentCellState = mapCellStateProvider.GetCellStateAt(charRenderProps.MapX, charRenderProps.MapY);
}
var action_taken = false;
foreach (var coord in coords)
{
if (action_taken)
break;

await PickUpItems(currentCellState);
if (!time_to_die)
{
var cellState = mapCellStateProvider.GetCellStateAt(coord.X, coord.Y);

await Walk();
await Face(originalDirection);
if (priorityCoord.HasValue && !cellState.NPC.HasValue)
priorityCoord = null;

attackCount = 0;
if ((attackCount < CONSECUTIVE_ATTACK_COUNT && !currentPositionCellState.NPC.HasValue) || cellState.NPC.HasValue)
{
if (cellState.NPC.HasValue && character.Stats[CharacterStat.HP] > character.Stats[CharacterStat.MaxHP] * .1)
{
await FaceCoordinateIfNeeded(new MapCoordinate(charRenderProps.MapX, charRenderProps.MapY), coord);
await Attack(cellState);
attackCount++;
action_taken = true;
priorityCoord = coord;
}
else if (cellState.Items.Any())
{
await PickUpItems(cellState);
action_taken = true;
}
else if (healItems.Any() && character.Stats[CharacterStat.HP] < character.Stats[CharacterStat.MaxHP] * .3)
{
await UseHealItem(healItems, targetHealthPercent: .6);
action_taken = true;
}
else if (healSpells.Any() && character.Stats[CharacterStat.HP] < character.Stats[CharacterStat.MaxHP]
&& character.Stats[CharacterStat.TP] > character.Stats[CharacterStat.MaxTP] * .5)
{
await CastHealSpell(healSpells);
action_taken = true;
}
else if (character.Stats[CharacterStat.Weight] >= character.Stats[CharacterStat.MaxWeight])
{
await SitDown();
action_taken = true;
time_to_die = true;
}

healItems = charInventoryRepo.ItemInventory
.Where(x => _itemData.Data.Any(y => y.ID == x.ItemID && y.Type == ItemType.Heal))
.ToList();

healSpells = charInventoryRepo.SpellInventory
.Where(x => _spellData.Data.Any(y => y.ID == x.ID && y.Type == SpellType.Heal))
.ToList();
}
else
{
if (!currentPositionCellState.NPC.HasValue)
Console.WriteLine($"[MOVE] Walking due to consecutive attacks: {attackCount}");
else
Console.WriteLine($"[ATTK] Killing NPC at player location");

// find a direction that's open to walk to
var targetWalkCell = coords.Select(z => mapCellStateProvider.GetCellStateAt(z.X, z.Y))
.FirstOrDefault(z => walkValidator.IsCellStateWalkable(z));
if (targetWalkCell == null)
{
Console.WriteLine($"[WRN ] Couldn't find open space to walk!");
break;
}

await FaceCoordinateIfNeeded(new MapCoordinate(charRenderProps.MapX, charRenderProps.MapY), targetWalkCell.Coordinate);

var originalDirection = charRenderProps.Direction;
await Walk();
await Face(originalDirection.Opposite());

// kill NPC if it was in our starting point
while (currentPositionCellState.NPC.HasValue && !TerminationRequested)
{
await Attack(currentPositionCellState);

handler.PollForPacketsAndHandle();
currentPositionCellState = mapCellStateProvider.GetCellStateAt(charRenderProps.MapX, charRenderProps.MapY);
}

await PickUpItems(currentPositionCellState);

await Walk();
await Face(originalDirection);

attackCount = 0;
action_taken = true;
}
}
}

Expand All @@ -181,15 +236,32 @@ private async Task Walk()
private async Task Face(EODirection direction)
{
Console.WriteLine($"[FACE] {Enum.GetName(typeof(EODirection), direction),7}");
await TrySend(() => _characterActions.Face(direction.Opposite()));
await TrySend(() => _characterActions.Face(direction));

// todo: character actions Face() should also change the character's direction instead of relying on client to update it separately
_characterRepository.MainCharacter = _characterRepository.MainCharacter
.WithRenderProperties(_characterRepository.MainCharacter.RenderProperties.WithDirection(direction.Opposite()));
.WithRenderProperties(_characterRepository.MainCharacter.RenderProperties.WithDirection(direction));

await Task.Delay(TimeSpan.FromMilliseconds(FACE_BACKOFF_MS));
}

private async Task FaceCoordinateIfNeeded(MapCoordinate originalCoord, MapCoordinate targetCoord)
{
var charRenderProps = _characterRepository.MainCharacter.RenderProperties;
var nextCoord = new MapCoordinate(charRenderProps.GetDestinationX(), charRenderProps.GetDestinationY());
if (nextCoord != targetCoord)
{
var diff = targetCoord - originalCoord;
var direction = diff.X > 0 ? EODirection.Right
: diff.X < 0 ? EODirection.Left
: diff.Y > 0 ? EODirection.Down
: diff.Y < 0 ? EODirection.Up : charRenderProps.Direction;

if (direction != charRenderProps.Direction)
await Face(direction);
}
}

private async Task PickUpItems(IMapCellState cellState)
{
foreach (var item in cellState.Items)
Expand Down
8 changes: 8 additions & 0 deletions EOLib/Domain/Character/WalkValidationActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public bool CanMoveToCoordinates(int gridX, int gridY)
return false;

var cellState = _mapCellStateProvider.GetCellStateAt(gridX, gridY);
return IsCellStateWalkable(cellState);
}

public bool IsCellStateWalkable(IMapCellState cellState)
{
var mainCharacter = _characterProvider.MainCharacter;

if (cellState.Character.HasValue) //todo: walk through players after certain elapsed time
return mainCharacter.NoWall && IsTileSpecWalkable(cellState.TileSpec);
Expand Down Expand Up @@ -116,5 +122,7 @@ public interface IWalkValidationActions
bool CanMoveToDestinationCoordinates();

bool CanMoveToCoordinates(int gridX, int gridY);

bool IsCellStateWalkable(IMapCellState cellState);
}
}

0 comments on commit b9ff4cf

Please sign in to comment.