diff --git a/core/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java b/core/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java index d4916f4543..9860295e40 100644 --- a/core/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java +++ b/core/src/main/java/tc/oc/pgm/blitz/BlitzMatchModule.java @@ -76,6 +76,16 @@ public int getNumOfLives(UUID id) { return lifeManager.getLives(id); } + public void setLives(MatchPlayer matchPlayer, int lives) { + UUID id = matchPlayer.getId(); + if (lives == lifeManager.getLives(id)) return; + + lifeManager.setLives(id, lives); + if (this.config.getBroadcastLives()) { + this.showLivesTitle(matchPlayer); + } + } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void handleDeath(final MatchPlayerDeathEvent event) { MatchPlayer victim = event.getVictim(); @@ -123,23 +133,24 @@ public void handleJoin(final PlayerParticipationStartEvent event) { @EventHandler public void handleSpawn(final ParticipantSpawnEvent event) { if (this.config.getBroadcastLives()) { - int lives = this.lifeManager.getLives(event.getPlayer().getId()); - event - .getPlayer() - .showTitle( - title( - empty(), - translatable( - "blitz.livesRemaining", - NamedTextColor.RED, - translatable( - lives == 1 ? "misc.life" : "misc.lives", - NamedTextColor.AQUA, - text(lives))), - Title.Times.times(Duration.ZERO, fromTicks(60), fromTicks(20)))); + MatchPlayer matchPlayer = event.getPlayer(); + showLivesTitle(matchPlayer); } } + public void showLivesTitle(MatchPlayer matchPlayer) { + int lives = this.lifeManager.getLives(matchPlayer.getId()); + matchPlayer.showTitle( + title( + empty(), + translatable( + "blitz.livesRemaining", + NamedTextColor.RED, + translatable( + lives == 1 ? "misc.life" : "misc.lives", NamedTextColor.AQUA, text(lives))), + Title.Times.times(Duration.ZERO, fromTicks(60), fromTicks(20)))); + } + @EventHandler(priority = EventPriority.MONITOR) public void onBlitzPlayerEliminated(final BlitzPlayerEliminatedEvent event) { this.eliminatedPlayers.add(event.getPlayer().getId()); diff --git a/core/src/main/java/tc/oc/pgm/blitz/LifeManager.java b/core/src/main/java/tc/oc/pgm/blitz/LifeManager.java index c255bcf5f2..0878e83749 100644 --- a/core/src/main/java/tc/oc/pgm/blitz/LifeManager.java +++ b/core/src/main/java/tc/oc/pgm/blitz/LifeManager.java @@ -41,4 +41,10 @@ public int addLives(UUID player, int dlives) { return lives; } + + public void setLives(UUID player, int lives) { + assertNotNull(player, "player id"); + + this.livesLeft.put(player, Math.max(0, lives)); + } } diff --git a/core/src/main/java/tc/oc/pgm/variables/BlitzVariable.java b/core/src/main/java/tc/oc/pgm/variables/BlitzVariable.java new file mode 100644 index 0000000000..0f91a375a1 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/variables/BlitzVariable.java @@ -0,0 +1,38 @@ +package tc.oc.pgm.variables; + +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.blitz.BlitzMatchModule; +import tc.oc.pgm.filters.Filterable; + +public class BlitzVariable implements Variable { + + private final VariableDefinition definition; + + public BlitzVariable(VariableDefinition definition) { + this.definition = definition; + } + + @Override + public VariableDefinition getDefinition() { + return definition; + } + + @Override + public double getValue(Filterable context) { + MatchPlayer matchPlayer = (MatchPlayer) context; + BlitzMatchModule blitzMatchModule = matchPlayer.getMatch().needModule(BlitzMatchModule.class); + return blitzMatchModule.getNumOfLives(matchPlayer.getId()); + } + + @Override + public void setValue(Filterable context, double value) { + MatchPlayer matchPlayer = (MatchPlayer) context; + BlitzMatchModule blitzMatchModule = matchPlayer.getMatch().needModule(BlitzMatchModule.class); + blitzMatchModule.setLives(matchPlayer, Math.max((int) value, 0)); + } + + @Override + public VariableType getType() { + return VariableType.LIVES; + } +} diff --git a/core/src/main/java/tc/oc/pgm/variables/DummyVariable.java b/core/src/main/java/tc/oc/pgm/variables/DummyVariable.java new file mode 100644 index 0000000000..a8402b57cf --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/variables/DummyVariable.java @@ -0,0 +1,53 @@ +package tc.oc.pgm.variables; + +import java.util.HashMap; +import java.util.Map; +import tc.oc.pgm.filters.FilterMatchModule; +import tc.oc.pgm.filters.Filterable; + +public class DummyVariable> implements Variable { + + private final VariableDefinition definition; + private final Map values; + + public DummyVariable(VariableDefinition definition) { + this.definition = definition; + this.values = new HashMap<>(); + } + + @Override + public VariableDefinition getDefinition() { + return definition; + } + + @Override + public double getValue(Filterable context) { + return values.computeIfAbsent(getAncestor(context), k -> definition.getDefault()); + } + + @Override + public void setValue(Filterable context, double value) { + T ctx = getAncestor(context); + values.put(ctx, value); + // For performance reasons, let's avoid launching an event for every variable change + context.getMatch().needModule(FilterMatchModule.class).invalidate(ctx); + } + + @Override + public VariableType getType() { + return VariableType.DUMMY; + } + + private T getAncestor(Filterable context) { + T filterable = context.getFilterableAncestor(definition.getScope()); + if (filterable != null) return filterable; + + throw new IllegalStateException( + "Wrong variable scope for '" + + getId() + + "', expected " + + definition.getScope().getSimpleName() + + " which cannot be found in " + + context.getClass().getSimpleName()); + } +} diff --git a/core/src/main/java/tc/oc/pgm/variables/Variable.java b/core/src/main/java/tc/oc/pgm/variables/Variable.java index 171fa2617c..4d70e4388f 100644 --- a/core/src/main/java/tc/oc/pgm/variables/Variable.java +++ b/core/src/main/java/tc/oc/pgm/variables/Variable.java @@ -1,52 +1,18 @@ package tc.oc.pgm.variables; -import java.util.HashMap; -import java.util.Map; import tc.oc.pgm.api.feature.Feature; -import tc.oc.pgm.filters.FilterMatchModule; import tc.oc.pgm.filters.Filterable; -public class Variable> implements Feature> { - - private final VariableDefinition definition; - private final Map values; - - public Variable(VariableDefinition definition) { - this.definition = definition; - this.values = new HashMap<>(); - } - - @Override - public String getId() { - return definition.getId(); - } +public interface Variable> extends Feature> { @Override - public VariableDefinition getDefinition() { - return definition; + default String getId() { + return getDefinition().getId(); } - public double getValue(Filterable context) { - return values.computeIfAbsent(getAncestor(context), k -> definition.getDefault()); - } + double getValue(Filterable context); - public void setValue(Filterable context, double value) { - T ctx = getAncestor(context); - values.put(ctx, value); - // For performance reasons, let's avoid launching an event for every variable change - context.getMatch().needModule(FilterMatchModule.class).invalidate(ctx); - } + void setValue(Filterable context, double value); - private T getAncestor(Filterable context) { - T filterable = context.getFilterableAncestor(definition.getScope()); - if (filterable != null) return filterable; - - throw new IllegalStateException( - "Wrong variable scope for '" - + getId() - + "', expected " - + definition.getScope().getSimpleName() - + " which cannot be found in " - + context.getClass().getSimpleName()); - } + VariableType getType(); } diff --git a/core/src/main/java/tc/oc/pgm/variables/VariableDefinition.java b/core/src/main/java/tc/oc/pgm/variables/VariableDefinition.java index e0427001c7..651b49cccd 100644 --- a/core/src/main/java/tc/oc/pgm/variables/VariableDefinition.java +++ b/core/src/main/java/tc/oc/pgm/variables/VariableDefinition.java @@ -8,11 +8,13 @@ public class VariableDefinition> extends SelfIdentifying private final Class scope; private final double def; + private final VariableType variableType; - public VariableDefinition(String id, Class scope, double def) { + public VariableDefinition(String id, Class scope, double def, VariableType variableType) { super(id); this.scope = scope; this.def = def; + this.variableType = variableType; } public Class getScope() { @@ -23,6 +25,10 @@ public double getDefault() { return def; } + public VariableType getVariableType() { + return variableType; + } + @SuppressWarnings("unchecked") public Variable getVariable(Match match) { return (Variable) match.getFeatureContext().get(this.getId()); diff --git a/core/src/main/java/tc/oc/pgm/variables/VariableType.java b/core/src/main/java/tc/oc/pgm/variables/VariableType.java new file mode 100644 index 0000000000..e91fef10d1 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/variables/VariableType.java @@ -0,0 +1,47 @@ +package tc.oc.pgm.variables; + +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.filters.Filterable; +import tc.oc.pgm.util.xml.InvalidXMLException; +import tc.oc.pgm.util.xml.Node; + +public enum VariableType { + DUMMY(Filterable.class), + LIVES(MatchPlayer.class); + + private final Class[] supportedScopes; + + VariableType(Class... supportedScopes) { + this.supportedScopes = supportedScopes; + } + + public boolean supports(Class cls) { + for (Class supportedScope : supportedScopes) { + if (supportedScope.isAssignableFrom(cls)) { + return true; + } + } + return false; + } + + static final Map stringTypeMap = new ConcurrentHashMap<>(); + + static { + for (VariableType value : VariableType.values()) { + stringTypeMap.put(value.name().toLowerCase(Locale.ROOT), value); + } + } + + static VariableType parse(Node variableTypeNode, VariableType defaultType) + throws InvalidXMLException { + if (variableTypeNode == null) return defaultType; + VariableType variableType = stringTypeMap.get(variableTypeNode.getValue()); + if (variableType != null) return variableType; + + throw new InvalidXMLException( + "Unknown Variable Type: " + variableTypeNode.getValue(), variableTypeNode); + } +} diff --git a/core/src/main/java/tc/oc/pgm/variables/VariablesModule.java b/core/src/main/java/tc/oc/pgm/variables/VariablesModule.java index 1ada5aaba8..ec3cf26d5a 100644 --- a/core/src/main/java/tc/oc/pgm/variables/VariablesModule.java +++ b/core/src/main/java/tc/oc/pgm/variables/VariablesModule.java @@ -17,6 +17,7 @@ import tc.oc.pgm.api.map.factory.MapModuleFactory; import tc.oc.pgm.api.match.Match; import tc.oc.pgm.api.module.exception.ModuleLoadException; +import tc.oc.pgm.api.player.MatchPlayer; import tc.oc.pgm.filters.Filterable; import tc.oc.pgm.util.xml.InvalidXMLException; import tc.oc.pgm.util.xml.Node; @@ -74,7 +75,16 @@ public static > VariableCache of( @Override public VariablesMatchModule createMatchModule(Match match) throws ModuleLoadException { for (VariableDefinition varDef : this.variables) { - match.getFeatureContext().add(new Variable<>(varDef)); + switch (varDef.getVariableType()) { + case DUMMY: + match.getFeatureContext().add(new DummyVariable<>(varDef)); + break; + case LIVES: + match + .getFeatureContext() + .add(new BlitzVariable((VariableDefinition) varDef)); + break; + } } return new VariablesMatchModule(); @@ -102,8 +112,15 @@ public VariablesModule parse(MapFactory factory, Logger logger, Document doc) Class> scope = Filterables.parse(Node.fromRequiredAttr(variable, "scope")); double def = XMLUtils.parseNumber(Node.fromAttr(variable, "default"), Double.class, 0d); + Node variableTypeNode = Node.fromAttr(variable, "type"); + VariableType variableType = VariableType.parse(variableTypeNode, VariableType.DUMMY); + if (!variableType.supports(scope)) { + throw new InvalidXMLException( + "VariableType: " + variableType + " Does not support scope: " + scope, + variableTypeNode); + } - VariableDefinition varDef = new VariableDefinition<>(id, scope, def); + VariableDefinition varDef = new VariableDefinition<>(id, scope, def, variableType); factory.getFeatures().addFeature(variable, varDef); variables.add(varDef); }