Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom research unlock #3942

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
650f47a
feat(research): economy unlock research
StarWishsama Aug 8, 2023
10be7ce
chore: typo fix
StarWishsama Aug 9, 2023
4106eda
chore: annotation fix
StarWishsama Aug 9, 2023
c3b1a91
fix(vault): validate vault status on unlocking
StarWishsama Aug 9, 2023
c7f4daa
chore: cleanup import
StarWishsama Aug 9, 2023
0d3d3a4
feat(guide): support custom unlock provider
StarWishsama Aug 9, 2023
e4c3b50
chore: remove `var` usage
StarWishsama Aug 9, 2023
694080f
chore: unlock unit fix
StarWishsama Aug 9, 2023
a676060
chore: relocate `SlimefunGuideUnlockProvider`
StarWishsama Aug 9, 2023
117a232
style: apply suggestion
StarWishsama Aug 9, 2023
18f8141
Update src/main/java/io/github/thebusybiscuit/slimefun4/integrations/…
StarWishsama Aug 10, 2023
59d8ed1
Update src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/Sl…
StarWishsama Aug 10, 2023
fb8f6d7
style: apply suggestion
StarWishsama Aug 10, 2023
f241c80
fix: missing import
StarWishsama Aug 10, 2023
80a48e0
style: use precondition
StarWishsama Aug 10, 2023
b2272d5
fix: use `checkArgument`
StarWishsama Aug 10, 2023
e6b8164
chore: fix line encodings
StarWishsama Aug 14, 2023
fccad03
feat(guide): support custom locked item
StarWishsama Aug 14, 2023
b42f653
chore(docs): fix doc
StarWishsama Aug 14, 2023
328f109
chore: more typo fix
StarWishsama Aug 16, 2023
5808e9a
Merge remote-tracking branch 'upstream/master' into feature/currency-…
StarWishsama Aug 27, 2023
ee2cbf9
Merge remote-tracking branch 'upstream/master' into feature/currency-…
StarWishsama Sep 10, 2023
ce2c026
chore: use `Preconditions`
StarWishsama Sep 10, 2023
a2ed3cf
chore: use `Preconditions`
StarWishsama Sep 10, 2023
38d04df
chore: fix condition check
StarWishsama Sep 10, 2023
9644364
chore: typo fix
StarWishsama Sep 10, 2023
9235128
Merge remote-tracking branch 'upstream/master' into feature/currency-…
StarWishsama Nov 25, 2023
529138b
chore(provider): implemented suggestion
StarWishsama Nov 25, 2023
ae752b3
Merge remote-tracking branch 'upstream/master' into feature/currency-…
StarWishsama Jan 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,20 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>

<exclusions>
<exclusion>
<!-- We don't need any of the dependencies -->
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- TODO: Remove this dependency -->
<dependency>
<groupId>commons-lang</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.github.thebusybiscuit.slimefun4.api.guide;

import io.github.thebusybiscuit.slimefun4.api.researches.Research;
import javax.annotation.Nonnull;
import org.bukkit.entity.Player;

