diff --git a/pom.xml b/pom.xml index c971f15..2bbec56 100644 --- a/pom.xml +++ b/pom.xml @@ -4,19 +4,18 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - example-expansion + pinata 1.0.0-SNAPSHOT jar - ExampleExpansion - An Example expansion for GamesInTheBox + Pinata + A mob to punch - me.hsgamer.gamesinthebox.exampleexpansion.ExampleExpansion + me.hsgamer.gamesinthebox.pinata.Pinata true - jitpack.io @@ -36,28 +35,4 @@ provided - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/me/hsgamer/gamesinthebox/exampleexpansion/ExampleExpansion.java b/src/main/java/me/hsgamer/gamesinthebox/exampleexpansion/ExampleExpansion.java deleted file mode 100644 index cb9ac4f..0000000 --- a/src/main/java/me/hsgamer/gamesinthebox/exampleexpansion/ExampleExpansion.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.hsgamer.gamesinthebox.exampleexpansion; - -import me.hsgamer.hscore.expansion.common.Expansion; - -public class ExampleExpansion implements Expansion { - @Override - public boolean onLoad() { - return true; - } - - @Override - public void onEnable() { - // Do something - } - - @Override - public void onDisable() { - // Do something - } -} diff --git a/src/main/java/me/hsgamer/gamesinthebox/pinata/GameArenaLogic.java b/src/main/java/me/hsgamer/gamesinthebox/pinata/GameArenaLogic.java new file mode 100644 index 0000000..144b199 --- /dev/null +++ b/src/main/java/me/hsgamer/gamesinthebox/pinata/GameArenaLogic.java @@ -0,0 +1,89 @@ +package me.hsgamer.gamesinthebox.pinata; + +import me.hsgamer.gamesinthebox.game.feature.BoundingFeature; +import me.hsgamer.gamesinthebox.game.feature.PointFeature; +import me.hsgamer.gamesinthebox.game.simple.feature.SimpleBoundingFeature; +import me.hsgamer.gamesinthebox.game.simple.feature.SimpleBoundingOffsetFeature; +import me.hsgamer.gamesinthebox.game.simple.feature.SimpleRewardFeature; +import me.hsgamer.gamesinthebox.game.template.TemplateGameArena; +import me.hsgamer.gamesinthebox.game.template.TemplateGameArenaLogic; +import me.hsgamer.gamesinthebox.pinata.feature.ListenerFeature; +import me.hsgamer.gamesinthebox.pinata.feature.PinataFeature; +import me.hsgamer.gamesinthebox.pinata.feature.SpawnFeature; +import me.hsgamer.minigamecore.base.Feature; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class GameArenaLogic extends TemplateGameArenaLogic { + private final Pinata expansion; + + public GameArenaLogic(Pinata expansion, TemplateGameArena arena) { + super(arena); + this.expansion = expansion; + } + + @Override + public void forceEnd() { + PinataFeature pinataFeature = arena.getFeature(PinataFeature.class); + pinataFeature.setClearAllEntities(false); + pinataFeature.stopTask(); + pinataFeature.clearAllEntities(); + + arena.getFeature(ListenerFeature.class).unregister(); + } + + @Override + public List loadFeatures() { + SimpleBoundingFeature boundingFeature = new SimpleBoundingFeature(arena); + return Arrays.asList( + boundingFeature, + new SimpleBoundingOffsetFeature(arena, boundingFeature, true), + new PinataFeature(arena), + new ListenerFeature(expansion, arena), + new SpawnFeature(arena) + ); + } + + @Override + public void postInit() { + BoundingFeature boundingFeature = arena.getFeature(BoundingFeature.class); + arena.getFeature(PinataFeature.class).addEntityClearCheck(entity -> !boundingFeature.checkBounding(entity.getLocation(), true)); + } + + @Override + public void onInGameStart() { + arena.getFeature(ListenerFeature.class).register(); + arena.getFeature(PinataFeature.class).startTask(); + } + + @Override + public void onInGameUpdate() { + arena.getFeature(SpawnFeature.class).checkAndSpawn(); + } + + @Override + public void onEndingStart() { + List topList = arena.getFeature(PointFeature.class).getTopUUID().collect(Collectors.toList()); + arena.getFeature(SimpleRewardFeature.class).tryReward(topList); + + arena.getFeature(PinataFeature.class).setClearAllEntities(true); + } + + @Override + public boolean isEndingOver() { + return super.isEndingOver() && arena.getFeature(PinataFeature.class).isAllEntityCleared(); + } + + @Override + public void onEndingOver() { + PinataFeature pinataFeature = arena.getFeature(PinataFeature.class); + pinataFeature.setClearAllEntities(false); + pinataFeature.stopTask(); + pinataFeature.clearAllEntities(); + + arena.getFeature(ListenerFeature.class).unregister(); + } +} diff --git a/src/main/java/me/hsgamer/gamesinthebox/pinata/GameEditor.java b/src/main/java/me/hsgamer/gamesinthebox/pinata/GameEditor.java new file mode 100644 index 0000000..f38eb47 --- /dev/null +++ b/src/main/java/me/hsgamer/gamesinthebox/pinata/GameEditor.java @@ -0,0 +1,207 @@ +package me.hsgamer.gamesinthebox.pinata; + +import com.google.common.base.Enums; +import me.hsgamer.gamesinthebox.game.GameArena; +import me.hsgamer.gamesinthebox.game.simple.action.ValueAction; +import me.hsgamer.gamesinthebox.game.simple.feature.SimpleBoundingFeature; +import me.hsgamer.gamesinthebox.game.simple.feature.SimpleBoundingOffsetFeature; +import me.hsgamer.gamesinthebox.game.template.TemplateGame; +import me.hsgamer.gamesinthebox.game.template.TemplateGameArenaLogic; +import me.hsgamer.gamesinthebox.game.template.TemplateGameEditor; +import me.hsgamer.gamesinthebox.game.template.feature.ArenaLogicFeature; +import me.hsgamer.gamesinthebox.pinata.feature.ListenerFeature; +import me.hsgamer.gamesinthebox.pinata.feature.PinataFeature; +import me.hsgamer.hscore.bukkit.utils.MessageUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.stream.Collectors; + +public class GameEditor extends TemplateGameEditor { + private final SimpleBoundingFeature.Editor simpleBoundingFeatureEditor = SimpleBoundingFeature.editor(true); + private final SimpleBoundingOffsetFeature.Editor simpleBoundingOffsetFeatureEditor = SimpleBoundingOffsetFeature.editor(); + private final List nameTags = new ArrayList<>(); + private EntityType entityType = EntityType.SHEEP; + private boolean damageAsScore = false; + + public GameEditor(@NotNull TemplateGame game) { + super(game); + } + + @Override + protected @NotNull Map createActionMap() { + Map map = super.createActionMap(); + + map.putAll(simpleBoundingFeatureEditor.getActions()); + map.putAll(simpleBoundingOffsetFeatureEditor.getActions()); + + map.put("add-name-tag", new SimpleAction() { + @Override + public @NotNull String getDescription() { + return "Add a name tag to the list"; + } + + @Override + public boolean performAction(@NotNull CommandSender sender, @NotNull String... args) { + if (args.length == 0) { + return false; + } + nameTags.add(String.join(" ", args)); + return true; + } + + @Override + public @NotNull String getArgsUsage() { + return ""; + } + }); + map.put("clear-name-tags", new SimpleAction() { + @Override + public @NotNull String getDescription() { + return "Clear the name tags list"; + } + + @Override + public boolean performAction(@NotNull CommandSender sender, @NotNull String... args) { + nameTags.clear(); + return true; + } + }); + map.put("set-pinata-type", new ValueAction() { + @Override + public @NotNull String getDescription() { + return "Set the pinata type"; + } + + @Override + protected boolean performAction(@NotNull CommandSender sender, @NotNull EntityType value, String... args) { + entityType = value; + return true; + } + + @Override + protected int getValueArgCount() { + return 1; + } + + @Override + protected Optional parseValue(@NotNull CommandSender sender, String... args) { + return Optional.ofNullable(args[0]) + .map(String::toUpperCase) + .flatMap(s -> Enums.getIfPresent(EntityType.class, s).toJavaUtil()) + .filter(EntityType::isAlive); + } + + @Override + protected @NotNull List getValueArgs(@NotNull CommandSender sender, String... args) { + return Arrays.stream(EntityType.values()) + .filter(EntityType::isAlive) + .map(Enum::name) + .collect(Collectors.toList()); + } + }); + map.put("set-damage-as-score", new ValueAction() { + @Override + protected boolean performAction(@NotNull CommandSender sender, @NotNull Boolean value, String... args) { + damageAsScore = value; + return true; + } + + @Override + protected int getValueArgCount() { + return 1; + } + + @Override + protected Optional parseValue(@NotNull CommandSender sender, String... args) { + return Optional.of(Boolean.parseBoolean(args[0])); + } + + @Override + protected @NotNull List getValueArgs(@NotNull CommandSender sender, String... args) { + return Arrays.asList("true", "false"); + } + + @Override + public @NotNull String getDescription() { + return "Set whether to use damage as score"; + } + + @Override + public @NotNull String getArgsUsage() { + return ""; + } + }); + + return map; + } + + @Override + protected @NotNull List<@NotNull SimpleEditorStatus> createEditorStatusList() { + List<@NotNull SimpleEditorStatus> list = super.createEditorStatusList(); + list.add(simpleBoundingFeatureEditor.getStatus()); + list.add(simpleBoundingOffsetFeatureEditor.getStatus()); + list.add(new SimpleEditorStatus() { + @Override + public void sendStatus(@NotNull CommandSender sender) { + MessageUtils.sendMessage(sender, "&6&lPinata"); + MessageUtils.sendMessage(sender, "&6Type: &f" + entityType.name()); + MessageUtils.sendMessage(sender, "&6Damage As Score: &f" + damageAsScore); + MessageUtils.sendMessage(sender, "&6Name Tags: "); + nameTags.forEach(nameTag -> MessageUtils.sendMessage(sender, "&f- " + nameTag)); + } + + @Override + public void reset(@NotNull CommandSender sender) { + nameTags.clear(); + entityType = EntityType.SHEEP; + damageAsScore = false; + } + + @Override + public boolean canSave(@NotNull CommandSender sender) { + return true; + } + + @Override + public Map toPathValueMap(@NotNull CommandSender sender) { + Map map = new LinkedHashMap<>(); + if (!nameTags.isEmpty()) { + map.put("pinata.name-tag", nameTags); + } + if (entityType != EntityType.SHEEP) { + map.put("pinata.type", entityType.name()); + } + if (damageAsScore) { + map.put("damage-as-score", true); + } + return map; + } + }); + return list; + } + + @Override + public boolean migrate(@NotNull CommandSender sender, @NotNull GameArena gameArena) { + ArenaLogicFeature arenaLogicFeature = gameArena.getFeature(ArenaLogicFeature.class); + if (arenaLogicFeature == null) { + return false; + } + TemplateGameArenaLogic templateGameArenaLogic = arenaLogicFeature.getArenaLogic(); + if (!(templateGameArenaLogic instanceof GameArenaLogic)) { + return false; + } + + nameTags.clear(); + nameTags.addAll(gameArena.getFeature(PinataFeature.class).getNameTags()); + + entityType = gameArena.getFeature(PinataFeature.class).getEntityType(); + damageAsScore = gameArena.getFeature(ListenerFeature.class).isDamageAsScore(); + + simpleBoundingFeatureEditor.migrate(gameArena.getFeature(SimpleBoundingFeature.class)); + simpleBoundingOffsetFeatureEditor.migrate(gameArena.getFeature(SimpleBoundingOffsetFeature.class)); + return super.migrate(sender, gameArena); + } +} diff --git a/src/main/java/me/hsgamer/gamesinthebox/pinata/MessageConfig.java b/src/main/java/me/hsgamer/gamesinthebox/pinata/MessageConfig.java new file mode 100644 index 0000000..cff6f9e --- /dev/null +++ b/src/main/java/me/hsgamer/gamesinthebox/pinata/MessageConfig.java @@ -0,0 +1,43 @@ +package me.hsgamer.gamesinthebox.pinata; + +import me.hsgamer.hscore.config.annotation.ConfigPath; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +public interface MessageConfig { + @ConfigPath("display-name") + default String getDisplayName() { + return "Pinata"; + } + + @ConfigPath("default-hologram-lines") + default Map getDefaultHologramLines() { + Map map = new LinkedHashMap<>(); + map.put("description", Arrays.asList( + "&c&lPINATA", + "&fA pinata will be spawned at the game arena", + "&fYou need to hit it to get points", + "&fThe player with the most points will win" + )); + map.put("points", Collections.singletonList( + "&fPoints when you hit the pinata: &a{game_point_hit}" + )); + map.put("top", Arrays.asList( + "&a#1 &f{game_top_name_1} &7- &f{game_top_value_1}", + "&a#2 &f{game_top_name_2} &7- &f{game_top_value_2}", + "&a#3 &f{game_top_name_3} &7- &f{game_top_value_3}", + "&a#4 &f{game_top_name_4} &7- &f{game_top_value_4}", + "&a#5 &f{game_top_name_5} &7- &f{game_top_value_5}" + )); + map.put("status", Arrays.asList( + "&fStatus: &a{planner_game_state}", + "&fTime left: &a{game_time_left}" + )); + return map; + } + + void reloadConfig(); +} diff --git a/src/main/java/me/hsgamer/gamesinthebox/pinata/Pinata.java b/src/main/java/me/hsgamer/gamesinthebox/pinata/Pinata.java new file mode 100644 index 0000000..ba65e9f --- /dev/null +++ b/src/main/java/me/hsgamer/gamesinthebox/pinata/Pinata.java @@ -0,0 +1,66 @@ +package me.hsgamer.gamesinthebox.pinata; + +import me.hsgamer.gamesinthebox.game.simple.feature.SimplePointFeature; +import me.hsgamer.gamesinthebox.game.template.TemplateGame; +import me.hsgamer.gamesinthebox.game.template.TemplateGameArena; +import me.hsgamer.gamesinthebox.game.template.TemplateGameArenaLogic; +import me.hsgamer.gamesinthebox.game.template.TemplateGameEditor; +import me.hsgamer.gamesinthebox.game.template.expansion.TemplateGameExpansion; +import me.hsgamer.gamesinthebox.util.UpdateUtil; +import me.hsgamer.hscore.bukkit.config.BukkitConfig; +import me.hsgamer.hscore.common.CollectionUtils; +import me.hsgamer.hscore.config.proxy.ConfigGenerator; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class Pinata extends TemplateGameExpansion { + public static final SimplePointFeature.PointValue POINT_HIT = new SimplePointFeature.PointValue("hit", 1, false); + private final MessageConfig messageConfig = ConfigGenerator.newInstance(MessageConfig.class, new BukkitConfig(new File(getDataFolder(), "messages.yml"))); + + @Override + protected @NotNull String @NotNull [] getGameType() { + return new String[]{"pinata"}; + } + + @Override + public TemplateGameArenaLogic createArenaLogic(TemplateGameArena templateGameArena) { + return new GameArenaLogic(this, templateGameArena); + } + + @Override + public TemplateGameEditor getEditor(TemplateGame game) { + return new GameEditor(game); + } + + @Override + public List getPointValues() { + return Collections.singletonList(POINT_HIT); + } + + @Override + public List getDefaultHologramLines(String name) { + return Optional.ofNullable(messageConfig.getDefaultHologramLines().get(name)) + .map(CollectionUtils::createStringListFromObject) + .orElseGet(() -> super.getDefaultHologramLines(name)); + } + + @Override + public String getDisplayName() { + return messageConfig.getDisplayName(); + } + + @Override + public void onReload() { + super.onReload(); + messageConfig.reloadConfig(); + } + + @Override + protected void enable() { + UpdateUtil.notifyUpdate(this, "GamesInTheBox-MC/Pinata"); + } +} diff --git a/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/ListenerFeature.java b/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/ListenerFeature.java new file mode 100644 index 0000000..a105d2a --- /dev/null +++ b/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/ListenerFeature.java @@ -0,0 +1,87 @@ +package me.hsgamer.gamesinthebox.pinata.feature; + +import me.hsgamer.gamesinthebox.game.feature.GameConfigFeature; +import me.hsgamer.gamesinthebox.game.simple.SimpleGameArena; +import me.hsgamer.gamesinthebox.game.simple.feature.SimplePointFeature; +import me.hsgamer.gamesinthebox.pinata.Pinata; +import me.hsgamer.minigamecore.base.Feature; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; + +import java.util.Optional; + +public class ListenerFeature implements Feature, Listener { + private final Pinata expansion; + private final SimpleGameArena arena; + private PinataFeature pinataFeature; + private SimplePointFeature pointFeature; + private boolean damageAsScore = false; + + public ListenerFeature(Pinata expansion, SimpleGameArena arena) { + this.expansion = expansion; + this.arena = arena; + } + + @Override + public void init() { + this.pinataFeature = this.arena.getFeature(PinataFeature.class); + this.pointFeature = this.arena.getFeature(SimplePointFeature.class); + } + + @Override + public void postInit() { + GameConfigFeature gameConfigFeature = arena.getFeature(GameConfigFeature.class); + + if (gameConfigFeature != null) { + damageAsScore = Optional.ofNullable(gameConfigFeature.getString("damage-as-score")) + .map(Boolean::parseBoolean) + .orElse(false); + } + } + + public void register() { + Bukkit.getPluginManager().registerEvents(this, expansion.getPlugin()); + } + + public void unregister() { + HandlerList.unregisterAll(this); + } + + public boolean isDamageAsScore() { + return damageAsScore; + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPinataDamage(EntityDamageByEntityEvent event) { + Entity entity = event.getEntity(); + if (!pinataFeature.contains(entity)) return; + event.setCancelled(true); + + Entity damager = event.getDamager(); + if (damager instanceof Player) { + Player player = (Player) damager; + + if (damageAsScore) { + pointFeature.applyPoint(player.getUniqueId(), (int) event.getFinalDamage()); + } else { + pointFeature.applyPoint(player.getUniqueId(), Pinata.POINT_HIT); + } + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPinataDeath(EntityDeathEvent event) { + Entity entity = event.getEntity(); + if (!pinataFeature.contains(entity)) return; + + event.setDroppedExp(0); + event.getDrops().clear(); + } +} diff --git a/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/PinataFeature.java b/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/PinataFeature.java new file mode 100644 index 0000000..c684b02 --- /dev/null +++ b/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/PinataFeature.java @@ -0,0 +1,63 @@ +package me.hsgamer.gamesinthebox.pinata.feature; + +import com.google.common.base.Enums; +import me.hsgamer.gamesinthebox.game.feature.EntityFeature; +import me.hsgamer.gamesinthebox.game.feature.GameConfigFeature; +import me.hsgamer.gamesinthebox.game.simple.SimpleGameArena; +import me.hsgamer.gamesinthebox.util.Util; +import me.hsgamer.hscore.common.CollectionUtils; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class PinataFeature extends EntityFeature { + private final SimpleGameArena arena; + private List nameTags = Collections.emptyList(); + private EntityType entityType = EntityType.SHEEP; + + public PinataFeature(SimpleGameArena arena) { + this.arena = arena; + } + + @Override + public void postInit() { + GameConfigFeature config = arena.getFeature(GameConfigFeature.class); + + nameTags = Optional.ofNullable(config.get("pinata.name-tag")) + .map(CollectionUtils::createStringListFromObject) + .orElse(nameTags); + + entityType = Optional.ofNullable(config.get("pinata.type")) + .map(Objects::toString) + .map(String::toUpperCase) + .flatMap(s -> Enums.getIfPresent(EntityType.class, s).toJavaUtil()) + .filter(EntityType::isAlive) + .orElse(entityType); + } + + @Override + protected @Nullable Entity createEntity(Location location) { + LivingEntity entity = (LivingEntity) location.getWorld().spawnEntity(location, entityType); + String nameTag = Util.getRandomColorizedString(nameTags, ""); + if (!nameTag.isEmpty()) { + entity.setCustomName(nameTag); + entity.setCustomNameVisible(true); + } + return entity; + } + + public List getNameTags() { + return nameTags; + } + + public EntityType getEntityType() { + return entityType; + } +} diff --git a/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/SpawnFeature.java b/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/SpawnFeature.java new file mode 100644 index 0000000..75e5494 --- /dev/null +++ b/src/main/java/me/hsgamer/gamesinthebox/pinata/feature/SpawnFeature.java @@ -0,0 +1,31 @@ +package me.hsgamer.gamesinthebox.pinata.feature; + +import me.hsgamer.gamesinthebox.game.feature.BoundingOffsetFeature; +import me.hsgamer.gamesinthebox.game.simple.SimpleGameArena; +import me.hsgamer.minigamecore.base.Feature; +import org.bukkit.entity.Entity; + +import java.util.concurrent.atomic.AtomicReference; + +public class SpawnFeature implements Feature { + private final SimpleGameArena arena; + private final AtomicReference entity = new AtomicReference<>(); + private PinataFeature pinataFeature; + private BoundingOffsetFeature boundingOffsetFeature; + + public SpawnFeature(SimpleGameArena arena) { + this.arena = arena; + } + + @Override + public void init() { + this.pinataFeature = this.arena.getFeature(PinataFeature.class); + this.boundingOffsetFeature = this.arena.getFeature(BoundingOffsetFeature.class); + } + + public void checkAndSpawn() { + Entity entity = this.entity.get(); + if (entity != null && entity.isValid()) return; + pinataFeature.spawn(boundingOffsetFeature.getRandomLocation(), this.entity::set); + } +} diff --git a/src/main/resources/expansion.yml b/src/main/resources/expansion.yml index 35c0e83..e33e961 100644 --- a/src/main/resources/expansion.yml +++ b/src/main/resources/expansion.yml @@ -2,8 +2,4 @@ name: ${project.name} version: ${project.version} main: ${project.mainClass} description: ${project.description} - -# authors: -# depend: -# soft-depend: -# plugin-depend: \ No newline at end of file +authors: "HSGamer" \ No newline at end of file