package com.asteria.game.character.combat;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import com.asteria.game.NodeType;
import com.asteria.game.character.CharacterNode;
import com.asteria.game.character.player.Player;
import com.asteria.utility.Stopwatch;
/**
* A cache of players who have inflicted damage on another player in a combat
* session.
*
* @author lare96 <http://github.com/lare96>
*/
public final class CombatDamage {
/**
* The map of players who have inflicted damage.
*/
private final Map<Player, DamageCounter> attackers = new HashMap<>();
/**
* Registers damage in the backing collection for {@code character}. This
* method has no effect if the character isn't a {@code PLAYER} or if
* {@code amount} is below {@code 0}.
*
* @param character
* the character to register damage for.
* @param amount
* the amount of damage to register.
*/
public void add(CharacterNode character, int amount) {
if (character.getType() == NodeType.PLAYER && amount > 0) {
Player player = (Player) character;
DamageCounter counter = attackers.putIfAbsent(player, new DamageCounter(amount));
if (counter != null)
counter.incrementAmount(amount);
}
}
/**
* Determines which player in the backing collection has inflicted the most
* damage.
*
* @return the player who has inflicted the most damage, or an empty
* optional if there are no entries.
*/
public Optional<Player> calculateKiller() {
int amount = 0;
Player killer = null;
for (Entry<Player, DamageCounter> entry : attackers.entrySet()) {
DamageCounter counter = entry.getValue();
Player player = entry.getKey();
if (player.isDead() || !player.isRegistered() || counter.isTimeout() || !player.getPosition().withinDistance(
player.getPosition(), 25))
continue;
if (counter.getAmount() > amount) {
amount = counter.getAmount();
killer = player;
}
}
return Optional.ofNullable(killer);
}
/**
* Clears all data from the backing collection.
*/
public void clear() {
attackers.clear();
}
/**
* A counter that will track the amount of damage dealt and whether that
* damaged has timed out or not.
*
* @author lare96 <http://github.com/lare96>
*/
private static final class DamageCounter {
/**
* The amount of damage within this counter.
*/
private int amount;
/**
* The stopwatch that will determine when a timeout occurs.
*/
private final Stopwatch stopwatch = new Stopwatch().reset();
/**
* Creates a new {@link DamageCounter}.
*
* @param amount
* the amount of damage within this counter.
*/
public DamageCounter(int amount) {
this.amount = amount;
}
/**
* Gets the amount of damage within this counter.
*
* @return the amount of damage.
*/
public int getAmount() {
return amount;
}
/**
* Increments the amount of damage within this counter.
*
* @param amount
* the amount to increment by.
*/
public void incrementAmount(int amount) {
if (this.isTimeout()) {
this.amount = 0;
}
this.amount += amount;
this.stopwatch.reset();
}
/**
* Determines if this counter has timed out or not.
*
* @return {@code true} if this counter has timed out, {@code false}
* otherwise.
*/
public boolean isTimeout() {
return stopwatch.elapsed(CombatConstants.DAMAGE_CACHE_TIMEOUT, TimeUnit.SECONDS);
}
}
}