Adds missing comments and annotations

This commit is contained in:
Kristian Knarvik 2023-12-17 20:58:22 +01:00
parent 4af0531d32
commit 0249e013e3
6 changed files with 132 additions and 42 deletions

View File

@ -1,6 +1,6 @@
# Quest Mob Spawns # Quest Mob Spawns
This Spigot plugin will automatically spawn mobs required for every kill quest. The mobs will not harm or target any This Spigot plugin will automatically spawn mobs required for every kill quest. The mobs will not harm or target any
player unless the player has the kill quest, is on the stage for which the mobs should spawn, and has not yet killed the player unless the player has the kill quest, is on the stage for which the mobs should spawn, and has not yet killed the
required amount of mobs. The spawned mobs will not drop any items or XP. The mobs are marked as spawner spawned mods, so required amount of mobs. The spawned mobs will not drop any items or XP. The mobs are marked as spawner spawned mods, so
other plugins, like mcMMO will not grant any skill XP from the quest mobs. other plugins, like mcMMO will not grant any skill XP from the quest mobs.

View File

@ -82,5 +82,11 @@
<version>4.8.1</version> <version>4.8.1</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>24.0.1</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -18,6 +18,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -28,6 +29,9 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
/**
* This plugin's main class
*/
public final class QuestMobSpawns extends JavaPlugin { public final class QuestMobSpawns extends JavaPlugin {
private final Random random = new Random(); private final Random random = new Random();
@ -41,22 +45,20 @@ public final class QuestMobSpawns extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
plugin = this; plugin = this;
// Plugin startup logic // Set up the quests API
questsAPI = (QuestsAPI) Bukkit.getServer().getPluginManager().getPlugin("Quests"); questsAPI = (QuestsAPI) Bukkit.getServer().getPluginManager().getPlugin("Quests");
if (questsAPI == null) { if (questsAPI == null) {
this.setEnabled(false);
return; return;
} }
// Register listeners
Bukkit.getPluginManager().registerEvents(new MobDeathListener(), this); Bukkit.getPluginManager().registerEvents(new MobDeathListener(), this);
Bukkit.getPluginManager().registerEvents(new MobBlockDamageListener(questsAPI), this); Bukkit.getPluginManager().registerEvents(new MobBlockDamageListener(questsAPI), this);
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, this::spawnMobs, 200, 30 * 20); Bukkit.getScheduler().scheduleSyncRepeatingTask(this, this::spawnMobs, 200, 30 * 20);
} }
@Override
public void onDisable() {
// Plugin shutdown logic
}
/** /**
* Spawns mobs for all kill areas as necessary * Spawns mobs for all kill areas as necessary
*/ */
@ -109,7 +111,16 @@ public final class QuestMobSpawns extends JavaPlugin {
} }
} }
private void spawnMobsForStage(Map<IStage, Set<IQuester>> questPlayerMap, IQuest quest, IStage stage, int index) { /**
* Spawns necessary mobs for a given stage
*
* @param questPlayerMap <p>The set of quest-takers on the given stage</p>
* @param quest <p>The quest to spawn mobs for</p>
* @param stage <p>The stage to spawn mobs for</p>
* @param index <p>The index of the kill location to spawn mobs for</p>
*/
private void spawnMobsForStage(@NotNull Map<IStage, @NotNull Set<IQuester>> questPlayerMap, @NotNull IQuest quest,
@NotNull IStage stage, int index) {
Location killLocation = stage.getLocationsToKillWithin().get(index).clone(); Location killLocation = stage.getLocationsToKillWithin().get(index).clone();
int radius = stage.getRadiiToKillWithin().get(index); int radius = stage.getRadiiToKillWithin().get(index);
@ -140,14 +151,14 @@ public final class QuestMobSpawns extends JavaPlugin {
int z = (int) Math.round((random.nextInt(radius * 2) - radius) + killLocation.getZ()); int z = (int) Math.round((random.nextInt(radius * 2) - radius) + killLocation.getZ());
Block spawnBlock = killLocation.getWorld().getHighestBlockAt(x, z).getRelative(BlockFace.UP); Block spawnBlock = killLocation.getWorld().getHighestBlockAt(x, z).getRelative(BlockFace.UP);
Entity spawnedEntity = killLocation.getWorld().spawnEntity(spawnBlock.getLocation(), entityType); Entity spawnedEntity = killLocation.getWorld().spawnEntity(spawnBlock.getLocation(), entityType);
// Treat the spawned entity as the result of a spawner for plugins like mcMMO // Treat the spawned entity as the result of a spawner for plugins like mcMMO
if (spawnedEntity instanceof LivingEntity) { if (spawnedEntity instanceof LivingEntity) {
CreatureSpawnEvent spawnEvent = new CreatureSpawnEvent((LivingEntity) spawnedEntity, CreatureSpawnEvent spawnEvent = new CreatureSpawnEvent((LivingEntity) spawnedEntity,
CreatureSpawnEvent.SpawnReason.SPAWNER); CreatureSpawnEvent.SpawnReason.SPAWNER);
Bukkit.getPluginManager().callEvent(spawnEvent); Bukkit.getPluginManager().callEvent(spawnEvent);
} }
QuestMobHelper.setSpawnedMob(spawnedEntity); QuestMobHelper.setSpawnedMob(spawnedEntity);
QuestMobHelper.setQuestData(spawnedEntity, quest.getId(), quest.getStages().indexOf(stage), index, QuestMobHelper.setQuestData(spawnedEntity, quest.getId(), quest.getStages().indexOf(stage), index,
new int[]{killLocation.getBlockX(), killLocation.getBlockY(), killLocation.getBlockZ(), radius}); new int[]{killLocation.getBlockX(), killLocation.getBlockY(), killLocation.getBlockZ(), radius});
@ -159,13 +170,13 @@ public final class QuestMobSpawns extends JavaPlugin {
/** /**
* Counts the number of entities of the necessary type already in the kill zone * Counts the number of entities of the necessary type already in the kill zone
* *
* @param killLocation <p>The location of the kill location's centre</p> * @param killLocation <p>The location of the kill location's centre</p>
* @param radius <p>The radius of the kill zone</p> * @param radius <p>The radius of the kill zone</p>
* @param entityType <p>The type of entity to kill in the kill zone</p> * @param entityType <p>The type of entity to kill in the kill zone</p>
* @return <p></p> * @return <p></p>
*/ */
private int entitiesInKillZone(Location killLocation, int radius, EntityType entityType) { private int entitiesInKillZone(@NotNull Location killLocation, int radius, @NotNull EntityType entityType) {
if (killLocation.getWorld() == null) { if (killLocation.getWorld() == null) {
return 0; return 0;
} }
@ -195,8 +206,9 @@ public final class QuestMobSpawns extends JavaPlugin {
* @param maxMobs <p>The maximum amount of mobs allowed in the kill zone</p> * @param maxMobs <p>The maximum amount of mobs allowed in the kill zone</p>
* @return <p>The number of mobs to spawn</p> * @return <p>The number of mobs to spawn</p>
*/ */
private int calculateMobNumber(Map<IStage, Set<IQuester>> questPlayerMap, IQuest quest, IStage stage, int index, private int calculateMobNumber(@NotNull Map<IStage, @NotNull Set<IQuester>> questPlayerMap, @NotNull IQuest quest,
Location killLocation, int radius, int maxMobs) { @NotNull IStage stage, int index, @NotNull Location killLocation, int radius,
int maxMobs) {
int mobsToSpawn = 0; int mobsToSpawn = 0;
for (IQuester player : questPlayerMap.get(stage)) { for (IQuester player : questPlayerMap.get(stage)) {
Location playerLocation = player.getPlayer().getLocation().clone(); Location playerLocation = player.getPlayer().getLocation().clone();

View File

@ -18,17 +18,22 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityInteractEvent; import org.bukkit.event.entity.EntityInteractEvent;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent; import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A listener for quest mobs causing damage
*/
public class MobBlockDamageListener implements Listener { public class MobBlockDamageListener implements Listener {
private final QuestsAPI questsAPI; private final QuestsAPI questsAPI;
public MobBlockDamageListener(QuestsAPI questsAPI) { public MobBlockDamageListener(@NotNull QuestsAPI questsAPI) {
this.questsAPI = questsAPI; this.questsAPI = questsAPI;
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void onExplode(EntityExplodeEvent event) { public void onExplode(@NotNull EntityExplodeEvent event) {
if (QuestMobHelper.isSpawnedMob(event.getEntity())) { if (QuestMobHelper.isSpawnedMob(event.getEntity())) {
event.blockList().clear(); event.blockList().clear();
event.setCancelled(false); event.setCancelled(false);
@ -36,17 +41,17 @@ public class MobBlockDamageListener implements Listener {
} }
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockChange(EntityChangeBlockEvent event) { public void onBlockChange(@NotNull EntityChangeBlockEvent event) {
cancelIfQuestMob(event, event.getEntity()); cancelIfQuestMob(event, event.getEntity());
} }
@EventHandler @EventHandler
public void onDoorBreak(EntityBreakDoorEvent event) { public void onDoorBreak(@NotNull EntityBreakDoorEvent event) {
cancelIfQuestMob(event, event.getEntity()); cancelIfQuestMob(event, event.getEntity());
} }
@EventHandler @EventHandler
public void onEntityTarget(EntityTargetLivingEntityEvent event) { public void onEntityTarget(@NotNull EntityTargetLivingEntityEvent event) {
Entity entity = event.getEntity(); Entity entity = event.getEntity();
if (!QuestMobHelper.isSpawnedMob(entity) || !(event.getTarget() instanceof Player player)) { if (!QuestMobHelper.isSpawnedMob(entity) || !(event.getTarget() instanceof Player player)) {
return; return;
@ -62,14 +67,14 @@ public class MobBlockDamageListener implements Listener {
event.setCancelled(QuestMobHelper.get2dDistance(player.getLocation(), killLocation) > killLocationData[3]); event.setCancelled(QuestMobHelper.get2dDistance(player.getLocation(), killLocation) > killLocationData[3]);
} }
} }
@EventHandler @EventHandler
public void onTrample(EntityInteractEvent event) { public void onTrample(@NotNull EntityInteractEvent event) {
cancelIfQuestMob(event, event.getEntity()); cancelIfQuestMob(event, event.getEntity());
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
public void onEntityDamage(EntityDamageByEntityEvent event) { public void onEntityDamage(@NotNull EntityDamageByEntityEvent event) {
if (!QuestMobHelper.isSpawnedMob(event.getDamager())) { if (!QuestMobHelper.isSpawnedMob(event.getDamager())) {
return; return;
} }
@ -84,26 +89,43 @@ public class MobBlockDamageListener implements Listener {
/** /**
* Checks whether the given player has the quest the given entity was spawned for * Checks whether the given player has the quest the given entity was spawned for
* *
* @param entity <p>The entity with stored quest data</p> * @param entity <p>The entity with stored quest data</p>
* @param player <p>A player</p> * @param player <p>A player</p>
* @return <p>True if the player has the quest stored in the entity</p> * @return <p>True if the player has the quest stored in the entity</p>
*/ */
private Boolean hasQuest(Entity entity, Player player) { private Boolean hasQuest(@NotNull Entity entity, @NotNull Player player) {
IQuester questPlayer = questsAPI.getQuester(player.getUniqueId()); IQuester questPlayer = questsAPI.getQuester(player.getUniqueId());
IQuest quest = getQuest(QuestMobHelper.getQuestId(entity)); String questId = QuestMobHelper.getQuestId(entity);
if (questId == null) {
return false;
}
IQuest quest = getQuest(questId);
if (quest == null) { if (quest == null) {
// Remove the entity if the quest it belongs to no longer exists // Remove the entity if the quest it belongs to no longer exists
entity.remove(); entity.remove();
return false; return false;
} }
IStage stage = quest.getStage(QuestMobHelper.getStageId(entity)); Integer stageId = QuestMobHelper.getStageId(entity);
int index = QuestMobHelper.getIndex(entity); if (stageId == null) {
return false;
}
IStage stage = quest.getStage(stageId);
Integer index = QuestMobHelper.getIndex(entity);
if (index == null) {
return false;
}
return questPlayer.getQuestData().containsKey(quest) && questPlayer.getCurrentStage(quest) == stage && return questPlayer.getQuestData().containsKey(quest) && questPlayer.getCurrentStage(quest) == stage &&
questPlayer.getQuestData(quest).getMobNumKilled().get(index) < stage.getMobNumToKill().get(index); questPlayer.getQuestData(quest).getMobNumKilled().get(index) < stage.getMobNumToKill().get(index);
} }
private IQuest getQuest(String questId) { /**
* Gets the quest with the given id
*
* @param questId <p>The id of the quest to get</p>
* @return <p>The quest, or null if no such quest exists</p>
*/
private @Nullable IQuest getQuest(@NotNull String questId) {
for (IQuest quest : questsAPI.getLoadedQuests()) { for (IQuest quest : questsAPI.getLoadedQuests()) {
if (quest.getId().equalsIgnoreCase(questId)) { if (quest.getId().equalsIgnoreCase(questId)) {
return quest; return quest;
@ -111,8 +133,14 @@ public class MobBlockDamageListener implements Listener {
} }
return null; return null;
} }
private void cancelIfQuestMob(Cancellable cancellable, Entity entity) { /**
* Cancels a cancellable event if the given entity has been spawned by this plugin
*
* @param cancellable <p>A cancellable event</p>
* @param entity <p>The entity to check</p>
*/
private void cancelIfQuestMob(@NotNull Cancellable cancellable, @NotNull Entity entity) {
if (QuestMobHelper.isSpawnedMob(entity)) { if (QuestMobHelper.isSpawnedMob(entity)) {
cancellable.setCancelled(true); cancellable.setCancelled(true);
} }

View File

@ -6,11 +6,15 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityDropItemEvent; import org.bukkit.event.entity.EntityDropItemEvent;
import org.jetbrains.annotations.NotNull;
/**
* A listener for the death of quest mobs
*/
public class MobDeathListener implements Listener { public class MobDeathListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onMobDeath(EntityDeathEvent deathEvent) { public void onMobDeath(@NotNull EntityDeathEvent deathEvent) {
if (QuestMobHelper.isSpawnedMob(deathEvent.getEntity())) { if (QuestMobHelper.isSpawnedMob(deathEvent.getEntity())) {
deathEvent.getDrops().clear(); deathEvent.getDrops().clear();
deathEvent.setDroppedExp(0); deathEvent.setDroppedExp(0);
@ -18,7 +22,7 @@ public class MobDeathListener implements Listener {
} }
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onMobDrop(EntityDropItemEvent dropEvent) { public void onMobDrop(@NotNull EntityDropItemEvent dropEvent) {
if (QuestMobHelper.isSpawnedMob(dropEvent.getEntity())) { if (QuestMobHelper.isSpawnedMob(dropEvent.getEntity())) {
dropEvent.setCancelled(true); dropEvent.setCancelled(true);
} }

View File

@ -6,7 +6,12 @@ import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A helper class for quest mobs
*/
public class QuestMobHelper { public class QuestMobHelper {
private static final NamespacedKey questMobKey = new NamespacedKey(QuestMobSpawns.getPlugin(), "questMob"); private static final NamespacedKey questMobKey = new NamespacedKey(QuestMobSpawns.getPlugin(), "questMob");
@ -15,23 +20,57 @@ public class QuestMobHelper {
private static final NamespacedKey mobIndexKey = new NamespacedKey(QuestMobSpawns.getPlugin(), "mobIndex"); private static final NamespacedKey mobIndexKey = new NamespacedKey(QuestMobSpawns.getPlugin(), "mobIndex");
private static final NamespacedKey killZoneLocationKey = new NamespacedKey(QuestMobSpawns.getPlugin(), "killZoneLocationKey"); private static final NamespacedKey killZoneLocationKey = new NamespacedKey(QuestMobSpawns.getPlugin(), "killZoneLocationKey");
public static boolean isSpawnedMob(Entity entity) { /**
* Checks whether the given entity has been spawned by this plugin
*
* @param entity <p>The entity to check</p>
* @return <p>True if the entity was spawned by this plugin</p>
*/
public static boolean isSpawnedMob(@NotNull Entity entity) {
return Boolean.TRUE.equals(entity.getPersistentDataContainer().get(questMobKey, PersistentDataType.BOOLEAN)); return Boolean.TRUE.equals(entity.getPersistentDataContainer().get(questMobKey, PersistentDataType.BOOLEAN));
} }
public static void setSpawnedMob(Entity entity) { /**
* Marks the given entity as spawned by this plugin
*
* @param entity <p>The entity to mark</p>
*/
public static void setSpawnedMob(@NotNull Entity entity) {
entity.getPersistentDataContainer().set(questMobKey, PersistentDataType.BOOLEAN, true); entity.getPersistentDataContainer().set(questMobKey, PersistentDataType.BOOLEAN, true);
} }
public static String getQuestId(Entity entity) { /**
* Gets the id of the quest the given entity was spawned for
*
* @param entity <p>The entity to get the quest id from</p>
* @return <p>The quest id, or null if not a quest mob</p>
*/
public static @Nullable String getQuestId(@NotNull Entity entity) {
return entity.getPersistentDataContainer().get(questIdKey, PersistentDataType.STRING); return entity.getPersistentDataContainer().get(questIdKey, PersistentDataType.STRING);
} }
public static Integer getStageId(Entity entity) { /**
* Gets the id of the stage the given entity was spawned for
*
* <p>The stage id is relative to the quest the stage belongs to.</p>
*
* @param entity <p>The entity to get the stage id for</p>
* @return <p>The stage id, or null if not a quest mob</p>
*/
public static @Nullable Integer getStageId(Entity entity) {
return entity.getPersistentDataContainer().get(stageIdKey, PersistentDataType.INTEGER); return entity.getPersistentDataContainer().get(stageIdKey, PersistentDataType.INTEGER);
} }
public static Integer getIndex(Entity entity) { /**
* Gets the index of this entity in the quest's "mobs to kill" array
*
* <p>This property is necessary in order to check whether the player has already reached the required number of
* mob kills. This matters if the stage has some other clear property other than killing this type of mob.</p>
*
* @param entity <p>The entity to check</p>
* @return <p>The entity's index, or null if not a quest mob</p>
*/
public static @Nullable Integer getIndex(@NotNull Entity entity) {
return entity.getPersistentDataContainer().get(mobIndexKey, PersistentDataType.INTEGER); return entity.getPersistentDataContainer().get(mobIndexKey, PersistentDataType.INTEGER);
} }
@ -41,7 +80,7 @@ public class QuestMobHelper {
* @param entity <p>The entity to get data for</p> * @param entity <p>The entity to get data for</p>
* @return <p>An array containing x,y,z,radius</p> * @return <p>An array containing x,y,z,radius</p>
*/ */
public static int[] getKillZoneLocation(Entity entity) { public static int[] getKillZoneLocation(@NotNull Entity entity) {
return entity.getPersistentDataContainer().get(killZoneLocationKey, PersistentDataType.INTEGER_ARRAY); return entity.getPersistentDataContainer().get(killZoneLocationKey, PersistentDataType.INTEGER_ARRAY);
} }
@ -52,7 +91,8 @@ public class QuestMobHelper {
* @param questId <p>The id of the quest the mob was spawned for</p> * @param questId <p>The id of the quest the mob was spawned for</p>
* @param killLocationInfo <p>Information about the kill location (x, y, z, range)</p> * @param killLocationInfo <p>Information about the kill location (x, y, z, range)</p>
*/ */
public static void setQuestData(Entity entity, String questId, int stageId, int mobIndex, int[] killLocationInfo) { public static void setQuestData(@NotNull Entity entity, @NotNull String questId, int stageId, int mobIndex,
int[] killLocationInfo) {
PersistentDataContainer persistentDataContainer = entity.getPersistentDataContainer(); PersistentDataContainer persistentDataContainer = entity.getPersistentDataContainer();
persistentDataContainer.set(questIdKey, PersistentDataType.STRING, questId); persistentDataContainer.set(questIdKey, PersistentDataType.STRING, questId);
persistentDataContainer.set(stageIdKey, PersistentDataType.INTEGER, stageId); persistentDataContainer.set(stageIdKey, PersistentDataType.INTEGER, stageId);
@ -67,7 +107,7 @@ public class QuestMobHelper {
* @param location2 <p>The second location</p> * @param location2 <p>The second location</p>
* @return <p>The 2d distance between the locations</p> * @return <p>The 2d distance between the locations</p>
*/ */
public static double get2dDistance(Location location1, Location location2) { public static double get2dDistance(@NotNull Location location1, @NotNull Location location2) {
return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2) + return Math.sqrt(Math.pow(location2.getX() - location1.getX(), 2) +
Math.pow(location2.getZ() - location1.getZ(), 2)); Math.pow(location2.getZ() - location1.getZ(), 2));
} }