using MinecraftClient.Mapping; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MinecraftClient.ChatBots { /// /// The AutoAttack bot will automatically attack any hostile mob close to the player /// class AutoAttack : ChatBot { private Dictionary entitiesToAttack = new Dictionary(); // mobs within attack range private int attackCooldown = 6; private int attackCooldownCounter = 6; private Double attackSpeed = 4; private Double attackCooldownSeconds; private bool overrideAttackSpeed = false; private int attackRange = 4; private Double serverTPS; private float health = 100; private bool singleMode = true; private bool priorityDistance = true; private InteractType interactMode; public AutoAttack(string mode, string priority, bool overrideAttackSpeed = false, double cooldownSeconds = 1, InteractType interaction = InteractType.Attack) { if (mode == "single") singleMode = true; else if (mode == "multi") singleMode = false; else LogToConsoleTranslated("bot.autoAttack.mode", mode); if (priority == "distance") priorityDistance = true; else if (priority == "health") priorityDistance = false; else LogToConsoleTranslated("bot.autoAttack.priority", priority); interactMode = interaction; if (overrideAttackSpeed) { if (cooldownSeconds <= 0) { LogToConsoleTranslated("bot.autoAttack.invalidcooldown"); } else { this.overrideAttackSpeed = overrideAttackSpeed; this.attackCooldownSeconds = cooldownSeconds; attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); } } } public override void Initialize() { if (!GetEntityHandlingEnabled()) { LogToConsoleTranslated("extra.entity_required"); LogToConsoleTranslated("general.bot_unload"); UnloadBot(); } } public override void Update() { if (AutoEat.Eating | health == 0) return; if (attackCooldownCounter == 0) { attackCooldownCounter = attackCooldown; if (entitiesToAttack.Count > 0) { if (singleMode) { int priorityEntity = 0; if (priorityDistance) // closest distance priority { 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 { float entityHealth = int.MaxValue; foreach (var entity in entitiesToAttack) { if (entity.Value.Health < entityHealth) { priorityEntity = entity.Key; entityHealth = entity.Value.Health; } } } if (entitiesToAttack.ContainsKey(priorityEntity)) { // check entity distance and health again if (shouldAttackEntity(entitiesToAttack[priorityEntity])) { InteractEntity(priorityEntity, interactMode); // hit the entity! SendAnimation(Inventory.Hand.MainHand); // Arm animation } } } else { foreach (KeyValuePair entity in entitiesToAttack) { // check that we are in range once again. if (shouldAttackEntity(entity.Value)) { InteractEntity(entity.Key, interactMode); // hit the entity! } } SendAnimation(Inventory.Hand.MainHand); // Arm animation } } } else { attackCooldownCounter--; } } public override void OnEntitySpawn(Entity entity) { shouldAttackEntity(entity); } public override void OnEntityDespawn(Entity entity) { if (entitiesToAttack.ContainsKey(entity.ID)) { entitiesToAttack.Remove(entity.ID); } } public override void OnEntityHealth(Entity entity, float health) { if (!entity.Type.IsHostile()) return; if (entitiesToAttack.ContainsKey(entity.ID)) { entitiesToAttack[entity.ID].Health = health; if (entitiesToAttack[entity.ID].Health <= 0) { entitiesToAttack.Remove(entity.ID); } } } public override void OnEntityMove(Entity entity) { shouldAttackEntity(entity); } public override void OnHealthUpdate(float health, int food) { this.health = health; } public override void OnPlayerProperty(Dictionary prop) { if (overrideAttackSpeed) return; foreach (var attackSpeedKey in new[] { "generic.attackSpeed", "minecraft:generic.attack_speed" }) { // adjust auto attack cooldown for maximum attack damage if (prop.ContainsKey(attackSpeedKey)) { if (attackSpeed != prop[attackSpeedKey]) { serverTPS = GetServerTPS(); attackSpeed = prop[attackSpeedKey]; attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); } } } } public override void OnServerTpsUpdate(double tps) { if (overrideAttackSpeed) return; serverTPS = tps; // re-calculate attack speed attackCooldownSeconds = 1 / attackSpeed * (serverTPS / 20.0); // server tps will affect the cooldown attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1); } /// /// 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. /// /// The entity to handle /// If the entity should be attacked public bool shouldAttackEntity(Entity entity) { if (!entity.Type.IsHostile() || entity.Health <= 0) return false; bool isBeingAttacked = entitiesToAttack.ContainsKey(entity.ID); if (GetCurrentLocation().Distance(entity.Location) <= attackRange) { // check to see if entity has not been marked as tracked, and if not, track it. if (!isBeingAttacked) { entitiesToAttack.Add(entity.ID, entity); } return true; } else { // remove marker on entity to attack it, as it is now out of range if (isBeingAttacked) { entitiesToAttack.Remove(entity.ID); } return false; } } } }