145 lines
6.1 KiB
Java
145 lines
6.1 KiB
Java
package tc.oc.pgm.score;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.logging.Logger;
|
|
import javax.annotation.Nonnull;
|
|
|
|
import com.google.common.base.Preconditions;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.collect.Range;
|
|
import net.md_5.bungee.api.chat.BaseComponent;
|
|
import net.md_5.bungee.api.chat.TranslatableComponent;
|
|
import org.jdom2.Document;
|
|
import org.jdom2.Element;
|
|
import tc.oc.api.docs.virtual.MapDoc;
|
|
import tc.oc.commons.core.util.Optionals;
|
|
import tc.oc.pgm.filters.Filter;
|
|
import tc.oc.pgm.filters.matcher.StaticFilter;
|
|
import tc.oc.pgm.filters.parser.FilterParser;
|
|
import tc.oc.pgm.goals.GoalModule;
|
|
import tc.oc.pgm.blitz.BlitzModule;
|
|
import tc.oc.pgm.map.MapModule;
|
|
import tc.oc.pgm.map.MapModuleContext;
|
|
import tc.oc.pgm.map.ProtoVersions;
|
|
import tc.oc.pgm.match.Match;
|
|
import tc.oc.pgm.match.MatchModuleFactory;
|
|
import tc.oc.pgm.module.ModuleDescription;
|
|
import tc.oc.pgm.regions.Region;
|
|
import tc.oc.pgm.regions.RegionParser;
|
|
import tc.oc.pgm.utils.MaterialPattern;
|
|
import tc.oc.pgm.utils.XMLUtils;
|
|
import tc.oc.pgm.xml.InvalidXMLException;
|
|
import tc.oc.pgm.xml.Node;
|
|
|
|
@ModuleDescription(name = "Score", follows = { GoalModule.class, BlitzModule.class })
|
|
public class ScoreModule implements MapModule, MatchModuleFactory<ScoreMatchModule> {
|
|
|
|
private final ScoreConfig config;
|
|
|
|
public ScoreModule(@Nonnull ScoreConfig config) {
|
|
Preconditions.checkNotNull(config, "score config");
|
|
this.config = config;
|
|
}
|
|
|
|
private static final BaseComponent GAME = new TranslatableComponent("match.scoreboard.scores.title");
|
|
@Override
|
|
public BaseComponent getGameName(MapModuleContext context) {
|
|
return GAME;
|
|
}
|
|
|
|
@Override
|
|
public Set<MapDoc.Gamemode> getGamemodes(MapModuleContext context) {
|
|
ImmutableSet.Builder<MapDoc.Gamemode> gamemodes = ImmutableSet.builder();
|
|
if(getConfig().killScore > 0) {
|
|
gamemodes.add(MapDoc.Gamemode.tdm);
|
|
}
|
|
if(context.features().containsAny(ScoreBoxFactory.class)) {
|
|
gamemodes.add(MapDoc.Gamemode.scorebox);
|
|
}
|
|
return gamemodes.build();
|
|
}
|
|
|
|
@Override
|
|
public ScoreMatchModule createMatchModule(Match match) {
|
|
return new ScoreMatchModule(match, config);
|
|
}
|
|
|
|
@Nonnull
|
|
public ScoreConfig getConfig() {
|
|
return config;
|
|
}
|
|
|
|
// ---------------------
|
|
// ---- XML Parsing ----
|
|
// ---------------------
|
|
|
|
public static ScoreModule parse(MapModuleContext context, Logger logger, Document doc) throws InvalidXMLException {
|
|
final boolean defaultScoresToZero = context.getProto().isNoOlderThan(ProtoVersions.DEFAULT_SCORES_TO_ZERO);
|
|
final int defaultBoxScore = defaultScoresToZero ? 0 : 1;
|
|
|
|
final FilterParser filterParser = context.needModule(FilterParser.class);
|
|
final RegionParser regionParser = context.needModule(RegionParser.class);
|
|
|
|
final List<Element> scoreElements = doc.getRootElement().getChildren("score");
|
|
if(scoreElements.isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
Optional<Integer> scoreLimit = Optional.empty();
|
|
Optional<Integer> deathScore = Optional.empty();
|
|
Optional<Integer> killScore = Optional.empty();
|
|
boolean kingOfTheHill = false;
|
|
|
|
for(Element scoreEl : scoreElements) {
|
|
if(scoreEl.getChild("king") != null) {
|
|
kingOfTheHill = true;
|
|
}
|
|
|
|
scoreLimit = Optionals.first(XMLUtils.parseNumber(scoreEl, "limit", Integer.class).range(Range.atLeast(1)).optional(), scoreLimit);
|
|
deathScore = Optionals.first(XMLUtils.parseNumber(scoreEl, "deaths", Integer.class).optional(), deathScore);
|
|
killScore = Optionals.first(XMLUtils.parseNumber(scoreEl, "kills", Integer.class).optional(), killScore);
|
|
|
|
for(Element scoreBoxEl : scoreEl.getChildren("box")) {
|
|
boolean silent = XMLUtils.parseBoolean(Node.fromAttr(scoreBoxEl, "silent"), false);
|
|
int points = XMLUtils.parseNumber(Node.fromAttr(scoreBoxEl, "value", "points"),
|
|
Integer.class,
|
|
defaultBoxScore);
|
|
|
|
final Filter filter = filterParser.property(scoreBoxEl, "filter")
|
|
.optional(StaticFilter.ALLOW);
|
|
|
|
final Optional<Region> region = regionParser.property(scoreBoxEl)
|
|
.legacy()
|
|
.optionalUnion();
|
|
final Filter trigger = region.isPresent() ? region.get()
|
|
: filterParser.property(scoreBoxEl, "trigger")
|
|
.dynamic()
|
|
.required();
|
|
|
|
final Map<MaterialPattern, Double> redeemables = new HashMap<>();
|
|
final Element elItems = scoreBoxEl.getChild("redeemables");
|
|
if(elItems != null) {
|
|
for(Element elItem : elItems.getChildren("item")) {
|
|
redeemables.put(XMLUtils.parseMaterialPattern(elItem),
|
|
XMLUtils.parseNumber(Node.fromAttr(elItem, "points"), Double.class, 1D));
|
|
}
|
|
}
|
|
|
|
context.features().define(scoreBoxEl, new ScoreBoxFactoryImpl(trigger, points, filter, silent, ImmutableMap.copyOf(redeemables)));
|
|
}
|
|
}
|
|
|
|
// For backwards compatibility, default kill/death points to 1 if proto is old and <king/> tag is not present
|
|
final int defaultKillScore = defaultScoresToZero || kingOfTheHill ? 0 : 1;
|
|
|
|
return new ScoreModule(new ScoreConfig(scoreLimit,
|
|
deathScore.orElse(defaultKillScore),
|
|
killScore.orElse(defaultKillScore)));
|
|
}
|
|
}
|