13 Commits

Author SHA1 Message Date
c4e29ff728 Update for 1.20.3
- Added verbose entry to the Player configuration files. Player configuration files will need to be regenerated. Please make sure to make backups of all configurations before regenerating configuration files.
- Fixed a bug with the hide feature; now, any entity within a 25 block radius will automatically lose target of the player when their luck activates while sneaking.
- Removed the vein-mining feature as it is unstable and non-functional. A fix and/or replacement will be implemented in a future update.
- Updated supported version to 1.20.3
- Adjusted Logging methods.
2023-12-07 20:38:47 -06:00
32a9435e30 Fixed stream error when triggering OreVein effect 2022-06-15 11:23:54 -05:00
d282c2e982 Revert "FeelingLucky v1.3.0"
This reverts commit 0ad60075da.
2022-06-15 11:17:04 -05:00
0ad60075da FeelingLucky v1.3.0
This update will entail providing spigot support.
This is currently incomplete.
2022-06-15 09:35:35 -05:00
661b7bd6a7 Removed deprecated "new Double" call. 2022-06-15 08:38:42 -05:00
639bf09e48 Merge branch 'main' of https://github.com/SimplexDevelopment/FeelingLucky 2022-06-15 08:34:22 -05:00
8b3486a269 Update v1.2.1
Changed version to 1.2.1
Added codacy analysis workflow.
2022-06-15 08:34:15 -05:00
94b6067f97 Update README.md 2022-06-15 08:30:09 -05:00
0b07bd9da2 Minor Update 1.2.1
Adjusted the Luck class to reflect:
- SplittableRandom has been replaced with SecureRandom in favor of entropy-based pseudorandom calculations compared to pseudorandom calculations based off the system time.
- Adjusted the quickRNG to factor in whether the user has the luck potion effect, and to just apply the multiplier regardless of whether it is the default value.
- Also adjusted the values, as the original value still remained at 1024, whereas the randomized number criteria was a percentage of 100. Both the input value and the randomized number criteria now are percentages of 100, based off a total of 1024 possible points.
2022-06-15 08:28:53 -05:00
400687733f Update README.md 2022-06-14 12:30:43 -05:00
c7a168ede1 Update README.md 2022-06-14 12:25:33 -05:00
fda004a3c8 Update README.md 2022-06-14 01:52:21 -05:00
0c82515f43 Update Release 1.2.0
Added a command to regenerate the configuration file.

This command can only be used from console.
2022-06-14 01:51:45 -05:00
35 changed files with 830 additions and 337 deletions

32
.github/workflows/codacy-analysis.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Codacy Security Scan
on:
push:
branches: [ "master", "main" ]
pull_request:
branches: [ "master", "main" ]
jobs:
codacy-security-scan:
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@main
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@master
with:
output: results.sarif
format: sarif
# Adjust severity of non-security issues
gh-code-scanning-compat: true
# Force 0 exit code to allow SARIF file generation
# This will handover control about PR rejection to the GitHub side
max-allowed-issues: 2147483647
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@main
with:
sarif_file: results.sarif

View File

@ -1,4 +1,4 @@
# <center>FeelingLucky Beta v1.0.2</center>
# <center>FeelingLucky v1.2.0 - A luck driven mechanics plugin.</center>
## <center><u><span style="color:blue">Plugin Description:</u></center>
@ -17,12 +17,13 @@ Admins can set, reset, add to, and take from player's luck stat.
Admins can also reload the main configuration, as well as individual and all player configurations.
For this, the command is <b><span style="color:violet">/luck reload -m</color></b> for the main config,
<b><span style="color:violet">/luck reload</color></b> to reload all player configurations, and <b><span style="color:violet">/luck reload -p <i>PLAYER_NAME</i></span></b> to reload individual player configuration files.
Server owners and/or individuals with console access can run /rgc to regenerate the main configuration file in the case that there are values missing, corrupted, or invalid.
## <center><u><span style="color:blue">Server Requirements:</u></center>
In order to run <b>FeelingLucky</b> v<b>1.1.0</b>, the latest version of Paper or Spigot is required.
#### <center><span style="color:red">Note: Paper is REQUIRED for this plugin to run. Spigot is not supported, and support for Spigot will not be added in the future.</center></span>
#### <center><span style="color:red">Note: Paper is REQUIRED for this plugin to run. Spigot is not supported, however Spigot support is currently in progress.</center></span>
### <center>Note: If you are migrating from an Alpha build, the plugin configuration folder will need to be regenerated.</center>

View File

