2022-10-02 18:31:08 +08:00
|
|
|
|
using System;
|
2020-03-23 19:59:00 +08:00
|
|
|
|
using System.Collections.Generic;
|
2022-10-02 18:31:08 +08:00
|
|
|
|
using MinecraftClient.Mapping;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
using Tomlet.Attributes;
|
2020-03-23 19:59:00 +08:00
|
|
|
|
|
|
|
|
|
|
namespace MinecraftClient.ChatBots
|
|
|
|
|
|
{
|
2020-03-28 00:48:41 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The AutoAttack bot will automatically attack any hostile mob close to the player
|
|
|
|
|
|
/// </summary>
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public class AutoAttack : ChatBot
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public static Configs Config = new();
|
2020-08-18 19:02:36 +08:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
[TomlDoNotInlineObject]
|
|
|
|
|
|
public class Configs
|
2020-08-18 19:02:36 +08:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
[NonSerialized]
|
|
|
|
|
|
private const string BotName = "AutoAttack";
|
2020-08-18 19:02:36 +08:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public bool Enabled = false;
|
2021-03-28 19:26:38 +08:00
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.Mode$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public AttackMode Mode = AttackMode.single;
|
2022-08-15 17:31:17 -04:00
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.Priority$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public PriorityType Priority = PriorityType.distance;
|
|
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.Cooldown_Time$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public CooldownConfig Cooldown_Time = new(false, 1.0);
|
|
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.Interaction$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public InteractType Interaction = InteractType.Attack;
|
|
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.Attack_Hostile$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public bool Attack_Hostile = true;
|
|
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.Attack_Passive$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public bool Attack_Passive = false;
|
|
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.List_Mode$")]
|
2022-10-07 21:49:05 +08:00
|
|
|
|
public ListType List_Mode = ListType.whitelist;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
|
2022-11-30 16:22:48 +08:00
|
|
|
|
[TomlInlineComment("$ChatBot.AutoAttack.Entites_List$")]
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public List<EntityType> Entites_List = new() { EntityType.Zombie, EntityType.Cow };
|
|
|
|
|
|
|
|
|
|
|
|
public void OnSettingUpdate()
|
2021-03-28 19:26:38 +08:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (Cooldown_Time.Custom && Cooldown_Time.value <= 0)
|
2021-03-28 19:26:38 +08:00
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
LogToConsole(BotName, Translations.bot_autoAttack_invalidcooldown);
|
2022-10-05 15:02:30 +08:00
|
|
|
|
Cooldown_Time.value = 1.0;
|
2021-03-28 19:26:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-09-27 13:35:29 +02:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public enum AttackMode { single, multi };
|
2022-09-27 13:35:29 +02:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public enum PriorityType { distance, health };
|
|
|
|
|
|
|
|
|
|
|
|
public enum ListType { blacklist, whitelist };
|
|
|
|
|
|
|
|
|
|
|
|
public struct CooldownConfig
|
2022-09-27 13:35:29 +02:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public bool Custom;
|
|
|
|
|
|
public double value;
|
2022-09-27 13:35:29 +02:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public CooldownConfig()
|
2022-09-27 13:35:29 +02:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
Custom = false;
|
|
|
|
|
|
value = 0;
|
2022-09-27 14:23:19 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public CooldownConfig(double value)
|
|
|
|
|
|
{
|
|
|
|
|
|
Custom = true;
|
|
|
|
|
|
this.value = value;
|
|
|
|
|
|
}
|
2022-09-27 14:23:19 +02:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
public CooldownConfig(bool Override, double value)
|
2022-09-27 14:23:19 +02:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
this.Custom = Override;
|
|
|
|
|
|
this.value = value;
|
2022-09-27 13:35:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-08-18 19:02:36 +08:00
|
|
|
|
}
|
2020-03-23 19:59:00 +08:00
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
private readonly Dictionary<int, Entity> entitiesToAttack = new(); // mobs within attack range
|
|
|
|
|
|
private int attackCooldown = 6;
|
|
|
|
|
|
private int attackCooldownCounter = 6;
|
|
|
|
|
|
private Double attackSpeed = 4;
|
|
|
|
|
|
private Double attackCooldownSeconds;
|
|
|
|
|
|
private readonly bool overrideAttackSpeed = false;
|
|
|
|
|
|
private readonly int attackRange = 4;
|
|
|
|
|
|
private Double serverTPS;
|
|
|
|
|
|
private float health = 100;
|
|
|
|
|
|
private readonly bool attackHostile = true;
|
|
|
|
|
|
private readonly bool attackPassive = false;
|
|
|
|
|
|
|
|
|
|
|
|
public AutoAttack()
|
|
|
|
|
|
{
|
|
|
|
|
|
overrideAttackSpeed = Config.Cooldown_Time.Custom;
|
|
|
|
|
|
if (Config.Cooldown_Time.Custom)
|
|
|
|
|
|
{
|
|
|
|
|
|
attackCooldownSeconds = Config.Cooldown_Time.value;
|
|
|
|
|
|
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
attackHostile = Config.Attack_Hostile;
|
|
|
|
|
|
attackPassive = Config.Attack_Passive;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-23 19:59:00 +08:00
|
|
|
|
public override void Initialize()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!GetEntityHandlingEnabled())
|
|
|
|
|
|
{
|
2022-10-28 11:13:20 +08:00
|
|
|
|
LogToConsole(Translations.extra_entity_required);
|
|
|
|
|
|
LogToConsole(Translations.general_bot_unload);
|
2020-03-23 19:59:00 +08:00
|
|
|
|
UnloadBot();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void Update()
|
|
|
|
|
|
{
|
2020-04-24 22:14:30 +02:00
|
|
|
|
if (AutoEat.Eating | health == 0)
|
|
|
|
|
|
return;
|
2020-04-24 09:02:51 +12:00
|
|
|
|
|
|
|
|
|
|
if (attackCooldownCounter == 0)
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2020-04-24 09:02:51 +12:00
|
|
|
|
attackCooldownCounter = attackCooldown;
|
|
|
|
|
|
if (entitiesToAttack.Count > 0)
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (Config.Mode == Configs.AttackMode.single)
|
2020-08-18 19:02:36 +08:00
|
|
|
|
{
|
|
|
|
|
|
int priorityEntity = 0;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (Config.Priority == Configs.PriorityType.distance) // closest distance priority
|
2020-08-18 19:02:36 +08:00
|
|
|
|
{
|
|
|
|
|
|
double distance = 5;
|
|
|
|
|
|
foreach (var entity in entitiesToAttack)
|
|
|
|
|
|
{
|
|
|
|
|
|
var tmp = GetCurrentLocation().Distance(entity.Value.Location);
|
|
|
|
|
|
if (tmp < distance)
|
|
|
|
|
|
{
|
|
|
|
|
|
priorityEntity = entity.Key;
|
|
|
|
|
|
distance = tmp;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else // low health priority
|
|
|
|
|
|
{
|
2020-08-20 13:28:40 +02:00
|
|
|
|
float entityHealth = int.MaxValue;
|
2020-08-18 19:02:36 +08:00
|
|
|
|
foreach (var entity in entitiesToAttack)
|
|
|
|
|
|
{
|
2020-08-20 13:28:40 +02:00
|
|
|
|
if (entity.Value.Health < entityHealth)
|
2020-08-18 19:02:36 +08:00
|
|
|
|
{
|
|
|
|
|
|
priorityEntity = entity.Key;
|
2020-08-20 13:28:40 +02:00
|
|
|
|
entityHealth = entity.Value.Health;
|
2020-08-18 19:02:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-06-28 17:37:16 +02:00
|
|
|
|
|
2022-02-13 01:10:10 +01:00
|
|
|
|
if (entitiesToAttack.ContainsKey(priorityEntity))
|
2020-08-18 19:02:36 +08:00
|
|
|
|
{
|
2022-02-13 01:10:10 +01:00
|
|
|
|
// check entity distance and health again
|
2022-10-02 18:31:08 +08:00
|
|
|
|
if (ShouldAttackEntity(entitiesToAttack[priorityEntity]))
|
2022-02-13 01:10:10 +01:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
InteractEntity(priorityEntity, Config.Interaction); // hit the entity!
|
2022-02-13 01:10:10 +01:00
|
|
|
|
SendAnimation(Inventory.Hand.MainHand); // Arm animation
|
|
|
|
|
|
}
|
2020-08-18 19:02:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2020-08-18 19:02:36 +08:00
|
|
|
|
foreach (KeyValuePair<int, Entity> entity in entitiesToAttack)
|
2020-04-26 11:38:03 +12:00
|
|
|
|
{
|
2020-08-18 19:02:36 +08:00
|
|
|
|
// check that we are in range once again.
|
2022-10-02 18:31:08 +08:00
|
|
|
|
if (ShouldAttackEntity(entity.Value))
|
2020-08-18 19:02:36 +08:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
InteractEntity(entity.Key, Config.Interaction); // hit the entity!
|
2020-08-18 19:02:36 +08:00
|
|
|
|
}
|
2020-04-26 11:38:03 +12:00
|
|
|
|
}
|
2020-08-18 19:02:36 +08:00
|
|
|
|
SendAnimation(Inventory.Hand.MainHand); // Arm animation
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-04-24 09:02:51 +12:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
attackCooldownCounter--;
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnEntitySpawn(Entity entity)
|
|
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
ShouldAttackEntity(entity);
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
2020-04-24 22:14:30 +02:00
|
|
|
|
|
2020-03-24 15:03:32 +08:00
|
|
|
|
public override void OnEntityDespawn(Entity entity)
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2020-04-23 22:17:20 +12:00
|
|
|
|
if (entitiesToAttack.ContainsKey(entity.ID))
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2020-04-23 22:17:20 +12:00
|
|
|
|
entitiesToAttack.Remove(entity.ID);
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-04-24 22:14:30 +02:00
|
|
|
|
|
2022-06-28 17:37:16 +02:00
|
|
|
|
public override void OnEntityHealth(Entity entity, float health)
|
|
|
|
|
|
{
|
2022-09-27 13:35:29 +02:00
|
|
|
|
if (!IsAllowedToAttack(entity))
|
2022-06-28 17:37:16 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (entitiesToAttack.ContainsKey(entity.ID))
|
|
|
|
|
|
{
|
|
|
|
|
|
entitiesToAttack[entity.ID].Health = health;
|
|
|
|
|
|
|
|
|
|
|
|
if (entitiesToAttack[entity.ID].Health <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
entitiesToAttack.Remove(entity.ID);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-27 13:35:29 +02:00
|
|
|
|
private bool IsAllowedToAttack(Entity entity)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (attackHostile && entity.Type.IsHostile())
|
|
|
|
|
|
result = true;
|
|
|
|
|
|
|
|
|
|
|
|
if (attackPassive && entity.Type.IsPassive())
|
|
|
|
|
|
result = true;
|
|
|
|
|
|
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (Config.Entites_List.Count > 0)
|
2022-09-27 14:23:19 +02:00
|
|
|
|
{
|
2022-10-05 15:02:30 +08:00
|
|
|
|
bool inList = Config.Entites_List.Contains(entity.Type);
|
|
|
|
|
|
if (Config.List_Mode == Configs.ListType.blacklist)
|
|
|
|
|
|
result = !inList && result;
|
|
|
|
|
|
else
|
|
|
|
|
|
result = inList;
|
2022-09-27 14:23:19 +02:00
|
|
|
|
}
|
2022-09-27 13:35:29 +02:00
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-23 19:59:00 +08:00
|
|
|
|
public override void OnEntityMove(Entity entity)
|
|
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
ShouldAttackEntity(entity);
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-04-24 09:02:51 +12:00
|
|
|
|
public override void OnHealthUpdate(float health, int food)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.health = health;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-23 19:59:00 +08:00
|
|
|
|
public override void OnPlayerProperty(Dictionary<string, double> prop)
|
|
|
|
|
|
{
|
2021-03-28 19:26:38 +08:00
|
|
|
|
if (overrideAttackSpeed)
|
|
|
|
|
|
return;
|
2020-08-17 23:08:50 +08:00
|
|
|
|
foreach (var attackSpeedKey in new[] { "generic.attackSpeed", "minecraft:generic.attack_speed" })
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2020-08-17 23:08:50 +08:00
|
|
|
|
// adjust auto attack cooldown for maximum attack damage
|
|
|
|
|
|
if (prop.ContainsKey(attackSpeedKey))
|
2020-03-23 19:59:00 +08:00
|
|
|
|
{
|
2020-08-17 23:08:50 +08:00
|
|
|
|
if (attackSpeed != prop[attackSpeedKey])
|
|
|
|
|
|
{
|
|
|
|
|
|
serverTPS = GetServerTPS();
|
|
|
|
|
|
attackSpeed = prop[attackSpeedKey];
|
2021-03-28 19:26:38 +08:00
|
|
|
|
attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown
|
|
|
|
|
|
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
|
2020-08-17 23:08:50 +08:00
|
|
|
|
}
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnServerTpsUpdate(double tps)
|
|
|
|
|
|
{
|
2021-03-28 19:26:38 +08:00
|
|
|
|
if (overrideAttackSpeed)
|
|
|
|
|
|
return;
|
2022-09-27 13:35:29 +02:00
|
|
|
|
|
2020-03-23 19:59:00 +08:00
|
|
|
|
serverTPS = tps;
|
|
|
|
|
|
// re-calculate attack speed
|
2021-03-28 19:26:38 +08:00
|
|
|
|
attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown
|
|
|
|
|
|
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
2020-04-23 22:17:20 +12:00
|
|
|
|
|
2020-04-26 11:38:03 +12:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Checks to see if the entity should be attacked. If it should be attacked, it will add the entity
|
|
|
|
|
|
/// To a list of entities to attack every few ticks.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="entity">The entity to handle</param>
|
|
|
|
|
|
/// <returns>If the entity should be attacked</returns>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
public bool ShouldAttackEntity(Entity entity)
|
2020-04-23 22:17:20 +12:00
|
|
|
|
{
|
2022-09-27 13:35:29 +02:00
|
|
|
|
if (!IsAllowedToAttack(entity) || entity.Health <= 0)
|
2020-04-26 11:38:03 +12:00
|
|
|
|
return false;
|
2020-04-23 22:17:20 +12:00
|
|
|
|
|
|
|
|
|
|
bool isBeingAttacked = entitiesToAttack.ContainsKey(entity.ID);
|
2020-08-15 02:43:06 +08:00
|
|
|
|
if (GetCurrentLocation().Distance(entity.Location) <= attackRange)
|
2020-04-23 22:17:20 +12:00
|
|
|
|
{
|
|
|
|
|
|
// check to see if entity has not been marked as tracked, and if not, track it.
|
|
|
|
|
|
if (!isBeingAttacked)
|
|
|
|
|
|
{
|
|
|
|
|
|
entitiesToAttack.Add(entity.ID, entity);
|
|
|
|
|
|
}
|
2020-04-26 11:38:03 +12:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2020-04-24 22:14:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
2020-04-23 22:17:20 +12:00
|
|
|
|
{
|
|
|
|
|
|
// remove marker on entity to attack it, as it is now out of range
|
|
|
|
|
|
if (isBeingAttacked)
|
|
|
|
|
|
{
|
|
|
|
|
|
entitiesToAttack.Remove(entity.ID);
|
|
|
|
|
|
}
|
2020-04-26 11:38:03 +12:00
|
|
|
|
|
|
|
|
|
|
return false;
|
2020-04-23 22:17:20 +12:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-03-23 19:59:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|