package tc.oc.pgm.tnt.license;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import com.google.common.collect.ImmutableList;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import tc.oc.api.docs.User;
import tc.oc.api.docs.virtual.UserDoc.License.Kill;
import tc.oc.api.docs.virtual.UserDoc.License.Stats;
import tc.oc.api.users.UserService;
import tc.oc.commons.bukkit.event.targeted.TargetedEventHandler;
import tc.oc.pgm.events.ListenerScope;
import tc.oc.pgm.events.MatchPlayerDeathEvent;
import tc.oc.pgm.match.MatchScope;
import tc.oc.pgm.match.MatchUserFacet;
import tc.oc.pgm.match.ParticipantState;
/**
* Monitors player actions to grant or revoke TNT licenses.
*/
@ListenerScope(MatchScope.LOADED)
public class LicenseMonitorUserFacet implements MatchUserFacet, Listener {
private final static int ENEMY_KILLS_FOR_GRANT = 5; // Enemy kills required to automatically grant a license
private final static int UNIQUE_ENEMY_KILLS_FOR_GRANT = 3; // How many unique enemies must be within ENEMY_KILLS_FOR_GRANT
private final static double TEAM_KILLS_PERCENT_FOR_REVOKE = 0.5; // Percent of total kills that are team kills required to automatically revoke a license (must all be unique)
private final static int KILL_WINDOW_MIN = ENEMY_KILLS_FOR_GRANT; // Minimum window size
private final static int KILL_WINDOW_MAX = 25; // Maximum window size
private final static int KILL_WINDOW_INCREMENT_RATE = 50; // How many enemy kills for the window size to increase by one
private final User user;
private final UserService userService;
private final LicenseBroker licenseBroker;
private final LicenseConfiguration licenseConfiguration;
@Inject LicenseMonitorUserFacet(User user, UserService userService, LicenseBroker licenseBroker, LicenseConfiguration licenseConfiguration) {
this.user = user;
this.userService = userService;
this.licenseBroker = licenseBroker;
this.licenseConfiguration = licenseConfiguration;
}
private boolean canGrant() {
return licenseConfiguration.autoGrant() && !user.hasTntLicense() && user.requestedTntLicense();
}
private boolean canRevoke() {
return licenseConfiguration.autoRevoke() && user.hasTntLicense();
}
private boolean isActive() {
return canGrant() || canRevoke();
}
private int windowSize() {
return Math.max(KILL_WINDOW_MIN + (user.enemy_kills() / KILL_WINDOW_INCREMENT_RATE), KILL_WINDOW_MAX);
}
@TargetedEventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onKill(MatchPlayerDeathEvent event) {
if(!isActive()) return;
if(event.isSelfKill()) return;
final ParticipantState killer = event.getKiller();
if(killer == null) return;
if(!killer.getPlayerId().equals(user)) return;
final List<Kill> kills = ImmutableList.<Kill>builder()
.addAll(user.tnt_license_kills()
.subList(Math.max(0, user.tnt_license_kills().size() - windowSize()),
user.tnt_license_kills().size()))
.add(new Kill() {
@Override public String victim_id() {
return event.getVictim().getPlayerId()._id();
}
@Override public boolean friendly() {
return event.isTeamKill();
}
})
.build();
final Set<String> uniqueEnemies = new HashSet<>();
int enemies = 0;
int teammates = 0;
for(Kill kill : kills) {
if(kill.friendly()) {
teammates++;
} else {
uniqueEnemies.add(kill.victim_id());
enemies++;
}
}
if(canRevoke() && ((double) teammates) / windowSize() >= TEAM_KILLS_PERCENT_FOR_REVOKE) {
licenseBroker.revoke(user, LicenseBroker.RevokeReason.TEAM_KILLS, true);
} else if(canGrant() && teammates == 0 && enemies >= ENEMY_KILLS_FOR_GRANT && uniqueEnemies.size() >= UNIQUE_ENEMY_KILLS_FOR_GRANT) {
licenseBroker.grant(user, LicenseBroker.GrantReason.ENEMY_KILLS);
} else {
userService.update(user, (Stats) () -> kills);
}
}
}