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
Changes from all 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
@@ -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>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.api.events;

import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;

import org.apache.commons.lang.Validate;
@@ -28,9 +29,9 @@ public class SlimefunGuideOpenEvent extends Event implements Cancellable {
private boolean cancelled;

public SlimefunGuideOpenEvent(@Nonnull Player p, @Nonnull ItemStack guide, @Nonnull SlimefunGuideMode layout) {
Validate.notNull(p, "The Player cannot be null");
Validate.notNull(guide, "Guide cannot be null");
Validate.notNull(layout, "Layout cannot be null");
Preconditions.checkArgument(p != null, "The Player cannot be null");
Preconditions.checkArgument(guide != null, "Guide cannot be null");
Preconditions.checkArgument(layout != null, "Layout cannot be null");
this.player = p;
this.guide = guide;
this.layout = layout;
@@ -42,8 +43,7 @@ public SlimefunGuideOpenEvent(@Nonnull Player p, @Nonnull ItemStack guide, @Nonn
*
* @return The {@link Player}
*/
@Nonnull
public Player getPlayer() {
public @Nonnull Player getPlayer() {
return player;
}

@@ -53,8 +53,7 @@ public Player getPlayer() {
*
* @return The {@link ItemStack}
*/
@Nonnull
public ItemStack getGuide() {
public @Nonnull ItemStack getGuide() {
return guide;
}

@@ -64,8 +63,7 @@ public ItemStack getGuide() {
*
* @return The {@link SlimefunGuideMode}
*/
@Nonnull
public SlimefunGuideMode getGuideLayout() {
public @Nonnull SlimefunGuideMode getGuideLayout() {
return layout;
}

@@ -76,7 +74,7 @@ public SlimefunGuideMode getGuideLayout() {
* The new {@link SlimefunGuideMode}
*/
public void setGuideLayout(@Nonnull SlimefunGuideMode layout) {
Validate.notNull(layout, "You must specify a layout that is not-null!");
Preconditions.checkArgument(layout != null, "You must specify a layout that is not-null!");
this.layout = layout;
}

@@ -90,14 +88,12 @@ public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}

@Nonnull
public static HandlerList getHandlerList() {
public static @Nonnull HandlerList getHandlerList() {
return handlers;
}

@Nonnull
@Override
public HandlerList getHandlers() {
public @Nonnull HandlerList getHandlers() {
return getHandlerList();
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.github.thebusybiscuit.slimefun4.api.guide;

import io.github.bakedlibs.dough.items.CustomItemStack;
import io.github.bakedlibs.dough.items.ItemUtils;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.researches.Research;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import javax.annotation.Nonnull;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

/**
* The {@link SlimefunGuideUnlockProvider} used for process
* research unlock in Slimefun Guide.
* <p>
* You could trail the method to unlock your research,
* and locked item.
*
* @author StarWishsama
*/
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, take 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
* For example, Level(s) or money
*
* @return unit name
*/
@Nonnull String getUnitName();

/**
* This returns guide item when research locked
* By default, it shows up in the guide as a barrier with a name & the cost in it.
*
* @return locked item {@link ItemStack}
*/
@Nonnull
default ItemStack getLockedItem(@Nonnull Research research, @Nonnull SlimefunItem sfItem, @Nonnull Player p) {
return 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() + " " + getUnitName());
}
}
Original file line number Diff line number Diff line change
@@ -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;
@@ -26,6 +28,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;
@@ -47,6 +50,7 @@ public class Research implements Keyed {
private final String name;
private boolean enabled = true;
private int cost;
private Optional<SlimefunGuideUnlockProvider> unlockProvider = Optional.empty();

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

@@ -67,7 +71,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");

@@ -77,6 +82,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;
@@ -139,6 +176,15 @@ public int getCost() {
return cost;
}

/**
* Gets the custom {@link SlimefunGuideUnlockProvider} of this {@link Research}
*
* @return custom unlock provider {@link SlimefunGuideUnlockProvider}
*/
public @Nonnull Optional<SlimefunGuideUnlockProvider> getUnlockProvider() {
return unlockProvider;
}

/**
* Sets the cost in XP levels to unlock this {@link Research}.
*
@@ -153,6 +199,15 @@ public void setCost(int cost) {
this.cost = cost;
}

/**
* Set the custom {@link SlimefunGuideUnlockProvider} of this {@link Research}
*
* @param unlockProvider custom unlock provider {@link SlimefunGuideUnlockProvider}
*/
public void setUnlockProvider(Optional<SlimefunGuideUnlockProvider> unlockProvider) {
this.unlockProvider = unlockProvider;
}

/**
* Bind the specified {@link SlimefunItem SlimefunItems} to this {@link Research}.
*
@@ -245,7 +300,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.requirement-unfulfilled", true);
}
}
}
@@ -266,7 +321,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);
}

/**
Original file line number Diff line number Diff line change
@@ -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;
@@ -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<>();
@@ -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");
}
@@ -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.
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
@@ -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);
Loading