From 9bbe9ae3c480b106953c5621103d3b36ad8cc068 Mon Sep 17 00:00:00 2001 From: fullwall Date: Sun, 7 Feb 2021 18:45:10 +0800 Subject: [PATCH] Add simple /npc drops GUI and add NPC selection to /npc follow --- .../citizensnpcs/commands/NPCCommands.java | 41 ++++- .../npc/CitizensTraitFactory.java | 2 + .../net/citizensnpcs/trait/CommandTrait.java | 19 +-- .../net/citizensnpcs/trait/DropsTrait.java | 146 ++++++++++++++++++ 4 files changed, 187 insertions(+), 21 deletions(-) create mode 100644 main/src/main/java/net/citizensnpcs/trait/DropsTrait.java diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index 6b6562e6d..70ef00789 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -93,6 +93,7 @@ import net.citizensnpcs.trait.CommandTrait.ItemRequirementGUI; import net.citizensnpcs.trait.CommandTrait.NPCCommandBuilder; import net.citizensnpcs.trait.Controllable; import net.citizensnpcs.trait.CurrentLocation; +import net.citizensnpcs.trait.DropsTrait; import net.citizensnpcs.trait.EndermanTrait; import net.citizensnpcs.trait.FollowTrait; import net.citizensnpcs.trait.GameModeTrait; @@ -310,7 +311,6 @@ public class NPCCommands { Messaging.sendTr(sender, npc.data(). get(NPC.COLLIDABLE_METADATA) ? Messages.COLLIDABLE_SET : Messages.COLLIDABLE_UNSET, npc.getName()); - } @Command( @@ -604,6 +604,20 @@ public class NPCCommands { } } + @Command( + aliases = { "npc" }, + usage = "drops", + desc = "Edit an NPC's drops", + modifiers = { "drops" }, + min = 1, + max = 1, + permission = "citizens.npc.drops") + @Requirements(ownership = true, selected = true) + public void drops(CommandContext args, Player sender, NPC npc) throws CommandException { + DropsTrait trait = npc.getOrAddTrait(DropsTrait.class); + trait.displayEditor(sender); + } + @Command( aliases = { "npc" }, usage = "enderman -a[ngry]", @@ -644,7 +658,7 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "follow (player name) (-p[rotect])", + usage = "follow (player name|NPC id) (-p[rotect])", desc = "Toggles NPC following you", flags = "p", modifiers = { "follow" }, @@ -657,9 +671,30 @@ public class NPCCommands { if (args.argsLength() > 1) { name = args.getString(1); } + OfflinePlayer player = Bukkit.getOfflinePlayer(name); if (player == null) { - throw new CommandException(); + NPCCommandSelector.Callback callback = new NPCCommandSelector.Callback() { + @Override + public void run(NPC followingNPC) throws CommandException { + if (followingNPC == null) + throw new CommandException(Messages.COMMAND_MUST_HAVE_SELECTED); + if (!(sender instanceof ConsoleCommandSender) + && !followingNPC.getOrAddTrait(Owner.class).isOwnedBy(sender)) + throw new CommandException(Messages.COMMAND_MUST_BE_OWNER); + if (followingNPC.getEntity() instanceof Player) { + boolean following = followingNPC.getOrAddTrait(FollowTrait.class) + .toggle((Player) followingNPC.getEntity(), protect); + Messaging.sendTr(sender, following ? Messages.FOLLOW_SET : Messages.FOLLOW_UNSET, npc.getName(), + followingNPC.getName()); + } else { + throw new CommandException(); + } + } + }; + NPCCommandSelector.startWithCallback(callback, CitizensAPI.getNPCRegistry(), sender, args, + args.getString(1)); + return; } boolean following = npc.getOrAddTrait(FollowTrait.class).toggle(player, protect); Messaging.sendTr(sender, following ? Messages.FOLLOW_SET : Messages.FOLLOW_UNSET, npc.getName(), diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java index 0159caf87..35d18d25b 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java @@ -28,6 +28,7 @@ import net.citizensnpcs.trait.ClickRedirectTrait; import net.citizensnpcs.trait.CommandTrait; import net.citizensnpcs.trait.Controllable; import net.citizensnpcs.trait.CurrentLocation; +import net.citizensnpcs.trait.DropsTrait; import net.citizensnpcs.trait.EndermanTrait; import net.citizensnpcs.trait.FollowTrait; import net.citizensnpcs.trait.GameModeTrait; @@ -66,6 +67,7 @@ public class CitizensTraitFactory implements TraitFactory { registerTrait(TraitInfo.create(CommandTrait.class)); registerTrait(TraitInfo.create(Controllable.class)); registerTrait(TraitInfo.create(CurrentLocation.class)); + registerTrait(TraitInfo.create(DropsTrait.class)); registerTrait(TraitInfo.create(EndermanTrait.class)); registerTrait(TraitInfo.create(Equipment.class)); registerTrait(TraitInfo.create(FollowTrait.class)); diff --git a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java index 9eede0afb..02d4f1f27 100644 --- a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java @@ -37,7 +37,6 @@ import net.citizensnpcs.api.gui.MenuContext; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.persistence.DelegatePersistence; import net.citizensnpcs.api.persistence.Persist; -import net.citizensnpcs.api.persistence.PersistenceLoader; import net.citizensnpcs.api.persistence.Persister; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.TraitName; @@ -55,8 +54,7 @@ public class CommandTrait extends Trait { @Persist @DelegatePersistence(NPCCommandPersister.class) private final Map commands = Maps.newHashMap(); - @Persist - @DelegatePersistence(PlayerNPCCommandPersister.class) + @Persist(reify = true) private final Map cooldowns = Maps.newHashMap(); @Persist private double cost = -1; @@ -599,19 +597,4 @@ public class CommandTrait extends Trait { return command.cooldown > 0 || command.n > 0 || (command.perms != null && command.perms.size() > 0); } } - - private static class PlayerNPCCommandPersister implements Persister { - public PlayerNPCCommandPersister() { - } - - @Override - public PlayerNPCCommand create(DataKey root) { - return PersistenceLoader.load(PlayerNPCCommand.class, root); - } - - @Override - public void save(PlayerNPCCommand instance, DataKey root) { - PersistenceLoader.save(instance, root); - } - } } \ No newline at end of file diff --git a/main/src/main/java/net/citizensnpcs/trait/DropsTrait.java b/main/src/main/java/net/citizensnpcs/trait/DropsTrait.java new file mode 100644 index 000000000..5c5eaae34 --- /dev/null +++ b/main/src/main/java/net/citizensnpcs/trait/DropsTrait.java @@ -0,0 +1,146 @@ +package net.citizensnpcs.trait; + +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.event.NPCDeathEvent; +import net.citizensnpcs.api.gui.InventoryMenu; +import net.citizensnpcs.api.gui.InventoryMenuPage; +import net.citizensnpcs.api.gui.InventoryMenuSlot; +import net.citizensnpcs.api.gui.Menu; +import net.citizensnpcs.api.gui.MenuContext; +import net.citizensnpcs.api.gui.PercentageSlotHandler; +import net.citizensnpcs.api.persistence.Persist; +import net.citizensnpcs.api.trait.Trait; +import net.citizensnpcs.api.trait.TraitName; +import net.citizensnpcs.util.Util; + +@TraitName("dropstrait") +public class DropsTrait extends Trait { + @Persist(reify = true) + private List drops = Lists.newArrayList(); + + public DropsTrait() { + super("dropstrait"); + } + + public void displayEditor(Player sender) { + InventoryMenu.createSelfRegistered(new DropsGUI(this)).present(sender); + } + + @EventHandler + public void onNPCDeath(NPCDeathEvent event) { + if (!event.getNPC().equals(npc)) + return; + Random random = Util.getFastRandom(); + for (ItemDrop drop : drops) { + if (random.nextDouble() < drop.chance) { + event.getDrops().add(drop.drop); + } + } + } + + @Menu(title = "Add items for drops", type = InventoryType.CHEST, dimensions = { 5, 9 }) + public static class DropsGUI extends InventoryMenuPage { + private final Map chances = Maps.newHashMap(); + private Inventory inventory; + private int taskId; + private DropsTrait trait; + + private DropsGUI() { + throw new UnsupportedOperationException(); + } + + public DropsGUI(DropsTrait trait) { + this.trait = trait; + } + + @Override + public void initialise(MenuContext ctx) { + this.taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(CitizensAPI.getPlugin(), this, 0, 1); + this.inventory = ctx.getInventory(); + int k = 0; + for (int i = 1; i < 5; i += 2) { + for (int j = 0; j < 9; j++) { + int islot = (i - 1) * 9 + j; + + ItemDrop drop; + int chance = 100; + if (k < trait.drops.size()) { + drop = trait.drops.get(k++); + chance = (int) Math.floor(drop.chance * 100.0); + chances.put(islot, drop.chance); + ctx.getInventory().setItem(islot, drop.drop.clone()); + } + InventoryMenuSlot slot = ctx.getSlot(i * 9 + j); + slot.setItemStack(new ItemStack(Material.GLASS_PANE), "Drop chance " + chance + "%"); + slot.addClickHandler(new PercentageSlotHandler((pct) -> { + if (chances.containsKey(islot)) { + chances.put(islot, pct / 100.0); + } + return "Drop chance " + pct + "%"; + }, chance)); + } + } + } + + @Override + public void onClick(InventoryMenuSlot slot, InventoryClickEvent event) { + if (event.getAction().name().contains("PICKUP")) { + chances.remove(event.getSlot()); + } else if (event.getAction().name().contains("PLACE")) { + chances.putIfAbsent(event.getSlot(), 1.0); + } + } + + @Override + public void onClose(HumanEntity player) { + Bukkit.getScheduler().cancelTask(taskId); + } + + @Override + public void run() { + List drops = Lists.newArrayList(); + for (int i = 0; i < 5; i += 2) { + for (int j = 0; j < 9; j++) { + int slot = i * 9 + j; + ItemStack stack = inventory.getItem(slot); + if (stack == null || stack.getType() == Material.AIR) + continue; + drops.add(new ItemDrop(stack, chances.getOrDefault(slot, 1.0))); + } + } + this.trait.drops = drops; + } + } + + private static class ItemDrop { + @Persist + double chance; + @Persist + ItemStack drop; + + public ItemDrop() { + } + + public ItemDrop(ItemStack drop, double chance) { + this.drop = drop; + this.chance = chance; + } + } +} \ No newline at end of file