Skip to content

Commit

Permalink
Add decay and recovery times for control points
Browse files Browse the repository at this point in the history
Signed-off-by: mrcookie <[email protected]>
  • Loading branch information
mrcookieunderscore13 authored May 19, 2021
1 parent 5bfbfb4 commit 88f1ddb
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 64 deletions.
123 changes: 74 additions & 49 deletions core/src/main/java/tc/oc/pgm/controlpoint/ControlPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,37 +403,39 @@ private void dominateAndFireEvents(@Nullable Competitor dominantTeam, Duration d
*
* <p>If there is no neutral state, then the point is always either being captured by a specific
* team, or not being captured at all.
*
* <p>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)) {
return;
}

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
Expand All @@ -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()) {
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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="
Expand Down Expand Up @@ -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;
}
Expand All @@ -205,10 +230,6 @@ public CaptureCondition getCaptureCondition() {
return this.captureCondition;
}

public boolean isIncrementalCapture() {
return this.incrementalCapture;
}

public boolean hasNeutralState() {
return this.neutralState;
}
Expand Down
33 changes: 30 additions & 3 deletions core/src/main/java/tc/oc/pgm/controlpoint/ControlPointParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 88f1ddb

Please sign in to comment.