package mhfc.net.common.ai.general.provider.simple; import java.util.Objects; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import mhfc.net.common.ai.IActionRecorder; import mhfc.net.common.ai.IExecutableAction; import mhfc.net.common.ai.general.AIUtils; import mhfc.net.common.eventhandler.ai.ActionSelectionEvent; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; public interface ISelectionPredicate<EntityT extends EntityLiving> { public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target); public static class SelectionAdapter<EntityT extends EntityLiving> implements ISelectionPredicate<EntityT> { private DistanceAdapter<EntityT> distanceAdapter; private AngleAdapter<EntityT> angleAdapter; /** * The angles are in degrees, negative values refer to the left side, positive to the right side */ public SelectionAdapter(float minAngle, float maxAngle, double minDistance, double maxDistance) { distanceAdapter = new DistanceAdapter<>(minDistance, maxDistance); angleAdapter = new AngleAdapter<>(minAngle, maxAngle); } @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { return distanceAdapter.shouldSelectAttack(attack, actor, target) && angleAdapter.shouldSelectAttack(attack, actor, target); } } public static class CooldownAdapter<EntityT extends EntityLiving> implements ISelectionPredicate<EntityT> { private int cooldown; private int cooldownRemaining; private IExecutableAction<? super EntityT> attack; ISelectionPredicate<? super EntityT> originalPredicate; public CooldownAdapter( IExecutableAction<? super EntityT> attack, int cooldown, ISelectionPredicate<EntityT> originalPredicate) { this.attack = Objects.requireNonNull(attack); this.originalPredicate = Objects.requireNonNull(originalPredicate); this.cooldown = cooldown; this.cooldownRemaining = 0; } @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { if (cooldownRemaining > 0) cooldownRemaining--; return cooldownRemaining == 0; } @SubscribeEvent public void onSelectionSuccess(ActionSelectionEvent ase) { if (ase.chosenAction == attack) cooldownRemaining = cooldown; } } public static class DistanceAdapter<EntityT extends EntityLiving> implements ISelectionPredicate<EntityT> { private double minDistance, maxDistance; public DistanceAdapter(double minDistance, double maxDistance) { this.minDistance = minDistance; this.maxDistance = maxDistance; } @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { if (target == null) return false; double distance = actor.getDistanceToEntity(target); return distance >= minDistance && distance <= maxDistance; } } /** * Selects only when the target entity is within a certain view angle. When minAngle is bigger than maxAngle, it * wraps around once. Negative angle refer to the left side, positive to the right side. * * @author Katora * * @param <EntityT> */ public static class AngleAdapter<EntityT extends EntityLiving> implements ISelectionPredicate<EntityT> { private float minAngle, maxAngle; public AngleAdapter(float minAngle, float maxAngle) { this.minAngle = minAngle; this.maxAngle = maxAngle; } @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { if (target == null) return false; float angle = AIUtils.getViewingAngle(actor, target); if (minAngle <= maxAngle) return angle >= minAngle && angle <= maxAngle; else return angle <= maxAngle || angle >= minAngle; } } public static class SelectIdleAdapter<EntityT extends EntityLiving> implements ISelectionPredicate<EntityT> { @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { return target == null; } } public static class SelectAlways<EntityT extends EntityLiving> implements ISelectionPredicate<EntityT> { @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { return true; } } public static class AllOfAdapter<EntityT extends EntityLiving> implements ISelectionPredicate<EntityT> { private ISelectionPredicate<EntityT>[] group; public AllOfAdapter(ISelectionPredicate<EntityT>[] toFulfil) { group = Objects.requireNonNull(toFulfil); } @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { boolean all = true; for (ISelectionPredicate<EntityT> pred : group) { all &= pred.shouldSelectAttack(attack, actor, target); } return all; } } public static class SpecificLastActionAdapter<EntityT extends EntityLiving & IActionRecorder<? super EntityT>> implements ISelectionPredicate<EntityT> { private IExecutableAction<? super EntityT> actionToTrack; public SpecificLastActionAdapter(IExecutableAction<? super EntityT> actionToTrack) { this.actionToTrack = actionToTrack; } @Override public boolean shouldSelectAttack(IExecutableAction<? super EntityT> attack, EntityT actor, Entity target) { return actor.getLastAction() == actionToTrack; } } }