Implement timeout respecting in challenges completion.

Implement timeout in GUI's.

Relates #71
This commit is contained in:
BONNe 2021-09-24 16:03:02 +03:00
parent 1b01995546
commit bb5e861b30
8 changed files with 202 additions and 6 deletions

View File

@ -251,13 +251,25 @@ public class ChallengesPlayerData implements DataObject
* @param challengeName - unique challenge name
* @param times - the number of times to set
*/
public void setChallengeTimes(@NonNull String challengeName, @NonNull int times)
public void setChallengeTimes(@NonNull String challengeName, int times)
{
challengeStatus.put(challengeName, times);
challengesTimestamp.put(challengeName, System.currentTimeMillis());
}
/**
* Gets last completion time.
*
* @param challengeName the unique id
* @return the last completion time
*/
public long getLastCompletionTime(@NonNull String challengeName)
{
return this.challengesTimestamp.getOrDefault(challengeName, 0L);
}
/**
* Check if a challenge has been done
*

View File

@ -1397,6 +1397,43 @@ public class ChallengesManager
}
/**
* This method returns if given user breached timeout for given challenge.
* @param user - User that must be checked.
* @param world - World where challenge operates.
* @param challenge - Challenge that must be checked.
* @return True, if challenge is breached timeout, otherwise - false.
*/
public boolean isBreachingTimeOut(User user, World world, Challenge challenge)
{
if (challenge.getTimeout() <= 0)
{
// Challenge does not have a timeout.
return false;
}
return System.currentTimeMillis() <
this.getLastCompletionDate(user, world, challenge) + challenge.getTimeout();
}
/**
* Gets last completion date for given challenge.
*
* @param user the user
* @param world the world
* @param challenge the challenge
* @return the last completion date
*/
public long getLastCompletionDate(User user, World world, Challenge challenge)
{
String userId = this.getDataUniqueID(user, Util.getWorld(world));
this.addPlayerData(userId);
return this.playerCacheData.get(userId).getLastCompletionTime(challenge.getUniqueId());
}
/**
* This method sets given challenge as completed.
* @param user - Targeted user.

View File

@ -13,6 +13,7 @@ import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;
@ -159,13 +160,17 @@ public abstract class CommonPanel
String requirements = isCompletedAll ? "" : this.generateRequirements(challenge, target);
// Get rewards in single string
String rewards = isCompletedAll ? "" : this.generateRewards(challenge, isCompletedOnce);
// Get coolDown in singe string
String coolDown = isCompletedAll || challenge.getTimeout() <= 0 ? "" :
this.generateCoolDown(challenge, target);
if (!description.replaceAll("(?m)^[ \\t]*\\r?\\n", "").isEmpty())
{
String returnString = this.user.getTranslationOrNothing(reference + "lore",
"[requirements]", requirements,
"[rewards]", rewards,
"[status]", status);
"[status]", status,
"[cooldown]", coolDown);
// remove empty lines from the generated text.
List<String> collect =
@ -191,7 +196,8 @@ public abstract class CommonPanel
Constants.PARAMETER_DESCRIPTION, description,
"[requirements]", requirements,
"[rewards]", rewards,
"[status]", status);
"[status]", status,
"[cooldown]", coolDown);
// Remove empty lines and returns as a list.
@ -202,6 +208,43 @@ public abstract class CommonPanel
}
/**
* Generate cool down string.
*
* @param challenge the challenge
* @param target the target
* @return the string
*/
private String generateCoolDown(Challenge challenge, @Nullable User target)
{
final String reference = Constants.DESCRIPTIONS + "challenge.cooldown.";
String coolDown;
if (target != null && this.manager.isBreachingTimeOut(target, this.world, challenge))
{
long missing = this.manager.getLastCompletionDate(this.user, this.world, challenge) +
challenge.getTimeout() - System.currentTimeMillis();
coolDown = this.user.getTranslation(reference + "wait-time",
"[time]",
Utils.parseDuration(Duration.ofMillis(missing), this.user));
}
else
{
coolDown = "";
}
String timeout = this.user.getTranslation(reference + "timeout",
"[time]",
Utils.parseDuration(Duration.ofMillis(challenge.getTimeout()), this.user));
return this.user.getTranslation(reference + "lore",
"[timeout]", timeout,
"[wait-time]", coolDown);
}
/**
* This method generate requirements description for given challenge.
* @param challenge Challenge which requirements must be generated.

View File

@ -303,6 +303,12 @@ public class ChallengesPanel extends CommonPanel
this.createChallengeButton(template, challenge).getItem());
}
}
else if (challenge.isRepeatable() && challenge.getTimeout() > 0)
{
// Update timeout after clicking.
panel.getInventory().setItem(i,
this.createChallengeButton(template, challenge).getItem());
}
break;
case "COMPLETE_MAX":
if (challenge.isRepeatable())
@ -328,6 +334,12 @@ public class ChallengesPanel extends CommonPanel
this.createChallengeButton(template, challenge).getItem());
}
}
else if (challenge.getTimeout() > 0)
{
// Update timeout after clicking.
panel.getInventory().setItem(i,
this.createChallengeButton(template, challenge).getItem());
}
}
break;
case "MULTIPLE_PANEL":

View File

@ -3,6 +3,8 @@ package world.bentobox.challenges.tasks;
import com.google.common.collect.UnmodifiableIterator;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
@ -228,7 +230,8 @@ public class TryToComplete
int maxTimes)
{
return new TryToComplete(addon, user, challenge, world, topLabel, permissionPrefix).
build(maxTimes).meetsRequirements;
build(maxTimes).
meetsRequirements;
}
@ -713,6 +716,17 @@ public class TryToComplete
Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.not-repeatable"));
result = EMPTY_RESULT;
}
// Check if timeout is not broken
else if (this.manager.isBreachingTimeOut(this.user, this.world, this.challenge))
{
long missing = this.manager.getLastCompletionDate(this.user, this.world, challenge) +
this.challenge.getTimeout() - System.currentTimeMillis();
Utils.sendMessage(this.user, this.user.getTranslation("challenges.errors.timeout",
"[timeout]", Utils.parseDuration(Duration.ofMillis(this.challenge.getTimeout()), this.user),
"[wait-time]", Utils.parseDuration(Duration.ofMillis(missing), this.user)));
result = EMPTY_RESULT;
}
// Check environment
else if (!this.challenge.getEnvironment().isEmpty() &&
!this.challenge.getEnvironment().contains(this.user.getWorld().getEnvironment()))
@ -776,7 +790,7 @@ public class TryToComplete
*/
private int getAvailableCompletionTimes(int vantedTimes)
{
if (!this.challenge.isRepeatable())
if (!this.challenge.isRepeatable() || this.challenge.getTimeout() > 0)
{
// Challenge is not repeatable
vantedTimes = 1;
@ -1513,7 +1527,7 @@ public class TryToComplete
*
* @author tastybento
*/
class ChallengeResult
static class ChallengeResult
{
/**
* This method sets that challenge meets all requirements at least once.

View File

@ -1,6 +1,7 @@
package world.bentobox.challenges.utils;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@ -878,4 +879,43 @@ public class Utils
"[type]", prettifyObject(itemType, user),
"[meta]", meta);
}
/**
* This method parses duration to a readable format.
* @param duration that needs to be parsed.
* @return parsed duration string.
*/
public static String parseDuration(Duration duration, User user)
{
final String reference = Constants.DESCRIPTIONS + "challenge.cooldown.";
String returnString = "";
if (duration.toDays() > 0)
{
returnString += user.getTranslationOrNothing(reference + "in-days",
Constants.PARAMETER_NUMBER, String.valueOf(duration.toDays()));
}
if (duration.toHoursPart() > 0)
{
returnString += user.getTranslationOrNothing(reference + "in-hours",
Constants.PARAMETER_NUMBER, String.valueOf(duration.toHoursPart()));
}
if (duration.toMinutesPart() > 0)
{
returnString += user.getTranslationOrNothing(reference + "in-minutes",
Constants.PARAMETER_NUMBER, String.valueOf(duration.toMinutesPart()));
}
if (duration.toSecondsPart() > 0 || returnString.isBlank())
{
returnString += user.getTranslationOrNothing(reference + "in-seconds",
Constants.PARAMETER_NUMBER, String.valueOf(duration.toSecondsPart()));
}
return returnString;
}
}

View File

@ -829,6 +829,7 @@ challenges:
lore: |-
[description]
[status]
[cooldown]
[requirements]
[rewards]
# Contains a text generated inside [status] lore
@ -841,6 +842,23 @@ challenges:
completed-times-of: "&2 Completed &7&l [number] &r&2 out of &7&l [max] &r&2 times"
# Status message that indicates that max completion count reached for repeatable challenge
completed-times-reached: "&2&l Completed all &7 [max] &2 times"
# Contains a text generated inside [cooldown] lore
cooldown:
lore: |-
[timeout]
[wait-time]
# Text message that shows challenges timeout.
timeout: "&7&l Cool down: &r&7 [time]"
# Text message that shows challenges wait time if it is larger than 0.
wait-time: "&c&l Available after: &r&c [time]"
# Text message that replaces days if number > 1
in-days: "[number] d, "
# Text message that replaces hours if number > 1
in-hours: "[number] h, "
# Text message that replaces minutes if number > 1
in-minutes: "[number] min, "
# Text message that replaces seconds if number > 1
in-seconds: "[number] s"
# Contains a text generated inside [requirements] lore
requirements:
lore: |-
@ -1156,6 +1174,7 @@ challenges:
invalid-challenge: "&c Challenge [challenge] contains invalid data. It will not be loaded from database!"
no-library-entries: "&c Cannot find any library entries. Nothing to show."
not-hooked: "&c Challenges Addon could not find any GameMode."
timeout: "&c This challenge requires to wait [timeout] between completions. You must wait [wait-time] till complete it again."
# # Showcase for manual material translation
# materials:
# # Names should be lowercase.

View File

@ -849,6 +849,7 @@ challenges:
lore: |-
[description]
[status]
[cooldown]
[requirements]
[rewards]
# Contains a text generated inside [status] lore
@ -861,6 +862,23 @@ challenges:
completed-times-of: "&2 Izpildīts &7&l [number] &r&2 no &7&l [max] &r&2 reizēm"
# Status message that indicates that max completion count reached for repeatable challenge
completed-times-reached: "&2&l Izpildīts visas &7 [max] &2 reizes"
# Contains a text generated inside [cooldown] lore
cooldown:
lore: |-
[timeout]
[wait-time]
# Text message that shows challenges timeout.
timeout: "&7&l Taimauts: &r&7 [time]"
# Text message that shows challenges wait time if it is larger than 0.
wait-time: "&c&l Jānogaida: &r&c [time]"
# Text message that replaces days if number > 1
in-days: "[number] d, "
# Text message that replaces hours if number > 1
in-hours: "[number] h, "
# Text message that replaces minutes if number > 1
in-minutes: "[number] min, "
# Text message that replaces seconds if number > 1
in-seconds: "[number] s"
# Contains a text generated inside [requirements] lore
requirements:
lore: |-
@ -1180,6 +1198,7 @@ challenges:
invalid-challenge: "&c Uzdevums [challenge] satur nekorektus datus. Tas var tikt ielādēts nekorekti!"
no-library-entries: "&c Nevar atrast bibliotēkas ierakstus. Nav ko rādīt."
not-hooked: "&c Uzdevumu Papildinājumam neizdevās atrast Spēles Režīmu."
timeout: "&c Šim uzdevumam ir uzstādīts [timeout] taimauts starp izpildēm. Tev vēl ir jāgaida [wait-time], lai varētu pildīt uzdevumu."
# # Showcase for manual material translation
# materials:
# # Names should be lowercase.