diff --git a/src/main/java/dev/espi/protectionstones/PSGroupRegion.java b/src/main/java/dev/espi/protectionstones/PSGroupRegion.java index 2d5bc3df..9c2af006 100644 --- a/src/main/java/dev/espi/protectionstones/PSGroupRegion.java +++ b/src/main/java/dev/espi/protectionstones/PSGroupRegion.java @@ -61,6 +61,13 @@ public String getTaxPaymentPeriod() { return MiscUtil.concatWithoutLast(new ArrayList<>(s), ", "); } + @Override + public String getTimeTillNextPaymentDue() { + Set s = new HashSet<>(); + getMergedRegions().forEach(r -> s.add(r.getTimeTillNextPaymentDue())); + return MiscUtil.concatWithoutLast(new ArrayList<>(s), ", "); + } + @Override public void updateTaxPayments() { long currentTime = System.currentTimeMillis(); diff --git a/src/main/java/dev/espi/protectionstones/PSL.java b/src/main/java/dev/espi/protectionstones/PSL.java index b64c3248..41444ce0 100644 --- a/src/main/java/dev/espi/protectionstones/PSL.java +++ b/src/main/java/dev/espi/protectionstones/PSL.java @@ -193,7 +193,8 @@ public enum PSL { + ChatColor.BLUE + "Time between tax cycles: " + ChatColor.GRAY + "%taxperiod%" + "\n" + ChatColor.BLUE + "Time to pay taxes after cycle: " + ChatColor.GRAY + "%taxpaymentperiod%" + "\n" + ChatColor.BLUE + "Tax Autopayer: " + ChatColor.GRAY + "%taxautopayer%" + "\n" - + ChatColor.BLUE + "Taxes Owed: " + ChatColor.GRAY + "$%taxowed%"), + + ChatColor.BLUE + "Taxes Owed: " + ChatColor.GRAY + "$%taxowed%" + "\n" + + ChatColor.BLUE + "Next payment due in: " + ChatColor.GRAY + "%taxtimeleft%"), TAX_NEXT("tax.next_page", ChatColor.GRAY + "Do /ps tax info -p %page% to go to the next page!"), // ps buy diff --git a/src/main/java/dev/espi/protectionstones/PSMergedRegion.java b/src/main/java/dev/espi/protectionstones/PSMergedRegion.java index c5007220..25a4d0d4 100644 --- a/src/main/java/dev/espi/protectionstones/PSMergedRegion.java +++ b/src/main/java/dev/espi/protectionstones/PSMergedRegion.java @@ -34,6 +34,8 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.OptionalLong; import java.util.Set; import java.util.UUID; @@ -236,6 +238,41 @@ public String getTaxPaymentPeriod() { return MiscUtil.describeDuration(Duration.ofSeconds(getTypeOptions().taxPaymentTime)); } + @Override + public String getTimeTillNextPaymentDue() { + long currentTime = System.currentTimeMillis(); + + List payments = getTaxPaymentsDue(); + if (payments != null) { + OptionalLong earliest = payments.stream() + .filter(tp -> tp.getRegionId().equals(getId())) + .mapToLong(TaxPayment::getWhenPaymentIsDue) + .min(); + if (earliest.isPresent()) { + long msLeft = earliest.getAsLong() - currentTime; + if (msLeft <= 0) return "overdue"; + return MiscUtil.describeDuration(Duration.ofMillis(msLeft)); + } + } + + // No outstanding payments for this region — show time until next cycle + List lastAdded = getRegionLastTaxPaymentAddedEntries(); + if (lastAdded != null) { + Optional entry = lastAdded.stream() + .filter(e -> e.getRegionId().equals(getId())) + .findFirst(); + if (entry.isPresent()) { + long msLeft = entry.get().getLastPaymentAdded() + + Duration.ofSeconds(getTypeOptions().taxPeriod).toMillis() + - currentTime; + if (msLeft <= 0) return "soon"; + return MiscUtil.describeDuration(Duration.ofMillis(msLeft)); + } + } + + return "unknown"; + } + @Override public List getTaxPaymentsDue() { return mergedGroup.getTaxPaymentsDue(); diff --git a/src/main/java/dev/espi/protectionstones/PSRegion.java b/src/main/java/dev/espi/protectionstones/PSRegion.java index 2c2e285a..1fc1d223 100644 --- a/src/main/java/dev/espi/protectionstones/PSRegion.java +++ b/src/main/java/dev/espi/protectionstones/PSRegion.java @@ -511,6 +511,14 @@ public double getTaxRate() { */ public abstract String getTaxPaymentPeriod(); + /** + * Get the formatted time remaining until the next tax payment is due. + * If there are outstanding payments, returns the time until the earliest one expires. + * If no payments are outstanding, returns the time until the next tax cycle starts. + * @return formatted time string (e.g. "2d14h"), or "overdue" if past due + */ + public abstract String getTimeTillNextPaymentDue(); + /** * Get the list of tax payments that are due. * @return the list of tax payments outstanding diff --git a/src/main/java/dev/espi/protectionstones/PSStandardRegion.java b/src/main/java/dev/espi/protectionstones/PSStandardRegion.java index 243b1a40..152fb355 100644 --- a/src/main/java/dev/espi/protectionstones/PSStandardRegion.java +++ b/src/main/java/dev/espi/protectionstones/PSStandardRegion.java @@ -252,6 +252,39 @@ public String getTaxPaymentPeriod() { return MiscUtil.describeDuration(Duration.ofSeconds(getTypeOptions().taxPaymentTime)); } + @Override + public String getTimeTillNextPaymentDue() { + updateTaxPayments(); + long currentTime = System.currentTimeMillis(); + + List payments = getTaxPaymentsDue(); + if (payments != null && !payments.isEmpty()) { + long earliest = payments.stream() + .mapToLong(TaxPayment::getWhenPaymentIsDue) + .min().getAsLong(); + long msLeft = earliest - currentTime; + if (msLeft <= 0) return "overdue"; + return MiscUtil.describeDuration(Duration.ofMillis(msLeft)); + } + + // No outstanding payments — show time until next cycle + List lastAdded = getRegionLastTaxPaymentAddedEntries(); + if (lastAdded != null) { + Optional entry = lastAdded.stream() + .filter(e -> e.getRegionId().equals(getId())) + .findFirst(); + if (entry.isPresent()) { + long msLeft = entry.get().getLastPaymentAdded() + + Duration.ofSeconds(getTypeOptions().taxPeriod).toMillis() + - currentTime; + if (msLeft <= 0) return "soon"; + return MiscUtil.describeDuration(Duration.ofMillis(msLeft)); + } + } + + return "unknown"; + } + @Override public List getTaxPaymentsDue() { // taxes disabled diff --git a/src/main/java/dev/espi/protectionstones/commands/ArgTax.java b/src/main/java/dev/espi/protectionstones/commands/ArgTax.java index dd3b5eaf..0309fda6 100644 --- a/src/main/java/dev/espi/protectionstones/commands/ArgTax.java +++ b/src/main/java/dev/espi/protectionstones/commands/ArgTax.java @@ -151,7 +151,8 @@ public boolean taxInfo(String[] args, HashMap flags, PSPlayer p) .replace("%taxperiod%", r.getTaxPeriod()) .replace("%taxpaymentperiod%", r.getTaxPaymentPeriod()) .replace("%taxautopayer%", r.getTaxAutopayer() == null ? "none" : UUIDCache.getNameFromUUID(r.getTaxAutopayer())) - .replace("%taxowed%", String.format("%.2f", taxesOwed))); + .replace("%taxowed%", String.format("%.2f", taxesOwed)) + .replace("%taxtimeleft%", r.getTimeTillNextPaymentDue())); } else { PSL.msg(p, INFO_HELP); }