From 88f1ddb03dd25726fe1a5ddd6cad98c875700ca2 Mon Sep 17 00:00:00 2001 From: mrcookieunderscore13 Date: Tue, 18 May 2021 20:31:05 -0500 Subject: [PATCH] Add decay and recovery times for control points Signed-off-by: mrcookie --- .../tc/oc/pgm/controlpoint/ControlPoint.java | 123 +++++++++++------- .../controlpoint/ControlPointDefinition.java | 45 +++++-- .../pgm/controlpoint/ControlPointParser.java | 33 ++++- 3 files changed, 137 insertions(+), 64 deletions(-) diff --git a/core/src/main/java/tc/oc/pgm/controlpoint/ControlPoint.java b/core/src/main/java/tc/oc/pgm/controlpoint/ControlPoint.java index f5683474ae..2ca5d7cb63 100644 --- a/core/src/main/java/tc/oc/pgm/controlpoint/ControlPoint.java +++ b/core/src/main/java/tc/oc/pgm/controlpoint/ControlPoint.java @@ -403,9 +403,6 @@ private void dominateAndFireEvents(@Nullable Competitor dominantTeam, Duration d * *

If there is no neutral state, then the point is always either being captured by a specific * team, or not being captured at all. - * - *

If incremental capturing is disabled, then capturingTimeMillis is reset to zero whenever it - * stops increasing. */ private void dominate(Competitor dominantTeam, Duration dominantTime) { if (!this.capturable || !TimeUtils.isLongerThan(dominantTime, Duration.ZERO)) { @@ -413,27 +410,32 @@ private void dominate(Competitor dominantTeam, Duration dominantTime) { } ControlPointDefinition definition = this.getDefinition(); - if (this.controllingTeam != null && definition.hasNeutralState()) { // Point is owned and must go through the neutral state before another team can capture it if (dominantTeam == this.controllingTeam) { - this.regressCapture(dominantTeam, dominantTime); + // owner is recovering the point + recover(dominantTeam, dominantTime); } else if (dominantTeam != null) { - this.progressUncapture(dominantTeam, dominantTime); - } else if (!definition.isIncrementalCapture()) { - // No team is dominant and point is not incremental, so reset the time - this.capturingTime = Duration.ZERO; + // non-owner is uncapturing the point + uncapture(dominantTeam, dominantTime); + } else if (definition.getOwnedDecayRate() > 0) { + // nobody on point so decay to neutral state + ownedDecay(dominantTime); + } else { + // nobody on point, so "decay" to fully captured + decay(dominantTime); } } else if (this.capturingTeam != null) { // Point is being captured by a specific team if (dominantTeam == this.capturingTeam) { - this.progressCapture(dominantTeam, dominantTime); + // capturing team is making progress + capture(dominantTime); } else if (dominantTeam != null) { - this.regressCapture(dominantTeam, dominantTime); - } else if (!definition.isIncrementalCapture()) { - // No team is dominant and point is not incremental, so reset time and clear capturing team - this.capturingTime = Duration.ZERO; - this.capturingTeam = null; + // non-capturing team is dominate, so regress capturing team's progress + recover(dominantTeam, dominantTime); + } else { + // No team is dominating so decay + decay(dominantTime); } } else if (dominantTeam != null && dominantTeam != this.controllingTeam @@ -445,24 +447,30 @@ private void dominate(Competitor dominantTeam, Duration dominantTime) { } } - /** Progress toward the neutral state */ - private void progressUncapture(Competitor dominantTeam, Duration dominantTime) { - this.capturingTime = this.capturingTime.plus(dominantTime); - - if (!TimeUtils.isShorterThan(this.capturingTime, this.definition.getTimeToCapture())) { - // If uncapture is complete, recurse with the dominant team's remaining time - dominantTime = this.capturingTime.minus(this.definition.getTimeToCapture()); + private @Nullable Duration addCaptureTime(final Duration duration) { + this.capturingTime = this.capturingTime.plus(duration); + if (!TimeUtils.isLongerThan(definition.getTimeToCapture(), this.capturingTime)) { + final Duration remainder = this.capturingTime.minus(definition.getTimeToCapture()); this.capturingTime = Duration.ZERO; - this.controllingTeam = null; - this.dominate(dominantTeam, dominantTime); + return remainder; } + return null; } - /** Progress toward a new controller */ - private void progressCapture(Competitor dominantTeam, Duration dominantTime) { - this.capturingTime = this.capturingTime.plus(dominantTime); - if (!TimeUtils.isShorterThan(this.capturingTime, this.definition.getTimeToCapture())) { - this.capturingTime = Duration.ZERO; + private @Nullable Duration subtractCaptureTime(final Duration duration) { + if (TimeUtils.isLongerThan(this.capturingTime, duration)) { + this.capturingTime = this.capturingTime.minus(duration); + return null; + } else { + final Duration remainder = duration.minus(this.capturingTime); + this.capturingTime = duration.ZERO; + return remainder; + } + } + // Progress to a new owner + private void capture(Duration dominantTime) { + dominantTime = addCaptureTime(dominantTime); + if (dominantTime != null) { // Point is captured this.controllingTeam = this.capturingTeam; this.capturingTeam = null; if (this.getDefinition().isPermanent()) { @@ -471,31 +479,48 @@ private void progressCapture(Competitor dominantTeam, Duration dominantTime) { } } } - - /** Regress toward the current state */ - private void regressCapture(Competitor dominantTeam, Duration dominantTime) { - boolean crossZero = false; - if (definition.isIncrementalCapture()) { - // For incremental points, decrease the capture time - if (TimeUtils.isLongerThan(this.capturingTime, dominantTime)) { - this.capturingTime = this.capturingTime.minus(dominantTime); - } else { - dominantTime = dominantTime.minus(this.capturingTime); - this.capturingTime = Duration.ZERO; - crossZero = true; - } - } else { - // For non-incremental points, reset capture time to zero - this.capturingTime = Duration.ZERO; - crossZero = true; + // Progress towards the neutral state + private void uncapture(Competitor dominantTeam, Duration dominantTime) { + dominantTime = addCaptureTime(dominantTime); + if (dominantTime != null) { + this.controllingTeam = null; + this.dominate(dominantTeam, dominantTime); } - - if (crossZero) { + } + // Point being pulled back to current state (There is a lead on the point) + private void recover(Competitor dominantTeam, Duration dominantTime) { + dominantTime = + subtractCaptureTime( + Duration.ofMillis((long) (definition.getRecoveryRate() * dominantTime.toMillis()))); + if (dominantTeam != null) { this.capturingTeam = null; if (dominantTeam != this.controllingTeam) { // If the dominant team is not the controller, recurse with the remaining time - this.dominate(dominantTeam, dominantTime); + this.dominate( + dominantTeam, + Duration.ofMillis( + (long) ((1.0 / definition.getRecoveryRate()) * dominantTime.toMillis()))); } } } + // Point is being decayed back to its current state (No lead on point) + private void decay(Duration dominantTime) { + dominantTime = + subtractCaptureTime( + Duration.ofMillis((long) (definition.getDecayRate() * dominantTime.toMillis()))); + if (dominantTime != null) { + this.capturingTeam = null; + } + } + + // Point is being decayed back to neutral (No lead on point) + private void ownedDecay(Duration dominantTime) { + dominantTime = + addCaptureTime( + Duration.ofMillis((long) (definition.getOwnedDecayRate() * dominantTime.toMillis()))); + if (dominantTime != null) { + this.controllingTeam = null; + this.capturingTeam = null; + } + } } diff --git a/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointDefinition.java b/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointDefinition.java index 5481701673..3b09855a8c 100644 --- a/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointDefinition.java +++ b/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointDefinition.java @@ -38,6 +38,15 @@ public class ControlPointDefinition extends GoalDefinition { // Base time for the point to transition between states private final Duration timeToCapture; + // Time it takes for a point to decay while unowned. (Time is accurate when near 100% capture) + private final double decayRate; + + // Time it takes for a point to recover to captured state. (Accurate when almost uncaptured) + private final double recoveryRate; + + // Time it takes for a point to transition to neutral state. + private final double ownedDecayRate; + // Capture time multiplier for increasing or decreasing capture time based on the number of // players on the point private final float timeMultiplier; @@ -54,10 +63,6 @@ public enum CaptureCondition { private final CaptureCondition captureCondition; - // true: progress is retained if capturing is interrupted - // false: progress resets to zero if capturing is interrupted - private final boolean incrementalCapture; - // true: point must transition through unowned state to change owners // false: point transitions directly from one owner to the next // NOTE: points always start in an unowned state, regardless of this value @@ -92,10 +97,12 @@ public ControlPointDefinition( Filter visualMaterials, BlockVector capturableDisplayBeacon, Duration timeToCapture, + double decayRate, + double recoveryRate, + double ownedDecayRate, float timeMultiplier, @Nullable TeamFactory initialOwner, CaptureCondition captureCondition, - boolean incrementalCapture, boolean neutralState, boolean permanent, float pointsPerSecond, @@ -112,10 +119,12 @@ public ControlPointDefinition( this.visualMaterials = visualMaterials; this.capturableDisplayBeacon = capturableDisplayBeacon; this.timeToCapture = timeToCapture; + this.decayRate = decayRate; + this.recoveryRate = recoveryRate; + this.ownedDecayRate = ownedDecayRate; this.timeMultiplier = timeMultiplier; this.initialOwner = initialOwner; this.captureCondition = captureCondition; - this.incrementalCapture = incrementalCapture; this.neutralState = neutralState; this.permanent = permanent; this.pointsPerSecond = pointsPerSecond; @@ -132,14 +141,18 @@ public String toString() { + this.getId() + " timeToCapture=" + this.getTimeToCapture() + + " decayRate=" + + this.getDecayRate() + + " recoveryRate=" + + this.getRecoveryRate() + + " ownedDecayRate=" + + this.getOwnedDecayRate() + " timeMultiplier=" + this.getTimeMultiplier() + " initialOwner=" + this.getInitialOwner() + " captureCondition=" + this.getCaptureCondition() - + " incrementalCapture=" - + this.isIncrementalCapture() + " neutralState=" + this.hasNeutralState() + " permanent=" @@ -192,6 +205,18 @@ public Duration getTimeToCapture() { return this.timeToCapture; } + public double getDecayRate() { + return this.decayRate; + } + + public double getRecoveryRate() { + return this.recoveryRate; + } + + public double getOwnedDecayRate() { + return this.ownedDecayRate; + } + public float getTimeMultiplier() { return this.timeMultiplier; } @@ -205,10 +230,6 @@ public CaptureCondition getCaptureCondition() { return this.captureCondition; } - public boolean isIncrementalCapture() { - return this.incrementalCapture; - } - public boolean hasNeutralState() { return this.neutralState; } diff --git a/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointParser.java b/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointParser.java index dce99cc1bc..cd9d023c5b 100644 --- a/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointParser.java +++ b/core/src/main/java/tc/oc/pgm/controlpoint/ControlPointParser.java @@ -77,13 +77,38 @@ public static ControlPointDefinition parseControlPoint( Duration timeToCapture = XMLUtils.parseDuration(elControlPoint.getAttribute("capture-time"), Duration.ofSeconds(30)); + final double decayRate, recoveryRate, ownedDecayRate; + final Node attrIncremental = Node.fromAttr(elControlPoint, "incremental"); + final Node attrDecay = Node.fromAttr(elControlPoint, "decay-rate"); + final Node attrRecovery = Node.fromAttr(elControlPoint, "recovery-rate"); + final Node attrOwnedDecay = Node.fromAttr(elControlPoint, "owned-decay-rate"); + if (attrIncremental == null) { + recoveryRate = + XMLUtils.parseNumber(attrRecovery, Double.class, koth ? 1D : Double.POSITIVE_INFINITY); + decayRate = + XMLUtils.parseNumber(attrDecay, Double.class, koth ? 0.0 : Double.POSITIVE_INFINITY); + ownedDecayRate = XMLUtils.parseNumber(attrOwnedDecay, Double.class, 0.0); + } else { + if (attrDecay != null || attrRecovery != null || attrOwnedDecay != null) + throw new InvalidXMLException( + "Cannot combine this attribute with incremental", + attrDecay != null ? attrDecay : attrRecovery != null ? attrRecovery : attrOwnedDecay); + + final boolean incremental = XMLUtils.parseBoolean(attrIncremental, koth); + recoveryRate = incremental ? 1.0 : Double.POSITIVE_INFINITY; + decayRate = incremental ? 0.0 : Double.POSITIVE_INFINITY; + ownedDecayRate = 0.0; + } + float timeMultiplier = XMLUtils.parseNumber( elControlPoint.getAttribute("time-multiplier"), Float.class, koth ? 0.1f : 0f); - boolean incrementalCapture = - XMLUtils.parseBoolean(elControlPoint.getAttribute("incremental"), koth); boolean neutralState = XMLUtils.parseBoolean(elControlPoint.getAttribute("neutral-state"), koth); + + if (neutralState == false && ownedDecayRate > 0) { + throw new InvalidXMLException("This attribute requires a neutral state.", attrOwnedDecay); + } boolean permanent = XMLUtils.parseBoolean(elControlPoint.getAttribute("permanent"), false); float pointsPerSecond = XMLUtils.parseNumber(elControlPoint.getAttribute("points"), Float.class, 1f); @@ -117,10 +142,12 @@ public static ControlPointDefinition parseControlPoint( visualMaterials, capturableDisplayBeacon == null ? null : capturableDisplayBeacon.toBlockVector(), timeToCapture, + decayRate, + recoveryRate, + ownedDecayRate, timeMultiplier, initialOwner, captureCondition, - incrementalCapture, neutralState, permanent, pointsPerSecond,