mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2025-04-02 18:25:50 +02:00
Work on /npc shop, QoL tweaks, fix scoreboard team removal
This commit is contained in:
parent
ddf5660c66
commit
9cf7779ff4
@ -34,7 +34,6 @@ import net.citizensnpcs.api.CitizensPlugin;
|
||||
import net.citizensnpcs.api.InventoryHelper;
|
||||
import net.citizensnpcs.api.SkullMetaProvider;
|
||||
import net.citizensnpcs.api.ai.speech.SpeechFactory;
|
||||
import net.citizensnpcs.api.command.CommandContext;
|
||||
import net.citizensnpcs.api.command.CommandManager;
|
||||
import net.citizensnpcs.api.command.CommandManager.CommandInfo;
|
||||
import net.citizensnpcs.api.command.Injector;
|
||||
@ -71,6 +70,7 @@ import net.citizensnpcs.npc.ai.speech.Chat;
|
||||
import net.citizensnpcs.npc.ai.speech.CitizensSpeechFactory;
|
||||
import net.citizensnpcs.npc.profile.ProfileFetcher;
|
||||
import net.citizensnpcs.npc.skin.Skin;
|
||||
import net.citizensnpcs.trait.ShopTrait;
|
||||
import net.citizensnpcs.util.Messages;
|
||||
import net.citizensnpcs.util.NMS;
|
||||
import net.citizensnpcs.util.PlayerUpdateTask;
|
||||
@ -103,6 +103,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
private boolean saveOnDisable = true;
|
||||
private NPCDataStore saves;
|
||||
private NPCSelector selector;
|
||||
private Storage shops;
|
||||
private final SkullMetaProvider skullMetaProvider = new SkullMetaProvider() {
|
||||
@Override
|
||||
public String getTexture(SkullMeta meta) {
|
||||
@ -305,8 +306,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
}
|
||||
|
||||
public void onDependentPluginDisable() {
|
||||
storeNPCs();
|
||||
saves.saveToDiskImmediate();
|
||||
storeNPCs(false);
|
||||
saveOnDisable = false;
|
||||
}
|
||||
|
||||
@ -347,7 +347,8 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
registerScriptHelpers();
|
||||
|
||||
saves = createStorage(getDataFolder());
|
||||
if (saves == null) {
|
||||
shops = new YamlStorage(new File(getDataFolder(), "shops.yml"));
|
||||
if (saves == null || !shops.load()) {
|
||||
Messaging.severeTr(Messages.FAILED_LOAD_SAVES);
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
@ -420,6 +421,9 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
saves.reloadFromSource();
|
||||
saves.loadInto(npcRegistry);
|
||||
|
||||
shops.load();
|
||||
ShopTrait.loadShops(shops.getKey(""));
|
||||
|
||||
getServer().getPluginManager().callEvent(new CitizensReloadEvent());
|
||||
}
|
||||
|
||||
@ -504,17 +508,21 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
}
|
||||
|
||||
public void storeNPCs() {
|
||||
storeNPCs(false);
|
||||
}
|
||||
|
||||
public void storeNPCs(boolean async) {
|
||||
if (saves == null)
|
||||
return;
|
||||
saves.storeAll(npcRegistry);
|
||||
}
|
||||
|
||||
public void storeNPCs(CommandContext args) {
|
||||
storeNPCs();
|
||||
boolean async = args.hasFlag('a');
|
||||
ShopTrait.saveShops(shops.getKey(""));
|
||||
if (async) {
|
||||
saves.saveToDisk();
|
||||
new Thread(() -> {
|
||||
shops.save();
|
||||
}).start();
|
||||
} else {
|
||||
shops.save();
|
||||
saves.saveToDiskImmediate();
|
||||
}
|
||||
}
|
||||
@ -536,6 +544,8 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
Messaging.logTr(Messages.NUM_LOADED_NOTIFICATION, Iterables.size(npcRegistry), "?");
|
||||
startMetrics();
|
||||
scheduleSaveTask(Setting.SAVE_TASK_DELAY.asInt());
|
||||
shops.load();
|
||||
ShopTrait.loadShops(shops.getKey(""));
|
||||
Bukkit.getPluginManager().callEvent(new CitizensEnableEvent());
|
||||
new PlayerUpdateTask().runTaskTimer(Citizens.this, 0, 1);
|
||||
enabled = true;
|
||||
@ -545,8 +555,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
|
||||
private class CitizensSaveTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
storeNPCs();
|
||||
saves.saveToDisk();
|
||||
storeNPCs(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ public class AdminCommands {
|
||||
permission = "citizens.admin")
|
||||
public void save(CommandContext args, CommandSender sender, NPC npc) {
|
||||
Messaging.sendTr(sender, Messages.CITIZENS_SAVING);
|
||||
plugin.storeNPCs(args);
|
||||
plugin.storeNPCs(args.hasFlag('a'));
|
||||
Messaging.sendTr(sender, Messages.CITIZENS_SAVED);
|
||||
}
|
||||
}
|
@ -1191,7 +1191,7 @@ public class NPCCommands {
|
||||
int id = npcs.get(i).getId();
|
||||
String line = StringHelper.wrap(id) + " " + npcs.get(i).getName() + " (<<[[tp:command(/npc tp --id " + id
|
||||
+ "):Teleport to this NPC>>) (<<[[summon:command(/npc tph --id " + id
|
||||
+ "):Teleport NPC to me>> (<<<c>-:command(/npc remove " + id + "):Remove this NPC>>)";
|
||||
+ "):Teleport NPC to me>>) (<<<c>-:command(/npc remove " + id + "):Remove this NPC>>)";
|
||||
paginator.addLine(line);
|
||||
}
|
||||
|
||||
@ -2567,11 +2567,12 @@ public class NPCCommands {
|
||||
|
||||
@Command(
|
||||
aliases = { "npc" },
|
||||
usage = "tp",
|
||||
desc = "Teleport to a NPC",
|
||||
usage = "tp (-e(xact))",
|
||||
desc = "Teleport in front of an NPC",
|
||||
modifiers = { "tp", "teleport" },
|
||||
min = 1,
|
||||
max = 1,
|
||||
flags = "e",
|
||||
permission = "citizens.npc.tp")
|
||||
public void tp(CommandContext args, Player player, NPC npc) {
|
||||
Location to = npc.getOrAddTrait(CurrentLocation.class).getLocation();
|
||||
@ -2579,6 +2580,10 @@ public class NPCCommands {
|
||||
Messaging.sendError(player, Messages.TELEPORT_NPC_LOCATION_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
if (!args.hasFlag('e')) {
|
||||
to = to.clone().add(to.getDirection().setY(0));
|
||||
to.setDirection(to.getDirection().multiply(-1)).setPitch(0);
|
||||
}
|
||||
player.teleport(to, TeleportCause.COMMAND);
|
||||
Messaging.sendTr(player, Messages.TELEPORTED_TO_NPC, npc.getName());
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.citizensnpcs.trait;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
@ -28,6 +29,7 @@ import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
@ -324,6 +326,14 @@ public class CommandTrait extends Trait {
|
||||
commands.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(DataKey key) {
|
||||
Collection<NPCCommand> commands = this.commands.values();
|
||||
for (PlayerNPCCommand playerCommand : cooldowns.values()) {
|
||||
playerCommand.prune(globalCooldowns, commands);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendErrorMessage(Player player, CommandTraitMessages msg, Function<String, String> transform,
|
||||
Object... objects) {
|
||||
if (hideErrorMessages) {
|
||||
@ -445,6 +455,7 @@ public class CommandTrait extends Trait {
|
||||
int globalCooldown;
|
||||
Hand hand;
|
||||
int id;
|
||||
String key;
|
||||
int n;
|
||||
boolean op;
|
||||
List<String> perms;
|
||||
@ -466,6 +477,12 @@ public class CommandTrait extends Trait {
|
||||
this.bungeeServer = split.size() == 2 && split.get(0).equalsIgnoreCase("server") ? split.get(1) : null;
|
||||
}
|
||||
|
||||
public String getEncodedKey() {
|
||||
if (key != null)
|
||||
return key;
|
||||
return key = BaseEncoding.base64().encode(command.getBytes());
|
||||
}
|
||||
|
||||
public void run(NPC npc, Player clicker) {
|
||||
String cmd = command;
|
||||
if (command.startsWith("say")) {
|
||||
@ -628,7 +645,7 @@ public class CommandTrait extends Trait {
|
||||
}
|
||||
}
|
||||
long currentTimeSec = System.currentTimeMillis() / 1000;
|
||||
String commandKey = BaseEncoding.base64().encode(command.command.getBytes());
|
||||
String commandKey = command.getEncodedKey();
|
||||
if (lastUsed.containsKey(commandKey)) {
|
||||
long deadline = ((Number) lastUsed.get(commandKey)).longValue() + command.cooldown;
|
||||
if (currentTimeSec < deadline) {
|
||||
@ -667,6 +684,34 @@ public class CommandTrait extends Trait {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void prune(Map<String, Long> globalCooldowns, Collection<NPCCommand> commands) {
|
||||
long currentTimeSec = System.currentTimeMillis() / 1000;
|
||||
Set<String> commandKeys = Sets.newHashSet();
|
||||
for (NPCCommand command : commands) {
|
||||
String commandKey = command.getEncodedKey();
|
||||
commandKeys.add(commandKey);
|
||||
Number number = lastUsed.get(commandKey);
|
||||
if (number != null && number.longValue() + command.cooldown <= currentTimeSec) {
|
||||
lastUsed.remove(commandKey);
|
||||
}
|
||||
if (globalCooldowns != null) {
|
||||
number = globalCooldowns.get(commandKey);
|
||||
if (number != null && number.longValue() + command.globalCooldown <= currentTimeSec) {
|
||||
globalCooldowns.remove(commandKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String key : Sets.difference(Sets.newHashSet(lastUsed.keySet()), commandKeys)) {
|
||||
lastUsed.remove(key);
|
||||
nUsed.remove(key);
|
||||
}
|
||||
if (globalCooldowns != null) {
|
||||
for (String key : Sets.difference(Sets.newHashSet(globalCooldowns.keySet()), commandKeys)) {
|
||||
globalCooldowns.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean requiresTracking(NPCCommand command) {
|
||||
return command.globalCooldown > 0 || command.cooldown > 0 || command.n > 0
|
||||
|| (command.perms != null && command.perms.size() > 0);
|
||||
|
@ -87,6 +87,7 @@ public class ScoreboardTrait extends Trait {
|
||||
if (team.hasEntry(name)) {
|
||||
if (team.getSize() == 1) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
SENT_TEAMS.remove(player.getUniqueId(), team.getName());
|
||||
NMS.sendTeamPacket(player, team, 1);
|
||||
}
|
||||
team.unregister();
|
||||
|
@ -3,6 +3,7 @@ package net.citizensnpcs.trait;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -26,15 +27,20 @@ import net.citizensnpcs.api.gui.InputMenus;
|
||||
import net.citizensnpcs.api.gui.InputMenus.Choice;
|
||||
import net.citizensnpcs.api.gui.InventoryMenu;
|
||||
import net.citizensnpcs.api.gui.InventoryMenuPage;
|
||||
import net.citizensnpcs.api.gui.InventoryMenuPattern;
|
||||
import net.citizensnpcs.api.gui.InventoryMenuSlot;
|
||||
import net.citizensnpcs.api.gui.Menu;
|
||||
import net.citizensnpcs.api.gui.MenuContext;
|
||||
import net.citizensnpcs.api.gui.MenuPattern;
|
||||
import net.citizensnpcs.api.gui.MenuSlot;
|
||||
import net.citizensnpcs.api.persistence.Persist;
|
||||
import net.citizensnpcs.api.persistence.PersistenceLoader;
|
||||
import net.citizensnpcs.api.trait.Trait;
|
||||
import net.citizensnpcs.api.trait.TraitName;
|
||||
import net.citizensnpcs.api.util.Colorizer;
|
||||
import net.citizensnpcs.api.util.DataKey;
|
||||
import net.citizensnpcs.trait.shop.NPCShopAction;
|
||||
import net.citizensnpcs.trait.shop.NPCShopAction.GUI;
|
||||
|
||||
/**
|
||||
* Shop trait for NPC GUI shops.
|
||||
@ -46,11 +52,11 @@ public class ShopTrait extends Trait {
|
||||
}
|
||||
|
||||
public NPCShop getDefaultShop() {
|
||||
return NPC_SHOPS.computeIfAbsent(npc.getUniqueId().toString(), NPCShop::new);
|
||||
return StoredShops.NPC_SHOPS.computeIfAbsent(npc.getUniqueId().toString(), NPCShop::new);
|
||||
}
|
||||
|
||||
public NPCShop getShop(String name) {
|
||||
return SHOPS.computeIfAbsent(name, NPCShop::new);
|
||||
return StoredShops.GLOBAL_SHOPS.computeIfAbsent(name, NPCShop::new);
|
||||
}
|
||||
|
||||
public static class NPCShop {
|
||||
@ -200,11 +206,36 @@ public class ShopTrait extends Trait {
|
||||
|
||||
public static class NPCShopItem implements Cloneable {
|
||||
@Persist
|
||||
private List<NPCShopAction> cost;
|
||||
private final List<NPCShopAction> cost = Lists.newArrayList();
|
||||
@Persist
|
||||
private ItemStack display;
|
||||
@Persist
|
||||
private List<NPCShopAction> result;
|
||||
private final List<NPCShopAction> result = Lists.newArrayList();
|
||||
|
||||
private void changeAction(List<NPCShopAction> source, Function<NPCShopAction, Boolean> filter,
|
||||
NPCShopAction delta) {
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
if (filter.apply(source.get(i))) {
|
||||
if (delta == null) {
|
||||
source.remove(i);
|
||||
} else {
|
||||
source.set(i, delta);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (delta != null) {
|
||||
source.add(delta);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeCost(Function<NPCShopAction, Boolean> filter, NPCShopAction cost) {
|
||||
changeAction(this.cost, filter, cost);
|
||||
}
|
||||
|
||||
public void changeResult(Function<NPCShopAction, Boolean> filter, NPCShopAction result) {
|
||||
changeAction(this.result, filter, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NPCShopItem clone() {
|
||||
@ -220,9 +251,19 @@ public class ShopTrait extends Trait {
|
||||
}
|
||||
|
||||
@Menu(title = "NPC Shop Item Editor", type = InventoryType.CHEST, dimensions = { 6, 9 })
|
||||
@MenuSlot(slot = { 0, 4 }, material = Material.DISPENSER, amount = 1, title = "Place display item below")
|
||||
@MenuSlot(slot = { 3, 4 }, material = Material.DISPENSER, amount = 1, title = "<f>Place display item below")
|
||||
public static class NPCShopItemEditor extends InventoryMenuPage {
|
||||
@MenuPattern(
|
||||
offset = { 0, 0 },
|
||||
slots = { @MenuSlot(pat = 'x', material = Material.AIR) },
|
||||
value = "x x\n x \nx x")
|
||||
private InventoryMenuPattern actionItems;
|
||||
private final Consumer<NPCShopItem> callback;
|
||||
@MenuPattern(
|
||||
offset = { 0, 6 },
|
||||
slots = { @MenuSlot(pat = 'x', material = Material.AIR) },
|
||||
value = "x x\n x \nx x")
|
||||
private InventoryMenuPattern costItems;
|
||||
private MenuContext ctx;
|
||||
private final NPCShopItem modified;
|
||||
private NPCShopItem original;
|
||||
@ -239,9 +280,34 @@ public class ShopTrait extends Trait {
|
||||
if (modified.display != null) {
|
||||
ctx.getSlot(9 + 4).setItemStack(modified.display);
|
||||
}
|
||||
int pos = 0;
|
||||
for (GUI template : NPCShopAction.getGUIs()) {
|
||||
ItemStack item = template.createMenuItem();
|
||||
if (item == null)
|
||||
continue;
|
||||
costItems.getSlots().get(pos).setItemStack(item);
|
||||
costItems.getSlots().get(pos).addClickHandler(event -> {
|
||||
event.setCancelled(true);
|
||||
ctx.getMenu()
|
||||
.transition(template.createEditor(
|
||||
modified.cost.stream().filter(template::manages).findFirst().orElse(null),
|
||||
cost -> modified.changeCost(template::manages, cost)));
|
||||
});
|
||||
|
||||
actionItems.getSlots().get(pos).setItemStack(item);
|
||||
actionItems.getSlots().get(pos).addClickHandler(event -> {
|
||||
event.setCancelled(true);
|
||||
ctx.getMenu()
|
||||
.transition(template.createEditor(
|
||||
modified.result.stream().filter(template::manages).findFirst().orElse(null),
|
||||
result -> modified.changeResult(template::manages, result)));
|
||||
});
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
@MenuSlot(slot = { 4, 3 }, material = Material.REDSTONE_BLOCK, amount = 1, title = "Cancel")
|
||||
@MenuSlot(slot = { 5, 3 }, material = Material.REDSTONE_BLOCK, amount = 1, title = "<7>Cancel")
|
||||
public void onCancel(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
||||
event.setCancelled(true);
|
||||
ctx.getMenu().transitionBack();
|
||||
@ -255,20 +321,23 @@ public class ShopTrait extends Trait {
|
||||
callback.accept(original);
|
||||
}
|
||||
|
||||
@MenuSlot(slot = { 1, 5 }, material = Material.BOOK, amount = 1, title = "Set description")
|
||||
@MenuSlot(slot = { 4, 5 }, material = Material.BOOK, amount = 1, title = "<f>Set description")
|
||||
public void onEditDescription(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
||||
event.setCancelled(true);
|
||||
if (modified.display == null)
|
||||
return;
|
||||
ctx.getMenu().transition(InputMenus.stringSetter(
|
||||
() -> Joiner.on("<br>").skipNulls().join(modified.display.getItemMeta().getLore()), description -> {
|
||||
ItemMeta meta = modified.display.getItemMeta();
|
||||
meta.setLore(Lists.newArrayList(Splitter.on("<br>").split(Colorizer.parseColors(description))));
|
||||
modified.display.setItemMeta(meta);
|
||||
}));
|
||||
ctx.getMenu()
|
||||
.transition(InputMenus.stringSetter(() -> modified.display.getItemMeta().hasLore()
|
||||
? Joiner.on("<br>").skipNulls().join(modified.display.getItemMeta().getLore())
|
||||
: "", description -> {
|
||||
ItemMeta meta = modified.display.getItemMeta();
|
||||
meta.setLore(Lists
|
||||
.newArrayList(Splitter.on("<br>").split(Colorizer.parseColors(description))));
|
||||
modified.display.setItemMeta(meta);
|
||||
}));
|
||||
}
|
||||
|
||||
@MenuSlot(slot = { 1, 3 }, material = Material.FEATHER, amount = 1, title = "Set name")
|
||||
@MenuSlot(slot = { 4, 3 }, material = Material.FEATHER, amount = 1, title = "<f>Set name")
|
||||
public void onEditName(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
||||
event.setCancelled(true);
|
||||
if (modified.display == null)
|
||||
@ -280,7 +349,7 @@ public class ShopTrait extends Trait {
|
||||
}));
|
||||
}
|
||||
|
||||
@ClickHandler(slot = { 1, 4 })
|
||||
@ClickHandler(slot = { 4, 4 })
|
||||
public void onModifyDisplayItem(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
||||
event.setCancelled(true);
|
||||
if (event.getCursor() != null) {
|
||||
@ -292,14 +361,14 @@ public class ShopTrait extends Trait {
|
||||
}
|
||||
}
|
||||
|
||||
@MenuSlot(slot = { 4, 4 }, material = Material.TNT, amount = 1, title = "<c>Remove")
|
||||
@MenuSlot(slot = { 5, 4 }, material = Material.TNT, amount = 1, title = "<c>Remove")
|
||||
public void onRemove(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
||||
original = null;
|
||||
event.setCancelled(true);
|
||||
ctx.getMenu().transitionBack();
|
||||
}
|
||||
|
||||
@MenuSlot(slot = { 4, 5 }, material = Material.EMERALD_BLOCK, amount = 1, title = "Save")
|
||||
@MenuSlot(slot = { 5, 5 }, material = Material.EMERALD_BLOCK, amount = 1, title = "<a>Save")
|
||||
public void onSave(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
||||
original = modified;
|
||||
event.setCancelled(true);
|
||||
@ -485,8 +554,20 @@ public class ShopTrait extends Trait {
|
||||
SELL;
|
||||
}
|
||||
|
||||
@Persist(value = "npcShops", reify = true, namespace = "shopstrait")
|
||||
private static Map<String, NPCShop> NPC_SHOPS = Maps.newHashMap();
|
||||
@Persist(value = "globalShops", reify = true, namespace = "shopstrait")
|
||||
private static Map<String, NPCShop> SHOPS = Maps.newHashMap();
|
||||
private static class StoredShops {
|
||||
@Persist(value = "global", reify = true)
|
||||
private static Map<String, NPCShop> GLOBAL_SHOPS = Maps.newHashMap();
|
||||
@Persist(value = "npc", reify = true)
|
||||
private static Map<String, NPCShop> NPC_SHOPS = Maps.newHashMap();
|
||||
}
|
||||
|
||||
public static void loadShops(DataKey root) {
|
||||
SAVED = PersistenceLoader.load(StoredShops.class, root);
|
||||
}
|
||||
|
||||
public static void saveShops(DataKey root) {
|
||||
PersistenceLoader.save(SAVED, root);
|
||||
}
|
||||
|
||||
private static StoredShops SAVED = new StoredShops();
|
||||
}
|
@ -95,7 +95,6 @@ public class SkinTrait extends Trait {
|
||||
textureRaw = npc.data().get(NPC.PLAYER_SKIN_TEXTURE_PROPERTIES_METADATA);
|
||||
npc.data().remove(NPC.PLAYER_SKIN_TEXTURE_PROPERTIES_METADATA);
|
||||
update = true;
|
||||
|
||||
}
|
||||
if (npc.data().has(NPC.PLAYER_SKIN_TEXTURE_PROPERTIES_SIGN_METADATA)) {
|
||||
signature = npc.data().get(NPC.PLAYER_SKIN_TEXTURE_PROPERTIES_SIGN_METADATA);
|
||||
|
@ -2,15 +2,31 @@ package net.citizensnpcs.trait.shop;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import net.citizensnpcs.api.gui.InputMenus;
|
||||
import net.citizensnpcs.api.gui.InventoryMenuPage;
|
||||
import net.citizensnpcs.api.persistence.Persist;
|
||||
import net.citizensnpcs.api.persistence.PersistenceLoader;
|
||||
import net.citizensnpcs.api.persistence.PersisterRegistry;
|
||||
import net.citizensnpcs.trait.shop.NPCShopAction.ItemAction.ItemActionGUI;
|
||||
import net.citizensnpcs.trait.shop.NPCShopAction.MoneyAction.MoneyActionGUI;
|
||||
import net.citizensnpcs.trait.shop.NPCShopAction.PermissionAction.PermissionActionGUI;
|
||||
import net.citizensnpcs.util.Util;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
|
||||
public abstract class NPCShopAction implements Cloneable {
|
||||
@Override
|
||||
@ -22,9 +38,17 @@ public abstract class NPCShopAction implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean grant(HumanEntity entity);
|
||||
public abstract PendingAction grant(Entity entity);
|
||||
|
||||
public abstract boolean take(HumanEntity entity);
|
||||
public abstract PendingAction take(Entity entity);
|
||||
|
||||
public static interface GUI {
|
||||
public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback);
|
||||
|
||||
public ItemStack createMenuItem();
|
||||
|
||||
public boolean manages(NPCShopAction action);
|
||||
}
|
||||
|
||||
public static class ItemAction extends NPCShopAction {
|
||||
@Persist
|
||||
@ -42,35 +66,151 @@ public abstract class NPCShopAction implements Cloneable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean grant(HumanEntity entity) {
|
||||
return false;
|
||||
public PendingAction grant(Entity entity) {
|
||||
return PendingAction.create(() -> {
|
||||
return true;
|
||||
}, () -> {
|
||||
}, () -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean take(HumanEntity entity) {
|
||||
return false;
|
||||
public PendingAction take(Entity entity) {
|
||||
return PendingAction.create(() -> {
|
||||
return true;
|
||||
}, () -> {
|
||||
}, () -> {
|
||||
});
|
||||
}
|
||||
|
||||
public static class ItemActionGUI implements GUI {
|
||||
@Override
|
||||
public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createMenuItem() {
|
||||
return Util.createItem(Material.CHEST, "Item");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean manages(NPCShopAction action) {
|
||||
return action instanceof ItemAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MoneyAction extends NPCShopAction {
|
||||
@Persist
|
||||
public int money;
|
||||
public double money;
|
||||
|
||||
public MoneyAction() {
|
||||
}
|
||||
|
||||
public MoneyAction(int money) {
|
||||
this.money = money;
|
||||
@Override
|
||||
public PendingAction grant(Entity entity) {
|
||||
if (!(entity instanceof OfflinePlayer))
|
||||
return PendingAction.fail();
|
||||
Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider();
|
||||
OfflinePlayer player = (OfflinePlayer) entity;
|
||||
return PendingAction.create(() -> {
|
||||
return true;
|
||||
}, () -> {
|
||||
economy.depositPlayer(player, money);
|
||||
}, () -> {
|
||||
economy.withdrawPlayer(player, money);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean grant(HumanEntity entity) {
|
||||
return false;
|
||||
public PendingAction take(Entity entity) {
|
||||
if (!(entity instanceof OfflinePlayer))
|
||||
return PendingAction.fail();
|
||||
Economy economy = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider();
|
||||
OfflinePlayer player = (OfflinePlayer) entity;
|
||||
return PendingAction.create(() -> {
|
||||
return economy.has(player, money);
|
||||
}, () -> {
|
||||
economy.withdrawPlayer(player, money);
|
||||
}, () -> {
|
||||
economy.depositPlayer(player, money);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean take(HumanEntity entity) {
|
||||
return false;
|
||||
public static class MoneyActionGUI implements GUI {
|
||||
private Boolean supported;
|
||||
|
||||
@Override
|
||||
public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback) {
|
||||
final MoneyAction action = previous == null ? new MoneyAction() : (MoneyAction) previous;
|
||||
return InputMenus.filteredStringSetter(() -> Double.toString(action.money), (s) -> {
|
||||
try {
|
||||
double result = Double.parseDouble(s);
|
||||
if (result < 0)
|
||||
return false;
|
||||
action.money = result;
|
||||
} catch (NumberFormatException nfe) {
|
||||
return false;
|
||||
}
|
||||
callback.accept(action);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createMenuItem() {
|
||||
if (supported == null) {
|
||||
try {
|
||||
supported = Bukkit.getServicesManager().getRegistration(Economy.class).getProvider() != null;
|
||||
} catch (Throwable t) {
|
||||
supported = false;
|
||||
}
|
||||
}
|
||||
if (!supported) {
|
||||
return null;
|
||||
}
|
||||
return Util.createItem(Material.GOLD_INGOT, "Money");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean manages(NPCShopAction action) {
|
||||
return action instanceof MoneyAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PendingAction {
|
||||
private final Runnable execute;
|
||||
private final Supplier<Boolean> possible;
|
||||
private final Runnable rollback;
|
||||
|
||||
public PendingAction(Supplier<Boolean> isPossible, Runnable execute, Runnable rollback) {
|
||||
this.possible = isPossible;
|
||||
this.execute = execute;
|
||||
this.rollback = rollback;
|
||||
}
|
||||
|
||||
public boolean isPossible() {
|
||||
return possible.get();
|
||||
}
|
||||
|
||||
public void rollback() {
|
||||
rollback.run();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
execute.run();
|
||||
}
|
||||
|
||||
public static PendingAction create(Supplier<Boolean> isPossible, Runnable execute, Runnable rollback) {
|
||||
return new PendingAction(isPossible, execute, rollback);
|
||||
}
|
||||
|
||||
public static PendingAction fail() {
|
||||
return new PendingAction(() -> false, () -> {
|
||||
}, () -> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,20 +226,93 @@ public abstract class NPCShopAction implements Cloneable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean grant(HumanEntity entity) {
|
||||
return false;
|
||||
public PendingAction grant(Entity entity) {
|
||||
if (!(entity instanceof Player))
|
||||
return PendingAction.fail();
|
||||
Player player = (Player) entity;
|
||||
Permission perm = Bukkit.getServicesManager().getRegistration(Permission.class).getProvider();
|
||||
return PendingAction.create(() -> {
|
||||
return true;
|
||||
}, () -> {
|
||||
for (String permission : permissions) {
|
||||
perm.playerAdd(player, permission);
|
||||
}
|
||||
}, () -> {
|
||||
for (String permission : permissions) {
|
||||
perm.playerRemove(player, permission);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean take(HumanEntity entity) {
|
||||
return false;
|
||||
public PendingAction take(Entity entity) {
|
||||
if (!(entity instanceof Player))
|
||||
return PendingAction.fail();
|
||||
Player player = (Player) entity;
|
||||
Permission perm = Bukkit.getServicesManager().getRegistration(Permission.class).getProvider();
|
||||
return PendingAction.create(() -> {
|
||||
for (String permission : permissions) {
|
||||
if (!perm.playerHas(player, permission)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, () -> {
|
||||
for (String permission : permissions) {
|
||||
perm.playerRemove(player, permission);
|
||||
}
|
||||
}, () -> {
|
||||
for (String permission : permissions) {
|
||||
perm.playerAdd(player, permission);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class PermissionActionGUI implements GUI {
|
||||
private Boolean supported;
|
||||
|
||||
@Override
|
||||
public InventoryMenuPage createEditor(NPCShopAction previous, Consumer<NPCShopAction> callback) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createMenuItem() {
|
||||
if (supported == null) {
|
||||
try {
|
||||
supported = Bukkit.getServicesManager().getRegistration(Permission.class).getProvider() != null;
|
||||
} catch (Throwable t) {
|
||||
supported = false;
|
||||
}
|
||||
}
|
||||
if (!supported) {
|
||||
return null;
|
||||
}
|
||||
return Util.createItem(Util.getFallbackMaterial("OAK_SIGN", "SIGN"), "Permission");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean manages(NPCShopAction action) {
|
||||
return action instanceof PermissionAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PersisterRegistry<NPCShopAction> REGISTRY = PersistenceLoader.createRegistry(NPCShopAction.class);
|
||||
public static Iterable<GUI> getGUIs() {
|
||||
return GUI.values();
|
||||
}
|
||||
|
||||
public static void register(Class<? extends NPCShopAction> clazz, String type, GUI gui) {
|
||||
REGISTRY.register(type, clazz);
|
||||
GUI.put(clazz, gui);
|
||||
}
|
||||
|
||||
private static final Map<Class<? extends NPCShopAction>, GUI> GUI = new WeakHashMap<>();
|
||||
private static final PersisterRegistry<NPCShopAction> REGISTRY = PersistenceLoader
|
||||
.createRegistry(NPCShopAction.class);
|
||||
static {
|
||||
REGISTRY.register("items", ItemAction.class);
|
||||
REGISTRY.register("permissions", PermissionAction.class);
|
||||
REGISTRY.register("money", MoneyAction.class);
|
||||
register(ItemAction.class, "items", new ItemActionGUI());
|
||||
register(PermissionAction.class, "permissions", new PermissionActionGUI());
|
||||
register(MoneyAction.class, "money", new MoneyActionGUI());
|
||||
}
|
||||
}
|
@ -90,7 +90,8 @@ public class FoxTrait extends Trait {
|
||||
if (args.hasValueFlag("type")) {
|
||||
Fox.Type type = Util.matchEnum(Fox.Type.values(), args.getFlag("type"));
|
||||
if (type == null) {
|
||||
throw new CommandUsageException(Messages.INVALID_FOX_TYPE, Util.listValuesPretty(Fox.Type.values()));
|
||||
throw new CommandUsageException(
|
||||
Messaging.tr(Messages.INVALID_FOX_TYPE, Util.listValuesPretty(Fox.Type.values())), null);
|
||||
}
|
||||
trait.setType(type);
|
||||
output += ' ' + Messaging.tr(Messages.FOX_TYPE_SET, args.getFlag("type"));
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.citizensnpcs.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
@ -16,6 +17,9 @@ import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@ -26,7 +30,9 @@ import net.citizensnpcs.api.event.NPCCollisionEvent;
|
||||
import net.citizensnpcs.api.event.NPCPushEvent;
|
||||
import net.citizensnpcs.api.npc.NPC;
|
||||
import net.citizensnpcs.api.util.BoundingBox;
|
||||
import net.citizensnpcs.api.util.Colorizer;
|
||||
import net.citizensnpcs.api.util.SpigotUtil;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
public class Util {
|
||||
private Util() {
|
||||
@ -69,6 +75,22 @@ public class Util {
|
||||
return angle;
|
||||
}
|
||||
|
||||
public static ItemStack createItem(Material mat, String name) {
|
||||
return createItem(mat, name, null);
|
||||
}
|
||||
|
||||
public static ItemStack createItem(Material mat, String name, String description) {
|
||||
ItemStack stack = new ItemStack(mat, 1);
|
||||
ItemMeta meta = stack.getItemMeta();
|
||||
meta.setDisplayName(ChatColor.RESET + Colorizer.parseColors(name));
|
||||
if (description != null) {
|
||||
meta.setLore(Arrays.asList(Colorizer.parseColors(description).split("\n")));
|
||||
}
|
||||
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
|
||||
stack.setItemMeta(meta);
|
||||
return stack;
|
||||
}
|
||||
|
||||
public static void face(Entity entity, float yaw, float pitch) {
|
||||
double pitchCos = Math.cos(Math.toRadians(pitch));
|
||||
Vector vector = new Vector(Math.sin(Math.toRadians(yaw)) * -pitchCos, -Math.sin(Math.toRadians(pitch)),
|
||||
|
Loading…
Reference in New Issue
Block a user