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/filters/parse/FeatureFilterParser.java b/core/src/main/java/tc/oc/pgm/filters/parse/FeatureFilterParser.java index 2f7dd98016..a3946bd669 100644 --- a/core/src/main/java/tc/oc/pgm/filters/parse/FeatureFilterParser.java +++ b/core/src/main/java/tc/oc/pgm/filters/parse/FeatureFilterParser.java @@ -21,6 +21,7 @@ import tc.oc.pgm.util.xml.Node; import tc.oc.pgm.util.xml.XMLUtils; import tc.oc.pgm.variables.VariableDefinition; +import tc.oc.pgm.variables.VariableType; import tc.oc.pgm.variables.VariablesModule; public class FeatureFilterParser extends FilterParser { @@ -99,6 +100,9 @@ public Filter parseNot(Element el) throws InvalidXMLException { if (varMatch.matches()) { VariableDefinition variable = features.resolve(node, varMatch.group(1), VariableDefinition.class); + if (!variable.getVariableType().equals(VariableType.DUMMY)) { + throw new InvalidXMLException("Variable filters only support dummy variables!", node); + } Range range = XMLUtils.parseNumericRange(node, varMatch.group(2), Double.class); return new VariableFilter(variable, range); } diff --git a/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java b/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java index 576f710dc8..d99cbabb4c 100644 --- a/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java +++ b/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java @@ -93,6 +93,7 @@ import tc.oc.pgm.util.xml.Node; import tc.oc.pgm.util.xml.XMLUtils; import tc.oc.pgm.variables.VariableDefinition; +import tc.oc.pgm.variables.VariableType; public abstract class FilterParser implements XMLParser { @@ -661,6 +662,9 @@ public PlayerCountFilter parsePlayerCountFilter(Element el) throws InvalidXMLExc public Filter parseVariableFilter(Element el) throws InvalidXMLException { VariableDefinition varDef = features.resolve(Node.fromRequiredAttr(el, "var"), VariableDefinition.class); + if (!varDef.getVariableType().equals(VariableType.DUMMY)) { + throw new InvalidXMLException("Variable filters only support dummy variables!", el); + } Range range = XMLUtils.parseNumericRange(new Node(el), Double.class); if (varDef.getScope() == Party.class) 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..473984691a --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/variables/BlitzVariable.java @@ -0,0 +1,33 @@ +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 = (VariableDefinition) definition; + } + + @Override + public VariableDefinition getDefinition() { + return definition; + } + + @Override + public double getValue(Filterable context) { + MatchPlayer matchPlayer = context.getFilterableAncestor(MatchPlayer.class); + BlitzMatchModule blitzMatchModule = matchPlayer.moduleRequire(BlitzMatchModule.class); + return blitzMatchModule.getNumOfLives(matchPlayer.getId()); + } + + @Override + public void setValue(Filterable context, double value) { + MatchPlayer matchPlayer = context.getFilterableAncestor(MatchPlayer.class); + BlitzMatchModule blitzMatchModule = matchPlayer.moduleRequire(BlitzMatchModule.class); + blitzMatchModule.setLives(matchPlayer, Math.max((int) value, 0)); + } +} 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..c2c05f5a8b --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/variables/DummyVariable.java @@ -0,0 +1,48 @@ +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); + } + + 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..356e146627 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,16 @@ 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<>(); - } +public interface Variable> extends Feature> { @Override - public String getId() { - return definition.getId(); + default String getId() { + return getDefinition().getId(); } - @Override - public VariableDefinition getDefinition() { - return definition; - } + double getValue(Filterable context); - public double getValue(Filterable context) { - return values.computeIfAbsent(getAncestor(context), k -> definition.getDefault()); - } - - 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); - } - - 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()); - } + void setValue(Filterable context, double value); } 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..4e4c37a9e8 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,14 @@ public double getDefault() { return def; } + public Variable buildInstance() { + return getVariableType().buildInstance(this); + } + + 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..925f90421d --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/variables/VariableType.java @@ -0,0 +1,33 @@ +package tc.oc.pgm.variables; + +import java.util.function.Function; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.filters.Filterable; + +public enum VariableType { + DUMMY(DummyVariable::new, Filterable.class), + LIVES(BlitzVariable::new, MatchPlayer.class); + + private final Function, Variable> supplierFunction; + private final Class[] supportedScopes; + + VariableType( + Function, Variable> supplierFunction, + Class... supportedScopes) { + this.supplierFunction = supplierFunction; + this.supportedScopes = supportedScopes; + } + + public boolean supports(Class cls) { + for (Class supportedScope : supportedScopes) { + if (supportedScope.isAssignableFrom(cls)) { + return true; + } + } + return false; + } + + public Variable buildInstance(VariableDefinition definition) { + return supplierFunction.apply(definition); + } +} 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..bc7015888e 100644 --- a/core/src/main/java/tc/oc/pgm/variables/VariablesModule.java +++ b/core/src/main/java/tc/oc/pgm/variables/VariablesModule.java @@ -74,7 +74,7 @@ public static > VariableCache of( @Override public VariablesMatchModule createMatchModule(Match match) throws ModuleLoadException { for (VariableDefinition varDef : this.variables) { - match.getFeatureContext().add(new Variable<>(varDef)); + match.getFeatureContext().add(varDef.buildInstance()); } return new VariablesMatchModule(); @@ -102,8 +102,17 @@ 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 = + XMLUtils.parseEnum( + variableTypeNode, VariableType.class, "variable type", 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); }