package it.paspiz85.nanobot.logic;
import it.paspiz85.nanobot.attack.Attack;
import it.paspiz85.nanobot.exception.BotBadBaseException;
import it.paspiz85.nanobot.exception.BotException;
import it.paspiz85.nanobot.game.AttackScreen;
import it.paspiz85.nanobot.game.EnemyInfo;
import it.paspiz85.nanobot.game.Screen;
import it.paspiz85.nanobot.game.TroopsInfo;
import it.paspiz85.nanobot.util.Settings;
import it.paspiz85.nanobot.util.Utils;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
/**
* Attack state is when bot find and attack an opponent.
*
* @author paspiz85
*
*/
public final class StateAttack extends State<AttackScreen> {
public static StateAttack instance() {
return Utils.singleton(StateAttack.class, () -> new StateAttack());
}
private EnemyInfo prevLoot;
private StateAttack() {
super(Screen.getInstance(AttackScreen.class));
}
public boolean doConditionsMatch(final EnemyInfo loot) {
int gold = loot.getGold() == null ? 0 : loot.getGold();
int elixir = loot.getElixir() == null ? 0 : loot.getElixir();
int de = loot.getDarkElixir() == null ? 0 : loot.getDarkElixir();
// if threshold is 0 or not set, do not match based on them
final int goldThreshold = Settings.instance().getGoldThreshold();
final int elixirThreshold = Settings.instance().getElixirThreshold();
final int darkElixirThreshold = Settings.instance().getDarkElixirThreshold();
boolean result = false;
if (Settings.instance().isMatchAllConditions()) {
gold = goldThreshold == 0 ? Integer.MAX_VALUE : gold;
elixir = elixirThreshold == 0 ? Integer.MAX_VALUE : elixir;
de = darkElixirThreshold == 0 ? Integer.MAX_VALUE : de;
result = gold >= goldThreshold && elixir >= elixirThreshold && de >= darkElixirThreshold;
} else {
gold = goldThreshold == 0 ? Integer.MIN_VALUE : gold;
elixir = elixirThreshold == 0 ? Integer.MIN_VALUE : elixir;
de = darkElixirThreshold == 0 ? Integer.MIN_VALUE : de;
result = gold >= goldThreshold || elixir >= elixirThreshold || de >= darkElixirThreshold;
}
return result;
}
@Override
public void handle(final Context context) throws InterruptedException, BotException {
State<?> nextState = StateIdle.instance();
while (true) {
if (Thread.interrupted()) {
throw new InterruptedException(getClass().getSimpleName() + " is interrupted");
}
final long id = System.currentTimeMillis();
logger.log(Level.INFO, "Found opponent " + id);
// to avoid fog
platform.sleepRandom(500);
EnemyInfo enemyInfo;
boolean doAttack = false;
try {
enemyInfo = getScreen().parseEnemyInfo();
logger.log(Level.INFO, String.format("Detected %s", enemyInfo.toString()));
doAttack = doConditionsMatch(enemyInfo);
if (doAttack && Settings.instance().isDetectEmptyCollectors()) {
final Boolean isCollectorFullBase = getScreen().isCollectorFullBase();
doAttack = isCollectorFullBase == null ? false : isCollectorFullBase;
if (!doAttack) {
logger.log(Level.INFO, "Detected empty collectors");
}
}
} catch (final BotBadBaseException e) {
platform.saveScreenshot("bad_base_" + id);
throw e;
}
if (!doAttack) {
platform.leftClick(getScreen().getButtonNext(), true);
platform.sleepRandom(666);
try {
sleepUntil(() -> getScreen().isDisplayed());
} catch (final TimeoutException e) {
logger.log(Level.WARNING, "Next button not found");
break;
}
// to avoid server/client sync from nexting too fast
platform.sleepRandom(1000);
} else {
final String attackStrategy = Settings.instance().getAttackStrategy();
if (Attack.manualStrategy().equals(attackStrategy)) {
if (enemyInfo.equals(prevLoot)) {
logger.log(Level.INFO, "User is manually attacking/deciding");
}
prevLoot = enemyInfo;
Thread.sleep(5000);
} else {
final TroopsInfo troopsInfo = context.getTroopsInfo();
if (troopsInfo != null) {
logger.log(Level.INFO, "Attacking with " + troopsInfo);
Attack.getByName(attackStrategy).attack(enemyInfo, troopsInfo);
}
platform.leftClick(getScreen().getButtonEndBattle(), true);
platform.sleepRandom(1200);
platform.leftClick(getScreen().getButtonEndBattleQuestionOK(), true);
platform.sleepRandom(1200);
nextState = StateIdle.instance();
}
break;
}
}
context.setState(nextState);
}
}