@ -3,7 +3,7 @@ plugins {
}
group = 'io.github.simplex'
version = '1.2.0-RC01'
version = '1.3.0'
repositories {
mavenCentral()
@ -12,7 +12,7 @@ repositories {
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.19-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.20.3-R0.1-SNAPSHOT")
}
def targetJavaVersion = 17

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,10 +1,14 @@
package io.github.simplex.api;
import java.io.Serializable;
import org.bukkit.entity.Player;
import java.io.Serializable;
public interface LuckContainer extends Serializable
{
public interface LuckContainer extends Serializable {
boolean isVerbose();
void setVerbose(boolean verbose);
boolean isMatch(double number);

View File

@ -1,21 +1,24 @@
package io.github.simplex.lib;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
public enum Messages {
public enum Messages
{
NOT_FROM_CONSOLE(MiniComponent.err("This command may only be used in game.")),
NO_PERMISSION(MiniComponent.err("You do not have permission to use this command.")),
NO_PLAYER(MiniComponent.warn("That player cannot be found.")),
OUT_OF_BOUNDS(MiniComponent.err("Number must be between -1024.0 and 1024.0"));
private final Component message;
private final ComponentLike message;
Messages(Component message) {
Messages(ComponentLike message)
{
this.message = message;
}
public Component get() {
public ComponentLike get()
{
return message;
}
}

View File

@ -1,10 +1,12 @@
package io.github.simplex.lib;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
public class MiniComponent {
private final String content;
@ -21,17 +23,17 @@ public class MiniComponent {
}
@Contract("_ -> new")
public static Component info(String content) {
public static ComponentLike info(String content) {
return new MiniComponent(content).color(ChatColor.GREEN).send();
}
@Contract("_ -> new")
public static Component warn(String content) {
public static ComponentLike warn(String content) {
return new MiniComponent(content).color(ChatColor.YELLOW).decorate(TextDecoration.ITALIC).send();
}
@Contract("_ -> new")
public static Component err(String content) {
public static ComponentLike err(String content) {
return new MiniComponent(content).color(ChatColor.RED).decorate(TextDecoration.BOLD).send();
}
@ -45,7 +47,7 @@ public class MiniComponent {
return this;
}
public Component send() {
public @Nullable Component send() {
if (color == null) {
if (decoration == null) return Component.empty().content(content);

View File

@ -1,89 +1,116 @@
package io.github.simplex.luck;
import io.github.simplex.luck.listener.AbstractListener;
import io.github.simplex.luck.util.Logs;
import io.github.simplex.luck.util.SneakyWorker;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class Config extends YamlConfiguration {
private final FeelingLucky plugin;
private final List<String> configEntries = new ArrayList<>() {{
add("high_rarity_chance");
add("medium_rarity_chance");
add("low_rarity_chance");
add("block_drops");
add("bonemeal");
add("cheat_death");
add("enchanting");
add("experience");
add("item_drops");
add("random_effect");
add("restore_hunger");
add("take_damage");
add("unbreakable");
public class Config extends YamlConfiguration
{
private final Map<String, Object> configEntries = new HashMap<>()
{{
put("high_rarity_chance", 512.0);
put("medium_rarity_chance", 128.0);
put("low_rarity_chance", 64.0);
put("block_drops", "LOW");
put("bonemeal", "MED");
put("cheat_death", "MED");
put("enchanting", "HIGH");
put("experience", "HIGH");
put("give_damage", "LOW");
put("hide_check", "MED");
put("item_drops", "LOW");
put("jump_boost", "MED");
put("ore_vein", "HIGH");
put("random_effect", "HIGH");
put("restore_hunger", "NONE");
put("take_damage", "MED");
put("unbreakable", "HIGH");
}};
private File configFile;
public Config(FeelingLucky plugin) {
this.plugin = plugin;
public Config(FeelingLucky plugin)
{
File dataFolder = plugin.getDataFolder();
if (!dataFolder.exists()) dataFolder.mkdirs();
if (dataFolder.mkdirs())
{
plugin.getLogger().info("Created new data folder. Writing new configuration file...");
plugin.saveResource("config.yml", true);
}
File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) {
SneakyWorker.sneakyTry(configFile::createNewFile);
if (!configFile.exists())
{
plugin.getLogger().info("No configuration file exists. Creating a new one...");
plugin.saveResource("config.yml", true);
}
this.configFile = configFile;
load();
if (validateIntegrity()) {
File newFile = new File(plugin.getDataFolder(), "config.yml");
SneakyWorker.sneakyTry(() -> {
Files.delete(Path.of(this.configFile.getPath()));
newFile.createNewFile();
plugin.saveResource("config.yml", true);
});
this.configFile = newFile;
if (validateIntegrity(this.configFile))
{
load();
}
else
{
configEntries.forEach(super::set);
Logs.warn("Your configuration file is missing keys. " +
"\nPlease use /rgc in the console to regenerate the config file. " +
"\nAlternatively, delete the config.yml and restart your server. " +
"\nIt is safe to ignore this, as default values will be used." +
"\nHowever, it is highly recommended to regenerate the configuration.");
}
}
public void save() {
public void save()
{
SneakyWorker.sneakyTry(() -> save(configFile));
}
public void load() {
public void load()
{
SneakyWorker.sneakyTry(() -> load(configFile));
}
public void reload() {
public void reload()
{
save();
load();
}
public boolean validateIntegrity() {
for (String key : getKeys(false)) {
if (!configEntries.contains(key)) {
plugin.getLogger().severe("The contents of your configuration file is corrupted! Regenerating a new configuration file...");
return true;
public boolean validateIntegrity(@NotNull File fromDisk)
{
YamlConfiguration disk = YamlConfiguration.loadConfiguration(fromDisk);
if (disk.getKeys(true).isEmpty())
{
return false;
}
boolean result = true;
for (String key : configEntries.keySet())
{
if (!disk.getKeys(false).contains(key))
{
if (result)
result = false;
}
}
return false;
return result;
}
public AbstractListener.Rarity getRarity(String name) {
public AbstractListener.Rarity getRarity(String name)
{
return AbstractListener.Rarity.valueOf(getString(name));
}
public double getChance(String path) {
public double getChance(String path)
{
return getDouble(path);
}
}

View File

@ -4,33 +4,37 @@ import io.github.simplex.luck.listener.*;
import io.github.simplex.luck.player.PlayerConfig;
import io.github.simplex.luck.player.PlayerHandler;
import io.github.simplex.luck.util.LuckCMD;
import io.github.simplex.luck.util.SneakyWorker;
import io.github.simplex.luck.util.RegenerateConfigCMD;
import io.github.simplex.luck.util.SpecialFootItem;
import io.github.simplex.metrics.Metrics;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.kyori.adventure.chat.ChatType;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandMap;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
public final class FeelingLucky extends JavaPlugin {
public final class FeelingLucky extends JavaPlugin
{
private final Map<UUID, PlayerConfig> configMap = new HashMap<>();
private final File playerDirectory = new File(getDataFolder(), "players");
private final SpecialFootItem specialFootItem = new SpecialFootItem();
private final ChatType.Bound bind = ChatType.CHAT.bind(Component.text(getName()));
private PlayerHandler handler;
private Config config;
public Map<UUID, PlayerConfig> getConfigMap() {
public Map<UUID, PlayerConfig> getConfigMap()
{
return configMap;
}
@Override
public void onEnable() {
public void onEnable()
{
getLogger().info("Initializing metrics...");
new Metrics(this, 15054);
getLogger().info("Metrics loaded. Initializing the PlayerHandler...");
@ -41,15 +45,17 @@ public final class FeelingLucky extends JavaPlugin {
loadPlayerConfigurations();
getLogger().info("Attempting to load the main configuration...");
config = new Config(this);
getLogger().info("Main Config loaded successfully! Attempting to load the Luck command...");
getLogger().info("Main Config loaded successfully! Loading commands...");
new LuckCMD(this);
getLogger().info("Successfully loaded the Luck command!");
new RegenerateConfigCMD(this);
getLogger().info("Successfully loaded all commands!");
getLogger().info("Successfully initialized!");
}
@Override
public void onDisable() {
public void onDisable()
{
getLogger().info("Saving all player configurations...");
configMap.values().forEach(PlayerConfig::save);
getLogger().info("Complete! Saving the main config...");
@ -57,8 +63,10 @@ public final class FeelingLucky extends JavaPlugin {
getLogger().info("Complete! Goodbye! :)");
}
private void loadPlayerConfigurations() {
if (!playerDirectory.exists()) {
private void loadPlayerConfigurations()
{
if (!playerDirectory.exists())
{
getLogger().info("No directory exists. Creating...");
playerDirectory.mkdirs();
getLogger().info("Created new directory \"FeelingLucky/players\".");
@ -66,44 +74,67 @@ public final class FeelingLucky extends JavaPlugin {
}
File[] files = playerDirectory.listFiles();
if (files != null) {
Arrays.stream(files).forEach(file -> {
UUID uuid = UUID.fromString(file.getName().split("\\.")[0]);
configMap.put(uuid, PlayerConfig.initFrom(this, file));
});
if (files != null)
{
Arrays.stream(files).forEach(file ->
{
UUID uuid = UUID.fromString(file.getName().split("\\.")[0]);
configMap.put(uuid, PlayerConfig.initFrom(this, file));
});
configMap.forEach((u, pc) -> pc.load());
getLogger().info("Successfully loaded all configurations!");
} else {
}
else
{
getLogger().info("There are no player configurations to load.");
}
}
private void registerListeners() {
try {
Class<?>[] listeners = SneakyWorker.getClasses(AbstractListener.class.getPackage().getName());
Arrays.stream(listeners).forEach(l -> {
if (AbstractListener.class.isAssignableFrom(l)) {
if (l.equals(AbstractListener.class)) return;
SneakyWorker.sneakyTry(() -> l.getDeclaredConstructor(FeelingLucky.class).newInstance(this));
}
});
} catch (IOException | ClassNotFoundException ex) {
getLogger().severe(ex.getMessage());
}
private void registerListeners()
{
new BlockDrops(this);
new BonemealFullCrop(this);
new CheatDeath(this);
new EnchantmentBoost(this);
new ExpBoost(this);
new GiveDamage(this);
new HideCheck(this);
new IllOmen(this);
new ItemDrops(this);
new JumpBoost(this);
// new OreVein(this); (Currently unstable & unsafe).
new PlayerListener(this);
new RandomEffect(this);
new RestoreHunger(this);
new TakeDamage(this);
new UnbreakableTool(this);
new VillagerInventory(this);
}
public PlayerHandler getHandler() {
public PlayerHandler getHandler()
{
return handler;
}
@Override
@NotNull
public Config getConfig() {
public Config getConfig()
{
return config;
}
public SpecialFootItem getFoot() {
public SpecialFootItem getFoot()
{
return specialFootItem;
}
public CommandMap getCommandMap()
{
return getServer().getCommandMap();
}
public ChatType.Bound bind()
{
return bind;
}
}

View File

@ -1,29 +1,31 @@
package io.github.simplex.luck.listener;
import io.github.simplex.luck.Config;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.PlayerHandler;
import net.kyori.adventure.audience.Audience;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
public abstract class AbstractListener implements Listener {
protected final FeelingLucky plugin;
protected final Config config;
public AbstractListener(FeelingLucky plugin) {
this.plugin = plugin;
this.config = plugin.getConfig();
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
protected PlayerHandler getHandler() {
return plugin.getHandler();
}
public void register(AbstractListener listener) {
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
}
public boolean doesQualify(String name, double luck) {
return switch (config.getRarity(name)) {
case HIGH -> luck > config.getChance("high_rarity_chance");
case MED -> luck > config.getChance("medium_rarity_chance");
case LOW -> luck > config.getChance("low_rarity_chance");
return switch (plugin.getConfig().getRarity(name)) {
case HIGH -> luck > plugin.getConfig().getChance("high_rarity_chance");
case MED -> luck > plugin.getConfig().getChance("medium_rarity_chance");
case LOW -> luck > plugin.getConfig().getChance("low_rarity_chance");
case NONE -> true;
};
}
@ -34,4 +36,8 @@ public abstract class AbstractListener implements Listener {
LOW,
NONE
}
public Audience asAudience(final Player player) {
return player;
}
}

View File

@ -1,28 +1,35 @@
package io.github.simplex.luck.listener;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import io.github.simplex.luck.util.SneakyWorker;
import java.util.List;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockDropItemEvent;
import java.util.List;
public final class BlockDrops extends AbstractListener {
public BlockDrops(FeelingLucky plugin) {
public final class BlockDrops extends AbstractListener
{
public BlockDrops(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void extraBlockDrops(BlockDropItemEvent event) {
public void extraBlockDrops(BlockDropItemEvent event)
{
Player player = event.getPlayer();
Luck luck = getHandler().getLuckContainer(player);
List<Item> items = event.getItems();
if (luck.quickRNG(luck.getValue()) && doesQualify("block_drops", luck.getValue())) {
event.getItems().clear();
if (luck.quickRNG(luck.getValue()) && doesQualify("block_drops", luck.getValue()))
{
event.getItems().addAll(items.stream().map(SneakyWorker::move).toList());
}
if (luck.isVerbose())
asAudience(player).sendMessage(MiniComponent.info("You got lucky and received extra drops!"));
}
}

View File

@ -4,6 +4,7 @@ import io.github.simplex.lib.ItemBuilder;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import net.kyori.adventure.audience.Audience;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.Ageable;
@ -17,11 +18,13 @@ import org.bukkit.inventory.ItemStack;
public final class BonemealFullCrop extends AbstractListener {
public BonemealFullCrop(FeelingLucky plugin) {
super(plugin);
register(this);
}
@EventHandler
public void bonemealFullCrop(PlayerInteractEvent event) {
Player player = event.getPlayer();
Audience pAud = player;
Action action = event.getAction();
ItemStack bonemeal = ItemBuilder.of(Material.BONE_MEAL).build();
Luck luck = getHandler().getLuckContainer(player);
@ -42,7 +45,12 @@ public final class BonemealFullCrop extends AbstractListener {
crop.setAge(crop.getMaximumAge());
data.merge(crop);
block.setBlockData(data);
player.sendMessage(MiniComponent.info("You got lucky and your crops grew to maturity."));
if (luck.isVerbose()) {
asAudience(player).sendMessage(MiniComponent.info("Your luck has caused your crop to become ready for" +
" " +
"harvest!"));
}
}
}
}

View File

@ -3,25 +3,35 @@ package io.github.simplex.luck.listener;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import net.kyori.adventure.audience.Audience;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.PlayerDeathEvent;
public final class CheatDeath extends AbstractListener {
public CheatDeath(FeelingLucky plugin) {
public final class CheatDeath extends AbstractListener
{
public CheatDeath(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void cheatDeath(PlayerDeathEvent event) {
public void cheatDeath(PlayerDeathEvent event)
{
Player player = event.getPlayer();
Luck luck = getHandler().getLuckContainer(player);
double absorption = Math.round(Luck.RNG().nextDouble(5.0, 10.0));
if (luck.quickRNG(luck.getValue()) && doesQualify("cheat_death", luck.getValue())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("cheat_death", luck.getValue()))
{
event.setCancelled(true);
player.setHealth(1.0);
player.setAbsorptionAmount(absorption);
player.sendMessage(MiniComponent.of("You got lucky and cheated death!").send());
if (luck.isVerbose())
{
asAudience(player).sendMessage(MiniComponent.info("You got lucky and cheated death!"));
}
}
}
}

View File

@ -1,39 +1,52 @@
package io.github.simplex.luck.listener;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import java.util.List;
import java.util.Map;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.enchantment.EnchantItemEvent;
import java.util.List;
import java.util.Map;
public final class EnchantmentBoost extends AbstractListener {
public EnchantmentBoost(FeelingLucky plugin) {
public final class EnchantmentBoost extends AbstractListener
{
public EnchantmentBoost(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void enchantItem(EnchantItemEvent event) {
public void enchantItem(EnchantItemEvent event)
{
Map<Enchantment, Integer> enchMap = event.getEnchantsToAdd();
List<Enchantment> enchList = enchMap.keySet().stream().toList();
Player player = event.getEnchanter();
Luck luck = getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue()) && doesQualify("enchanting", luck.getValue())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("enchanting", luck.getValue()))
{
Enchantment particular = enchList.get(Luck.RNG().nextInt(enchList.size()));
int rng = Luck.RNG().nextInt(1, 5);
if ((enchMap.get(particular) + rng) > particular.getMaxLevel()) {
if ((enchMap.get(particular) + rng) > particular.getMaxLevel())
{
enchMap.replace(particular, particular.getMaxLevel());
}
enchMap.replace(particular, enchMap.get(particular) + rng);
if (luck.isVerbose())
{
asAudience(player).sendMessage(
MiniComponent.info("Your luck has given you an extra random enchantment."));
}
}
}
public FeelingLucky plugin() {
public FeelingLucky plugin()
{
return plugin;
}
}

View File

@ -1,27 +1,38 @@
package io.github.simplex.luck.listener;
import com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
public final class ExpBoost extends AbstractListener {
public ExpBoost(FeelingLucky plugin) {
public final class ExpBoost extends AbstractListener
{
public ExpBoost(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void boostExperienceGain(PlayerPickupExperienceEvent event) {
public void boostExperienceGain(PlayerPickupExperienceEvent event)
{
ExperienceOrb orb = event.getExperienceOrb();
int n = orb.getExperience();
int math = (5 * n ^ 2) / (2 * n + 4);
int rounded = Math.round(math);
Player player = event.getPlayer();
Luck luck = plugin.getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue()) && doesQualify("experience", luck.getValue())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("experience", luck.getValue()))
{
orb.setExperience(rounded);
if (luck.isVerbose())
{
asAudience(player).sendMessage(
MiniComponent.info("Your luck has given you extra experience!"));
}
}
}
}

View File

@ -8,20 +8,30 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
public class GiveDamage extends AbstractListener {
public GiveDamage(FeelingLucky plugin) {
public class GiveDamage extends AbstractListener
{
public GiveDamage(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void playerAttack(EntityDamageByEntityEvent e) {
public void playerAttack(EntityDamageByEntityEvent e)
{
if ((e.getDamager() instanceof Player player)
&& (e.getEntity() instanceof LivingEntity)) {
&& (e.getEntity() instanceof LivingEntity))
{
double nextDmg = e.getDamage() + Luck.RNG().nextDouble(1.0, 5.0);
Luck luck = plugin.getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("give_damage", luck.getValue()))
{
e.setDamage(nextDmg);
player.sendMessage(MiniComponent.info("Your luck has increased your damage output!"));
if (luck.isVerbose())
{
asAudience(player).sendMessage(
MiniComponent.info("Your luck gave you a critical hit!"));
}
}
}
}

View File

@ -3,57 +3,47 @@ package io.github.simplex.luck.listener;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Monster;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import java.util.*;
public class HideCheck extends AbstractListener {
public Map<Player, List<Entity>> entityMapList = new HashMap<>();
public HideCheck(FeelingLucky plugin) {
public class HideCheck extends AbstractListener
{
public HideCheck(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void initPlayerMaps(PlayerJoinEvent event) {
entityMapList.put(event.getPlayer(), List.of());
}
public void checkForSneak(PlayerToggleSneakEvent event)
{
Player player = event.getPlayer();
if (player.isSneaking())
return;
@EventHandler
public void checkTargeting(EntityTargetLivingEntityEvent event) {
if (event.getTarget() instanceof Player player) {
if (event.getEntity() instanceof LivingEntity entity) {
List<Entity> buffer = entityMapList.get(player).isEmpty() ?
new ArrayList<>() : entityMapList.get(player);
buffer.add(entity);
entityMapList.replace(player, buffer);
Luck luck = plugin.getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue()) && doesQualify("hide_check", luck.getValue()))
{
player.getNearbyEntities(25, 25, 25)
.stream()
.filter(e -> e instanceof Monster)
.map(e -> (Monster) e)
.forEach(m ->
{
final LivingEntity target = m.getTarget();
if (target != null && target.getUniqueId().equals(player.getUniqueId()))
{
m.setTarget(null);
}
});
if (luck.isVerbose())
{
asAudience(player).sendMessage(MiniComponent.info("Your luck has hidden you from sight."));
}
}
}
@EventHandler
public void checkForSneak(PlayerToggleSneakEvent event) {
Player player = event.getPlayer();
if (!player.isSneaking()) return;
Luck luck = plugin.getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue()) && !luck.isMarked(player)) {
entityMapList.get(player).forEach(e -> {
e.getTrackedPlayers().remove(player);
});
player.sendMessage(MiniComponent.info("Your luck has hidden you from sight."));
}
}
@EventHandler
public void removePlayerOnLeave(PlayerQuitEvent event) {
entityMapList.remove(event.getPlayer());
}
}

View File

@ -11,69 +11,92 @@ import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
public class IllOmen extends AbstractListener {
public IllOmen(FeelingLucky plugin) {
public class IllOmen extends AbstractListener
{
public IllOmen(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void reconnectCheck(PlayerJoinEvent event) {
public void reconnectCheck(PlayerJoinEvent event)
{
Player player = event.getPlayer();
PotionEffectType type = PotionEffectType.BAD_OMEN;
Luck luck = getHandler().getLuckContainer(player);
if (player.hasPotionEffect(type)) {
if (player.hasPotionEffect(type))
{
luck.cache();
double maths = luck.getValue() - (luck.getValue() * 0.25);
luck.setValue(maths);
player.sendMessage(MiniComponent.info("A -25% debuff has been applied to your luck from the Bad Omen status effect."));
} else if (luck.cached(player) && !player.hasPotionEffect(type)) {
asAudience(player).sendMessage(MiniComponent.info("A -25% debuff has been applied to your luck from the " +
"Bad Omen " +
"status effect."));
}
else if (luck.cached(player) && !player.hasPotionEffect(type))
{
luck.restore();
player.sendMessage("The -25% debuff to your luck has been removed.");
asAudience(player).sendMessage(MiniComponent.info("The -25% debuff to your luck has been removed."));
}
}
@EventHandler
public void effectApplyCheck(EntityPotionEffectEvent event) {
public void effectApplyCheck(EntityPotionEffectEvent event)
{
EntityPotionEffectEvent.Cause cause = EntityPotionEffectEvent.Cause.PATROL_CAPTAIN;
EntityPotionEffectEvent.Action added = EntityPotionEffectEvent.Action.ADDED;
EntityPotionEffectEvent.Action changed = EntityPotionEffectEvent.Action.CHANGED;
if (event.getCause().equals(cause) && (event.getAction().equals(added) || event.getAction().equals(changed))) {
if (event.getEntity() instanceof Player player) {
if (event.getCause().equals(cause) && (event.getAction().equals(added) || event.getAction().equals(changed)))
{
if (event.getEntity() instanceof Player player)
{
Luck luck = plugin.getHandler().getLuckContainer(player);
luck.cache();
double maths = luck.getValue() - (luck.getValue() * 0.25);
luck.setValue(maths);
player.sendMessage(MiniComponent.warn("A -25% debuff has been applied to your luck from the Bad Omen status effect."));
asAudience(player).sendMessage(
MiniComponent.warn("A -25% debuff has been applied to your luck from the Bad Omen status effect."));
}
}
}
@EventHandler
public void effectRemoveCheck(EntityPotionEffectEvent event) {
public void effectRemoveCheck(EntityPotionEffectEvent event)
{
PotionEffect old = event.getOldEffect();
EntityPotionEffectEvent.Action cleared = EntityPotionEffectEvent.Action.CLEARED;
EntityPotionEffectEvent.Action removed = EntityPotionEffectEvent.Action.REMOVED;
if (old == null) return;
if (old == null)
return;
if (old.getType().equals(PotionEffectType.BAD_OMEN) && (event.getAction().equals(cleared) || event.getAction().equals(removed))) {
if ((event.getEntity() instanceof Player player)) {
if (old.getType().equals(PotionEffectType.BAD_OMEN) && (event.getAction().equals(cleared) || event.getAction()
.equals(
removed)))
{
if ((event.getEntity() instanceof Player player))
{
Luck luck = plugin.getHandler().getLuckContainer(player);
if (luck.cached(player)) {
if (luck.cached(player))
{
luck.restore();
player.sendMessage("The -25% debuff to your luck has been removed.");
asAudience(player).sendMessage(MiniComponent.info("The -25% debuff to your luck has been removed."));
}
}
}
}
@EventHandler
public void disconnectCheck(PlayerQuitEvent event) {
if (event.getPlayer().hasPotionEffect(PotionEffectType.BAD_OMEN)) {
public void disconnectCheck(PlayerQuitEvent event)
{
if (event.getPlayer().hasPotionEffect(PotionEffectType.BAD_OMEN))
{
Luck luck = plugin.getHandler().getLuckContainer(event.getPlayer());
if (luck.cached(event.getPlayer())) {
if (luck.cached(event.getPlayer()))
{
luck.restore();
}
}

View File

@ -1,7 +1,11 @@
package io.github.simplex.luck.listener;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity;
@ -12,29 +16,32 @@ import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityDropItemEvent;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class ItemDrops extends AbstractListener {
public class ItemDrops extends AbstractListener
{
private final Map<UUID, Player> entityPlayerMap = new HashMap<>();
private boolean canAffect = false;
public ItemDrops(FeelingLucky plugin) {
public ItemDrops(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void checkForPreItemDrop(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof LivingEntity entity)) {
public void checkForPreItemDrop(EntityDamageByEntityEvent event)
{
if (!(event.getEntity() instanceof LivingEntity entity))
{
return;
}
if (!(event.getDamager() instanceof Player player)) {
if (!(event.getDamager() instanceof Player player))
{
return;
}
if (!(entity.getHealth() <= 0.0)) {
if (entity.getHealth() > 0.0)
{
return;
}
@ -42,8 +49,10 @@ public class ItemDrops extends AbstractListener {
}
@EventHandler
public void checkForDroppedItems(EntityDeathEvent event) {
if (event.getEntity() instanceof Player) {
public void checkForDroppedItems(EntityDeathEvent event)
{
if (event.getEntity() instanceof Player)
{
canAffect = false;
return;
}
@ -52,23 +61,32 @@ public class ItemDrops extends AbstractListener {
}
@EventHandler
public void itemDrops(EntityDropItemEvent event) {
public void itemDrops(EntityDropItemEvent event)
{
Entity entity = event.getEntity();
if (entityPlayerMap.get(entity.getUniqueId()) == null) return;
if (entityPlayerMap.get(entity.getUniqueId()) == null)
return;
if (!canAffect) return;
if (!canAffect)
return;
Player player = entityPlayerMap.get(entity.getUniqueId());
Luck luck = getHandler().getLuckContainer(player);
Item item = event.getItemDrop();
ItemStack stack = item.getItemStack();
int amount = stack.getAmount();
if (luck.quickRNG(luck.getValue()) && doesQualify("item_drops", luck.getValue())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("item_drops", luck.getValue()))
{
int rng = Luck.RNG().nextInt(2, 5);
amount += rng;
stack.setAmount(amount);
event.getItemDrop().setItemStack(stack);
if (luck.isVerbose())
{
asAudience(player).sendMessage(MiniComponent.info("Your luck earned you some extra loot!"));
}
}
}
}

View File

@ -11,16 +11,21 @@ import org.bukkit.util.Vector;
public class JumpBoost extends AbstractListener {
public JumpBoost(FeelingLucky plugin) {
super(plugin);
register(this);
}
@EventHandler
public void detectJumping(PlayerJumpEvent event) {
Player player = event.getPlayer(); // Player is never null; they're in game and jumping.
Luck luck = plugin.getHandler().getLuckContainer(player);
Vector velocity = player.getVelocity().clone();
if (luck.quickRNG(luck.getValue()) && !luck.isMarked(player)) {
player.setVelocity(new Vector(0, 2, 0));
player.sendMessage(MiniComponent.info("Your luck has boosted your jump height!"));
if (luck.quickRNG(luck.getValue()) && doesQualify("jump_boost", luck.getValue())) {
player.setVelocity(velocity.multiply(2.5));
if (luck.isVerbose()) {
asAudience(player).sendMessage(MiniComponent.info("Your luck gave you an extra boost to your jump!"));
}
}
}
}

View File

@ -12,40 +12,49 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockBreakEvent;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.ApiStatus;
/**
* This class is currently unstable.
*/
@Deprecated
@ApiStatus.Experimental
public class OreVein extends AbstractListener {
public OreVein(FeelingLucky plugin) {
super(plugin);
register(this);
}
@EventHandler
public void playerMine(BlockBreakEvent event) {
Player player = event.getPlayer();
Luck luck = plugin.getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue()) && event.getBlock().isValidTool(player.getInventory().getItemInMainHand())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("ore_vein", luck.getValue()) && event.getBlock().isValidTool(player.getInventory().getItemInMainHand())) {
getOresInArea(event.getBlock()).forEach(Block::breakNaturally);
player.sendMessage(MiniComponent.info("Your luck has let you mine all the blocks with one swing."));
}
}
public Stream<Block> getOresInArea(Block block) {
public List<Block> getOresInArea(Block block) {
Stream.Builder<Block> streamBuilder = Stream.builder();
Location start = block.getLocation();
World world = block.getWorld();
Stream<Tag<Material>> materialStream = Stream.of(Tag.COAL_ORES, Tag.COPPER_ORES, Tag.DIAMOND_ORES, Tag.GOLD_ORES, Tag.IRON_ORES, Tag.EMERALD_ORES, Tag.LAPIS_ORES, Tag.REDSTONE_ORES);
List<Tag<Material>> materialList = List.of(Tag.COAL_ORES, Tag.COPPER_ORES, Tag.DIAMOND_ORES, Tag.GOLD_ORES, Tag.IRON_ORES, Tag.EMERALD_ORES, Tag.LAPIS_ORES, Tag.REDSTONE_ORES);
for (int x = start.getBlockX() - 15; x <= start.getBlockX() + 15; x++) {
for (int y = start.getBlockY() - 15; y <= start.getBlockY() + 15; y++) {
for (int z = start.getBlockZ() - 15; z <= start.getBlockZ() + 15; z++) {
Location location = new Location(world, x, y, z);
Material blockType = location.getBlock().getType();
if (materialStream.anyMatch(o -> o.isTagged(blockType))) {
if (materialList.stream().anyMatch(o -> o.isTagged(blockType))) {
streamBuilder.add(location.getBlock());
}
}
}
}
return streamBuilder.build().filter(b -> b.getType().equals(block.getType()));
return streamBuilder.build().filter(b -> b.getType().equals(block.getType())).toList();
}
}

View File

@ -25,6 +25,7 @@ public final class PlayerListener extends AbstractListener {
public PlayerListener(FeelingLucky plugin) {
super(plugin);
this.timer = new CooldownTimer();
register(this);
}
@EventHandler
@ -36,22 +37,24 @@ public final class PlayerListener extends AbstractListener {
Luck luck = getHandler().getLuckContainer(player);
if (timer.onCooldown(player)) {
player.sendMessage(MiniComponent.err("That feature can only be used once every 30 seconds."));
player.sendMessage(MiniComponent.info("You have " + timer.remaining(player) + " seconds remaining."));
asAudience(player).sendMessage(MiniComponent.err("That feature can only be used once every 30 seconds."));
asAudience(player).sendMessage(MiniComponent.info("You have " + timer.remaining(player) + " seconds " +
"remaining."));
return;
}
if (action.isRightClick() && player.getInventory().getItemInMainHand().getType().equals(foot.getType())) {
if (foot.getItemMeta().equals(special.meta()) || foot.equals(special.get())) {
luck.setMultiplier(luck.multiplier() + 0.1);
player.sendMessage(MiniComponent.info("Your luck multiplier has increased by 0.1!"));
asAudience(player).sendMessage(MiniComponent.info("Your luck multiplier has increased by 0.1!"));
}
double rng = Luck.RNG().nextDouble(2.0, 5.0);
rng = Math.round(rng);
player.getInventory().remove(player.getInventory().getItemInMainHand());
luck.addTo(rng);
plugin.getHandler().updatePlayer(player, luck);
timer.setCooldown(player.getUniqueId(), System.currentTimeMillis());
player.sendMessage(MiniComponent.info("Your luck has been increased by " + rng + " points."));
asAudience(player).sendMessage(MiniComponent.info("Your luck has been increased by " + rng + " points."));
}
}
@ -74,7 +77,7 @@ public final class PlayerListener extends AbstractListener {
if (luck.quickRNG(33.0)) {
luck.takeFrom(5.0);
plugin.getHandler().updatePlayer(player, luck);
player.sendMessage(MiniComponent.warn("Your luck has been decreased by 5 points!"));
asAudience(player).sendMessage(MiniComponent.warn("Your luck has been decreased by 5 points!"));
}
}
}

View File

@ -4,21 +4,24 @@ import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
import io.github.simplex.luck.util.ListBox;
import java.util.List;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.potion.PotionEffect;
import java.util.List;
public class RandomEffect extends AbstractListener {
public RandomEffect(FeelingLucky plugin) {
public class RandomEffect extends AbstractListener
{
public RandomEffect(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void giveRandomEffect(PlayerRespawnEvent respawn) {
public void giveRandomEffect(PlayerRespawnEvent respawn)
{
Player player = respawn.getPlayer();
Luck luck = plugin.getHandler().getLuckContainer(player);
@ -26,14 +29,21 @@ public class RandomEffect extends AbstractListener {
int size = effectList.size();
PotionEffect random = effectList.get(Luck.RNG().nextInt(size - 1));
if (luck.quickRNG(luck.getValue()) && doesQualify("random_effect", luck.getValue())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("random_effect", luck.getValue()))
{
player.addPotionEffect(random);
player.sendMessage(MiniComponent.info("Thanks to luck, a random positive potion effect has been applied to you."));
if (luck.isVerbose())
{
asAudience(player).sendMessage(
MiniComponent.info("Thanks to luck, a random positive potion effect has " +
"been applied to you."));
}
}
}
@EventHandler
public void giveRandomEffect(PlayerTeleportEvent tp) {
public void giveRandomEffect(PlayerTeleportEvent tp)
{
Player player = tp.getPlayer();
Luck luck = plugin.getHandler().getLuckContainer(player);
@ -41,9 +51,14 @@ public class RandomEffect extends AbstractListener {
int size = effectList.size();
PotionEffect random = effectList.get(Luck.RNG().nextInt(size - 1));
if (luck.quickRNG(luck.getValue()) && doesQualify("random_effect", luck.getValue())) {
if (luck.quickRNG(luck.getValue()) && doesQualify("random_effect", luck.getValue()))
{
player.addPotionEffect(random);
player.sendMessage(MiniComponent.info("Thanks to luck, a random positive potion effect has been applied to you."));
if (luck.isVerbose())
{
asAudience(player).sendMessage(
MiniComponent.info("Thanks to luck, a random positive potion effect has been applied to you."));
}
}
}
}

View File

@ -1,5 +1,6 @@
package io.github.simplex.luck.listener;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.lib.PotionEffectBuilder;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
@ -10,26 +11,44 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
public class RestoreHunger extends AbstractListener {
public RestoreHunger(FeelingLucky plugin) {
public class RestoreHunger extends AbstractListener
{
public RestoreHunger(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void restoreHunger(PlayerItemConsumeEvent event) {
public void restoreHunger(PlayerItemConsumeEvent event)
{
ItemStack item = event.getItem();
Luck luck = getHandler().getLuckContainer(event.getPlayer());
PotionEffect effect = PotionEffectBuilder.newEffect().type(PotionEffectType.SATURATION).amplifier(2).duration(10).particles(false).create();
if (luck.notDefault()) {
PotionEffect effect = PotionEffectBuilder.newEffect()
.type(PotionEffectType.SATURATION)
.amplifier(2)
.duration(10)
.particles(false)
.create();
if (luck.notDefault())
{
double percentage = luck.getValue();
ListBox.foods.forEach(food -> {
if (item.isSimilar(food)) {
if (luck.quickRNG(percentage) && doesQualify("restore_hunger", percentage)) {
event.getPlayer().setExhaustion(event.getPlayer().getExhaustion() + 2);
event.getPlayer().addPotionEffect(effect);
}
}
});
ListBox.foods.forEach(food ->
{
if (item.isSimilar(food) && (luck.quickRNG(percentage) && doesQualify(
"restore_hunger", percentage)))
{
event.getPlayer().setExhaustion(event.getPlayer().getExhaustion() + 2);
event.getPlayer().addPotionEffect(effect);
}
});
if (luck.isVerbose())
{
asAudience(event.getPlayer())
.sendMessage(MiniComponent.info("Your luck has restored your hunger a little more."));
}
}
}
}

View File

@ -1,5 +1,6 @@
package io.github.simplex.luck.listener;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.lib.PotionEffectBuilder;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.player.Luck;
@ -13,6 +14,7 @@ import org.bukkit.event.entity.EntityDamageEvent;
public class TakeDamage extends AbstractListener {
public TakeDamage(FeelingLucky plugin) {
super(plugin);
register(this);
}
@EventHandler
@ -23,8 +25,7 @@ public class TakeDamage extends AbstractListener {
}
Player player = (Player) event.getEntity();
Luck luck = getHandler().getLuckContainer(player);
if (ListBox.acceptedCauses.contains(event.getCause())) {
if (luck.notDefault()) {
if (ListBox.acceptedCauses.contains(event.getCause()) && (luck.notDefault())) {
double percentage = luck.getValue();
/*
@ -36,7 +37,7 @@ public class TakeDamage extends AbstractListener {
if (luck.quickRNG(percentage)) {
event.setCancelled(true);
player.damage(event.getDamage() * 2);
player.sendMessage(Component.empty().content("You were unlucky and took double damage."));
asAudience(player).sendMessage(MiniComponent.warn("You were unlucky and took double damage!"));
}
return;
}
@ -44,13 +45,11 @@ public class TakeDamage extends AbstractListener {
if (luck.quickRNG(percentage) && doesQualify("take_damage", percentage)) {
event.setCancelled(true);
player.damage(event.getDamage() / 2);
player.sendMessage(Component.empty().content("You got lucky and took less damage."));
}
}
}
if (ListBox.sideCauses.contains(event.getCause())) {
if (luck.notDefault()) {
if (ListBox.sideCauses.contains(event.getCause()) && (luck.notDefault())) {
double percentage = luck.getValue();
/*
@ -70,9 +69,10 @@ public class TakeDamage extends AbstractListener {
event.setCancelled(true);
player.getActivePotionEffects().removeIf(p -> ListBox.potionEffects.contains(p.getType()));
player.setFireTicks(0);
player.sendMessage(Component.empty().content("You got lucky and your afflictions were cured."));
asAudience(player).sendMessage(MiniComponent.info("You got lucky and your afflictions were cured" +
"."));
}
}
}
}
}

View File

@ -11,29 +11,41 @@ import org.bukkit.inventory.CraftingInventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class UnbreakableTool extends AbstractListener {
public UnbreakableTool(FeelingLucky plugin) {
public class UnbreakableTool extends AbstractListener
{
public UnbreakableTool(FeelingLucky plugin)
{
super(plugin);
register(this);
}
@EventHandler
public void unbreakableTool(CraftItemEvent event) {
public void unbreakableTool(CraftItemEvent event)
{
CraftingInventory inventory = event.getInventory();
ItemStack stack = inventory.getResult();
if (stack == null) return;
if (stack == null)
return;
ItemMeta meta = stack.getItemMeta();
if (ItemBuilder.isTool(stack.getType())) {
if (event.getWhoClicked() instanceof Player player) {
Luck luck = getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue()) && doesQualify("unbreakable", luck.getValue())) {
meta.setUnbreakable(true);
stack.setItemMeta(meta);
inventory.setResult(stack);
player.sendMessage(MiniComponent.info("By the grace of Luck you have crafted an unbreakable tool!"));
if (ItemBuilder.isTool(stack.getType()) && (event.getWhoClicked() instanceof Player player))
{
Luck luck = getHandler().getLuckContainer(player);
if (luck.quickRNG(luck.getValue()) && doesQualify("unbreakable", luck.getValue()))
{
meta.setUnbreakable(true);
stack.setItemMeta(meta);
inventory.setResult(stack);
if (luck.isVerbose())
{
asAudience(player)
.sendMessage(
MiniComponent.info("By the grace of Luck you have crafted an unbreakable tool!"));
}
}
}
}
}

View File

@ -31,6 +31,8 @@ public class VillagerInventory extends AbstractListener {
recipe.setPriceMultiplier(1.25F);
recipe.setVillagerExperience(25);
recipe.setSpecialPrice(4);
register(this);
}
@EventHandler

View File

@ -4,10 +4,16 @@ import io.github.simplex.api.LuckContainer;
import io.github.simplex.luck.FeelingLucky;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@SuppressWarnings("all")
public class Luck implements LuckContainer {
@ -19,6 +25,7 @@ public class Luck implements LuckContainer {
private double BASE_VALUE;
private double multiplier;
private double tempSave;
private boolean verbose;
public Luck(FeelingLucky plugin, Player player) {
this(plugin, player, 1.0);
@ -34,10 +41,16 @@ public class Luck implements LuckContainer {
event = new PlayerLuckChangeEvent(this);
}
/**
* This creates a new instance of a pseudorandom number generator based off entropy provided by the operating system.
* This will allow for a much purer randomization, due to entropy being different for each call.
*
* @return A new instance of SecureRandom. Each time this method is called a new instance is created to provide maximum variation with entropic calculations.
*/
@Contract(pure = true,
value = "-> new")
public static @NotNull SplittableRandom RNG() {
return new SplittableRandom();
public static @NotNull SecureRandom RNG() {
return new SecureRandom(SecureRandom.getSeed(20));
}
public static boolean quickRNGnoMultiplier(double value) {
@ -53,6 +66,10 @@ public class Luck implements LuckContainer {
return (value >= actual);
}
public boolean playerHasLuckPE() {
return player.hasPotionEffect(PotionEffectType.LUCK);
}
public FeelingLucky getPlugin() {
return plugin;
}
@ -69,6 +86,16 @@ public class Luck implements LuckContainer {
return markedPlayers.contains(player);
}
@Override
public void setVerbose(final boolean verbose) {
this.verbose = verbose;
}
@Override
public boolean isVerbose() {
return verbose;
}
@Override
public boolean isMatch(double number) {
return getValue() == number;
@ -89,6 +116,12 @@ public class Luck implements LuckContainer {
return player;
}
/**
* Quickly calculate whether or not the player has enough luck to trigger the condition.
*
* @param value The players luck value.
* @return True if the player meets the criteria, false if they do not.
*/
public boolean quickRNG(double value) {
double rng;
if (value >= 1024.0) {
@ -97,13 +130,19 @@ public class Luck implements LuckContainer {
rng = RNG().nextDouble(0.0, 1024.0);
}
AtomicReference<Double> multiplier = new AtomicReference<>(multiplier());
double actual = Math.round((rng / 1024) * 100);
double newVal = Math.round((value / 1024) * 100);
if (multiplier() > 1.0) {
return ((value * multiplier()) >= actual);
if (playerHasLuckPE()) {
player.getActivePotionEffects()
.stream()
.filter(p -> p.getType().equals(PotionEffectType.LUCK))
.findFirst()
.ifPresent(p -> multiplier.updateAndGet(v -> (v + p.getAmplifier())));
}
return (value >= actual);
return ((newVal * multiplier.get()) >= actual);
}
public void reset() {

View File

@ -1,55 +1,38 @@
package io.github.simplex.luck.player;
import io.github.simplex.luck.FeelingLucky;
import io.github.simplex.luck.util.SneakyWorker;
import io.github.simplex.luck.util.Logs;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.attribute.Attribute;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class PlayerConfig {
public class PlayerConfig
{
private final File configFile;
private final OfflinePlayer player;
private volatile YamlConfiguration config;
private final FeelingLucky plugin;
private YamlConfiguration config;
@SuppressWarnings("ResultOfMethodCallIgnored")
public PlayerConfig(FeelingLucky plugin, Player player) {
public PlayerConfig(FeelingLucky plugin, Player player)
{
this.plugin = plugin;
this.player = player;
if (!plugin.getDataFolder().exists()) plugin.getDataFolder().mkdirs();
File dataFolder = new File(plugin.getDataFolder(), "players");
if (!dataFolder.exists()) dataFolder.mkdirs();
File file = new File(dataFolder, player.getUniqueId() + ".yml");
if (!file.exists()) {
String name = "username: " + player.getName();
String luck = "luck: " + 0;
String multiplier = "multiplier: " + 1.0;
SneakyWorker.sneakyTry(() -> {
file.createNewFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(file, StandardCharsets.UTF_8));
writer.write(name);
writer.newLine();
writer.write(luck);
writer.newLine();
writer.write(multiplier);
writer.close();
});
}
final File file = configFile(plugin, player);
configFile = file;
config = YamlConfiguration.loadConfiguration(configFile);
String tempUsername = config.getString("username");
if (tempUsername == null) {
if (tempUsername == null)
{
config.set("username", player.getName());
config.set("luck", plugin.getHandler().getLuckContainer(player).getDefaultValue());
config.set("multiplier", "1.0");
@ -57,50 +40,141 @@ public class PlayerConfig {
}
}
protected PlayerConfig(FeelingLucky plugin, File file) {
protected PlayerConfig(FeelingLucky plugin, File file)
{
this.plugin = plugin;
this.configFile = file;
this.player = Bukkit.getOfflinePlayer(UUID.fromString(file.getName().split("\\.")[0]));
config = YamlConfiguration.loadConfiguration(configFile);
}
@Contract("_, _ -> new")
public static PlayerConfig initFrom(FeelingLucky plugin, File file) {
public static PlayerConfig initFrom(FeelingLucky plugin, File file)
{
return new PlayerConfig(plugin, file);
}
public void save() {
SneakyWorker.sneakyTry(() -> config.save(configFile));
@NotNull
private File configFile(FeelingLucky plugin, Player player)
{
if (!plugin.getDataFolder().exists())
plugin.getDataFolder().mkdirs();
File dataFolder = new File(plugin.getDataFolder(), "players");
if (!dataFolder.exists())
dataFolder.mkdirs();
File file = new File(dataFolder, player.getUniqueId() + ".yml");
if (!file.exists())
{
try
{
file.createNewFile();
final YamlConfiguration v0 = new YamlConfiguration();
v0.set("username", player.getName());
v0.set("luck", 0);
v0.set("multiplier", 1.0);
v0.set("verbose", true);
v0.save(file);
}
catch (IOException ex)
{
Logs.error(ex);
}
}
return file;
}
public void load() {
SneakyWorker.sneakyTry(() -> config = YamlConfiguration.loadConfiguration(configFile));
public void save()
{
try
{
config.save(configFile);
}
catch (IOException ex)
{
Logs.error(ex);
}
}
public void reload() {
public void load()
{
try
{
config.load(configFile);
}
catch (IOException | InvalidConfigurationException ex)
{
Logs.error(ex);
Logs.warn("Attempting to reinitialize variable... this is dangerous!");
try
{
config = YamlConfiguration.loadConfiguration(configFile);
}
catch (IllegalArgumentException th)
{
Logs.error(th);
}
}
}
public void reload()
{
save();
load();
}
public OfflinePlayer getPlayer() {
public OfflinePlayer getPlayer()
{
return player;
}
public void setUsername(String name) {
config.set("username", name);
save();
public String getUsername() {
return config.getString("username");
}
public void setLuck(double luck) {
public double getLuck()
{
return config.getDouble("luck");
}
public void setLuck(double luck)
{
config.set("luck", luck);
save();
reload();
}
public void setMultiplier(double multiplier) {
public double getMultiplier()
{
return config.getDouble("multiplier");
}
public void setMultiplier(double multiplier)
{
config.set("multiplier", multiplier);
save();
reload();
}
public YamlConfiguration getConfig() {
public boolean isVerbose()
{
return config.getBoolean("verbose");
}
public void setVerbose(final boolean verbose)
{
config.set("verbose", verbose);
reload();
}
public void setUsername(String name)
{
config.set("username", name);
reload();
}
public YamlConfiguration getConfig()
{
return config;
}
}

View File

@ -38,9 +38,10 @@ public class PlayerHandler implements Listener {
plugin.getConfigMap().put(player.getUniqueId(), playerConfig);
}
String username = playerConfig.getConfig().getString("username");
double luck = playerConfig.getConfig().getDouble("luck");
double multiplier = playerConfig.getConfig().getDouble("multiplier");
String username = playerConfig.getUsername();
double luck = playerConfig.getLuck();
double multiplier = playerConfig.getMultiplier();
boolean verbose = playerConfig.isVerbose();
if (!player.getName().equalsIgnoreCase(username)) {
playerConfig.getConfig().set("username", player.getName());
@ -49,6 +50,7 @@ public class PlayerHandler implements Listener {
}
Luck container = new Luck(plugin, player, multiplier);
container.setVerbose(verbose);
container.setValue(luck);
playerLuckMap.put(player, container);

View File

@ -0,0 +1,52 @@
package io.github.simplex.luck.util;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Logs
{
private static final Logger logger = LoggerFactory.getLogger("FeelingLucky");
private Logs()
{
throw new AssertionError();
}
public static void info(String message)
{
logger.info(message);
}
public static void warn(String message)
{
logger.warn(message);
}
public static void warn(String message, Throwable th)
{
logger.warn(message, th);
}
public static void warn(Throwable th)
{
final String msg = ExceptionUtils.getRootCauseMessage(th);
logger.warn(msg);
}
public static void error(String message)
{
logger.error(message);
}
public static void error(String message, Throwable th)
{
logger.error(message, th);
}
public static void error(Throwable th)
{
final String msg = ExceptionUtils.getRootCauseMessage(th);
logger.error(msg, th);
}
}

View File

@ -22,12 +22,16 @@ public class LuckCMD extends Command implements TabCompleter, PluginIdentifiable
super("luck", "FeelingLucky main command.", "/<command> <info | set | reset | give | take> [player] [amount]", List.of());
this.plugin = plugin;
setPermission("luck.default");
register(plugin.getServer().getCommandMap());
plugin.getCommandMap().register("luck", "FeelingLucky", this);
plugin.getLogger().info("Successfully registered command: Luck");
}
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
if (args.length < 1 || args.length > 3) return false;
if (args.length < 1 || args.length > 3) {
sender.sendMessage(this.getUsage());
return false;
}
if (args.length == 3) {
if ((sender instanceof ConsoleCommandSender) || sender.hasPermission("luck.admin")) {
@ -65,21 +69,21 @@ public class LuckCMD extends Command implements TabCompleter, PluginIdentifiable
luck.setValue(amount);
plugin.getHandler().updatePlayer(player, luck);
config.setLuck(luck.getValue());
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
sender.sendMessage(MiniComponent.info("Successfully set " + args[1] + "'s Luck stat to " + amount + "."));
return true;
}
case "give" -> {
luck.addTo(amount);
plugin.getHandler().updatePlayer(player, luck);
config.setLuck(luck.getValue());
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
sender.sendMessage(MiniComponent.info("Successfully gave " + args[1] + " " + amount + " points of luck!"));
return true;
}
case "take" -> {
luck.takeFrom(amount);
plugin.getHandler().updatePlayer(player, luck);
config.setLuck(luck.getValue());
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
sender.sendMessage(MiniComponent.info("Successfully took " + amount + " points of luck from " + args[1]));
return true;
}
}
@ -110,6 +114,17 @@ public class LuckCMD extends Command implements TabCompleter, PluginIdentifiable
return true;
}
if (args[0].equalsIgnoreCase("verbose") && sender instanceof Player player) {
final boolean a1 = Boolean.parseBoolean(args[1]);
Luck luck = plugin.getHandler().getLuckContainer(player);
PlayerConfig config = plugin.getConfigMap().get(player.getUniqueId());
luck.setVerbose(a1);
plugin.getHandler().updatePlayer(player, luck);
config.setVerbose(a1);
sender.sendMessage(MiniComponent.info("Verbose mode set to " + a1 + "."));
return true;
}
if (args[0].equalsIgnoreCase("reset")) {
Player player = Bukkit.getPlayer(args[1]);

View File

@ -0,0 +1,46 @@
package io.github.simplex.luck.util;
import io.github.simplex.lib.MiniComponent;
import io.github.simplex.luck.FeelingLucky;
import org.bukkit.command.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class RegenerateConfigCMD extends Command implements TabCompleter, PluginIdentifiableCommand {
private final FeelingLucky plugin;
public RegenerateConfigCMD(FeelingLucky plugin) {
super("rgc", "Regenerate this plugin's config file.", "/<command>", List.of());
this.plugin = plugin;
setPermission("luck.rgc");
plugin.getCommandMap().register("rgc", "FeelingLucky", this);
plugin.getLogger().info("Successfully registered command: RGC.");
}
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
if (!(sender instanceof ConsoleCommandSender)) {
sender.sendMessage(MiniComponent.err("This command can only be used through console access."));
return true;
}
plugin.saveResource("config.yml", true);
plugin.getConfig().load();
plugin.getLogger().info("Configuration regenerated.");
return true;
}
@Override
public @NotNull FeelingLucky getPlugin() {
return plugin;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
return new ArrayList<>();
}
}

View File

@ -21,7 +21,7 @@ public class SneakyWorker {
+ ex.getClass().getSimpleName()
+ " has occurred. A cause will be printed. \n\n"
+ ex.getCause();
Bukkit.getLogger().severe(sb);
Logs.error(sb);
}
}

View File

@ -19,7 +19,11 @@ bonemeal: MED
cheat_death: MED
enchanting: HIGH
experience: HIGH
give_damage: LOW
hide_check: MED
item_drops: LOW
jump_boost: MED
ore_vein: HIGH
random_effect: HIGH
restore_hunger: NONE
take_damage: MED