/**
* The {@link SlimefunGuideUnlockProvider} used for process unlock research
* in Slimefun Guide.
* <p>
* You could trail the method to unlock your research,
* not only use experience.
*/
public interface SlimefunGuideUnlockProvider {

/**
* This method used for check {@link Player}
* could unlock specific research or not
*
* @param research {@link Research}
* @param p {@link Player}
*
* @return whether player can unlock research or not
*/
boolean canUnlock(@Nonnull Research research, @Nonnull Player p);

/**
* This method used for processing unlock research
* For example, taken player's experience level or money.
*
* @param research {@link Research}
* @param p {@link Player}
*/
void processUnlock(@Nonnull Research research, @Nonnull Player p);

/**
* This returns the unit name of research unlock token
*
* @return unit name
*/
@Nonnull
String getUnitName();
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

import com.google.common.base.Preconditions;

import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
Expand All @@ -25,6 +27,7 @@
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation;
import io.github.thebusybiscuit.slimefun4.api.guide.SlimefunGuideUnlockProvider;
import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.implementation.setup.ResearchSetup;
Expand All @@ -46,6 +49,7 @@ public class Research implements Keyed {
private final String name;
private boolean enabled = true;
private int cost;
private Optional<SlimefunGuideUnlockProvider> unlockProvider = Optional.empty();
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved

private final List<SlimefunItem> items = new LinkedList<>();

Expand All @@ -66,7 +70,8 @@ public class Research implements Keyed {
* The Cost in XP levels to unlock this {@link Research}
*
*/
public Research(@Nonnull NamespacedKey key, int id, @Nonnull String defaultName, int defaultCost) {
@ParametersAreNonnullByDefault
public Research(NamespacedKey key, int id, String defaultName, int defaultCost) {
Validate.notNull(key, "A NamespacedKey must be provided");
Validate.notNull(defaultName, "A default name must be specified");

Expand All @@ -76,6 +81,38 @@ public Research(@Nonnull NamespacedKey key, int id, @Nonnull String defaultName,
this.cost = defaultCost;
}

/**
* The constructor for a {@link Research}.
*
* Create a new research, then bind this research to the Slimefun items you want by calling
* {@link #addItems(SlimefunItem...)}. Once you're finished, call {@link #register()}
* to register it.
*
* @param key
* A unique identifier for this {@link Research}
* @param id
* old way of identifying researches
* @param defaultName
* The displayed name of this {@link Research}
* @param defaultCost
* The Cost in your custom method to unlock this {@link Research}
* @param unlockProvider
* The custom provider of unlock research {@link SlimefunGuideUnlockProvider}
*
*/
@ParametersAreNonnullByDefault
public Research(NamespacedKey key, int id, String defaultName, int defaultCost, SlimefunGuideUnlockProvider unlockProvider) {
Preconditions.checkNotNull(key, "A NamespacedKey must be provided");
Preconditions.checkNotNull(defaultName, "A default name must be specified");
Preconditions.checkNotNull(unlockProvider, "A unlock provider must be provided");

this.key = key;
this.id = id;
this.name = defaultName;
this.cost = defaultCost;
this.unlockProvider = Optional.of(unlockProvider);
}

@Override
public @Nonnull NamespacedKey getKey() {
return key;
Expand Down Expand Up @@ -138,6 +175,15 @@ public int getCost() {
return cost;
}

/**
* Gets the custom {@link SlimefunGuideUnlockProvider} of this {@link Research}
*
* @return custom unlock provider {@link SlimefunGuideUnlockProvider}
*/
public Optional<SlimefunGuideUnlockProvider> getUnlockProvider() {
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved
return unlockProvider;
}

/**
* Sets the cost in XP levels to unlock this {@link Research}.
*
Expand Down Expand Up @@ -228,7 +274,7 @@ public void unlockFromGuide(SlimefunGuideImplementation guide, Player player, Pl
if (this.canUnlock(player)) {
guide.unlockItem(player, sfItem, pl -> guide.openItemGroup(profile, itemGroup, page));
} else {
Slimefun.getLocalization().sendMessage(player, "messages.not-enough-xp", true);
Slimefun.getLocalization().sendMessage(player, "messages.not-meet-requirement", true);
}
}
}
Expand All @@ -249,7 +295,9 @@ public boolean canUnlock(@Nonnull Player p) {
}

boolean creativeResearch = p.getGameMode() == GameMode.CREATIVE && Slimefun.getRegistry().isFreeCreativeResearchingEnabled();
return creativeResearch || p.getLevel() >= cost;
SlimefunGuideUnlockProvider provider = unlockProvider.orElseGet(() -> Slimefun.getRegistry().getSlimefunGuideUnlockMode().getUnlockProvider());

return creativeResearch || provider.canUnlock(this, p);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideMode;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideUnlockMode;
import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlock;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide;
Expand Down Expand Up @@ -72,6 +73,7 @@ public final class SlimefunRegistry {
private boolean disableLearningAnimation;
private boolean logDuplicateBlockEntries;
private boolean talismanActionBarMessages;
private SlimefunGuideUnlockMode guideUnlockMode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a setter method for this would be nice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any demand for setter method? The original goal is implement a research scoped custom unlock provider for addon dev and a global bulit-in unlock provider.


private final Set<String> tickers = new HashSet<>();
private final Set<SlimefunItem> radioactive = new HashSet<>();
Expand Down Expand Up @@ -111,6 +113,8 @@ public void load(@Nonnull Slimefun plugin, @Nonnull Config cfg) {
freeCreativeResearches = cfg.getBoolean("researches.free-in-creative-mode");
researchFireworks = cfg.getBoolean("researches.enable-fireworks");
disableLearningAnimation = cfg.getBoolean("researches.disable-learning-animation");
guideUnlockMode = SlimefunGuideUnlockMode.check(cfg.getString("researches.unlock-research-mode"));

logDuplicateBlockEntries = cfg.getBoolean("options.log-duplicate-block-entries");
talismanActionBarMessages = cfg.getBoolean("talismans.use-actionbar");
}
Expand Down Expand Up @@ -260,6 +264,10 @@ public SlimefunGuideImplementation getSlimefunGuide(@Nonnull SlimefunGuideMode m
return guide;
}

public @Nonnull SlimefunGuideUnlockMode getSlimefunGuideUnlockMode() {
return guideUnlockMode;
}

/**
* This returns a {@link Map} connecting the {@link EntityType} with a {@link Set}
* of {@link ItemStack ItemStacks} which would be dropped when an {@link Entity} of that type was killed.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
package io.github.thebusybiscuit.slimefun4.core.guide;

import java.util.function.Consumer;

import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;

import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import io.github.thebusybiscuit.slimefun4.api.guide.SlimefunGuideUnlockProvider;
import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.api.researches.Research;
import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.implementation.guide.SurvivalSlimefunGuide;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

/**
* This interface is used for the different implementations that add behaviour
Expand Down Expand Up @@ -66,11 +64,12 @@ public interface SlimefunGuideImplementation {
@ParametersAreNonnullByDefault
default void unlockItem(Player p, SlimefunItem sfitem, Consumer<Player> callback) {
Research research = sfitem.getResearch();
SlimefunGuideUnlockProvider unlockProvider = research.getUnlockProvider().orElse(Slimefun.getRegistry().getSlimefunGuideUnlockMode().getUnlockProvider());

if (p.getGameMode() == GameMode.CREATIVE && Slimefun.getRegistry().isFreeCreativeResearchingEnabled()) {
research.unlock(p, true, callback);
} else {
p.setLevel(p.getLevel() - research.getCost());
unlockProvider.processUnlock(research, p);

boolean skipLearningAnimation = Slimefun.getRegistry().isLearningAnimationDisabled() || !SlimefunGuideSettings.hasLearningAnimationEnabled(p);
research.unlock(p, skipLearningAnimation, callback);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.github.thebusybiscuit.slimefun4.core.guide;

import io.github.thebusybiscuit.slimefun4.api.guide.SlimefunGuideUnlockProvider;
import javax.annotation.Nonnull;

import io.github.thebusybiscuit.slimefun4.core.guide.unlockprovider.CurrencyUnlockProvider;
import io.github.thebusybiscuit.slimefun4.core.guide.unlockprovider.ExperienceUnlockProvider;

/**
* This enum holds the different unlock research modes a {@link SlimefunGuide} can have.
* Each constant corresponds to a research unlock mode and a unlock provider
*
* @author StarWishsama
* @see SlimefunGuide
* @see SlimefunGuideImplementation
* @see SlimefunGuideUnlockProvider
*/
public enum SlimefunGuideUnlockMode {
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved
EXPERIENCE(new ExperienceUnlockProvider()),
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved

/**
* Unlock research by withdrawing player's balance.
*/
CURRENCY(new CurrencyUnlockProvider());

/**
* Research unlock provider
* <p>
* Process player can unlock research and process research payment.
*
* @see SlimefunGuideUnlockProvider
*/
@Nonnull
private final SlimefunGuideUnlockProvider unlockProvider;

SlimefunGuideUnlockMode(@Nonnull SlimefunGuideUnlockProvider unlockProvider) {
this.unlockProvider = unlockProvider;
}

/**
* Convert string to certain {@link SlimefunGuideUnlockMode}.
* If string is invalid will fall back to default one (player level)
*
* @param s text to validate
* @return {@link SlimefunGuideUnlockMode}
*/
@Nonnull
public static SlimefunGuideUnlockMode check(String s) {
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved
if (s == null) {
return SlimefunGuideUnlockMode.EXPERIENCE;
}

for (SlimefunGuideUnlockMode value : SlimefunGuideUnlockMode.values()) {
if (value.toString().equalsIgnoreCase(s)) {
return value;
}
}

return SlimefunGuideUnlockMode.EXPERIENCE;
}

@Nonnull
public SlimefunGuideUnlockProvider getUnlockProvider() {
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved
return unlockProvider;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.github.thebusybiscuit.slimefun4.core.guide.unlockprovider;

import javax.annotation.Nonnull;

import org.bukkit.entity.Player;

import io.github.thebusybiscuit.slimefun4.api.researches.Research;
import io.github.thebusybiscuit.slimefun4.api.guide.SlimefunGuideUnlockProvider;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.integrations.VaultIntegration;

/**
* Unlock research by withdrawing player's balance.
*
* @see SlimefunGuideUnlockProvider
*/
public final class CurrencyUnlockProvider implements SlimefunGuideUnlockProvider {
@Override
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved
public boolean canUnlock(@Nonnull Research research, @Nonnull Player p) {
if (!VaultIntegration.isAvailable()) {
throw new IllegalStateException("Vault integration is unavailable!");
}

return VaultIntegration.getPlayerBalance(p) >= research.getCost();
}

@Override
public void processUnlock(@Nonnull Research research, @Nonnull Player p) {
VaultIntegration.withdrawPlayer(p, research.getCost());
}

@Override
public @Nonnull String getUnitName() {
return Slimefun.getLocalization().getMessage("guide.unlock-mode.currency");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.github.thebusybiscuit.slimefun4.core.guide.unlockprovider;

import javax.annotation.Nonnull;

import org.bukkit.entity.Player;

import io.github.thebusybiscuit.slimefun4.api.researches.Research;
import io.github.thebusybiscuit.slimefun4.api.guide.SlimefunGuideUnlockProvider;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;

/**
* Unlock research by withdrawing player's experience level.
*
* @see SlimefunGuideUnlockProvider
*/
public final class ExperienceUnlockProvider implements SlimefunGuideUnlockProvider {
@Override
StarWishsama marked this conversation as resolved.
Show resolved Hide resolved
public boolean canUnlock(@Nonnull Research research, @Nonnull Player p) {
return p.getLevel() >= research.getCost();
}

@Override
public void processUnlock(@Nonnull Research research, @Nonnull Player p) {
p.setLevel(p.getLevel() - research.getCost());
}

@Override
public @Nonnull String getUnitName() {
return Slimefun.getLocalization().getMessage("guide.unlock-mode.experience");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,8 @@ private void displaySlimefunItem(ChestMenu menu, ItemGroup itemGroup, Player p,
menu.addItem(index, new CustomItemStack(ChestMenuUtils.getNoPermissionItem(), sfitem.getItemName(), message.toArray(new String[0])));
menu.addMenuClickHandler(index, ChestMenuUtils.getEmptyClickHandler());
} else if (isSurvivalMode() && research != null && !profile.hasUnlocked(research)) {
menu.addItem(index, new CustomItemStack(ChestMenuUtils.getNotResearchedItem(), ChatColor.WHITE + ItemUtils.getItemName(sfitem.getItem()), "&4&l" + Slimefun.getLocalization().getMessage(p, "guide.locked"), "", "&a> Click to unlock", "", "&7Cost: &b" + research.getCost() + " Level(s)"));
String unitName = research.getUnlockProvider().orElse(Slimefun.getRegistry().getSlimefunGuideUnlockMode().getUnlockProvider()).getUnitName();
menu.addItem(index, new CustomItemStack(ChestMenuUtils.getNotResearchedItem(), ChatColor.WHITE + ItemUtils.getItemName(sfitem.getItem()), "&4&l" + Slimefun.getLocalization().getMessage(p, "guide.locked"), "", "&a> Click to unlock", "", "&7Cost: &b" + research.getCost() + " " + unitName));
menu.addMenuClickHandler(index, (pl, slot, item, action) -> {
research.unlockFromGuide(this, p, profile, sfitem, itemGroup, page);
return false;
Expand Down
Loading