diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java index 2299f5864e..c8816ceabc 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/PaperWorldExtensions.java @@ -1,8 +1,14 @@ package com.denizenscript.denizen.paper.properties; +import com.denizenscript.denizen.nms.NMSHandler; +import com.denizenscript.denizen.nms.NMSVersion; +import com.denizenscript.denizen.objects.EntityTag; +import com.denizenscript.denizen.objects.LocationTag; import com.denizenscript.denizen.objects.WorldTag; import com.denizenscript.denizen.utilities.BukkitImplDeprecations; import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.ListTag; +import org.bukkit.boss.DragonBattle; public class PaperWorldExtensions { @@ -23,6 +29,81 @@ public static void register() { return new ElementTag(world.getWorld().getNoTickViewDistance()); }); + if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) { + + // <--[tag] + // @attribute + // @returns ElementTag(Number) + // @description + // Returns the number of end gateway portals. + // Only works in end worlds. + // --> + WorldTag.tagProcessor.registerTag(ElementTag.class, "gateway_count", (attribute, object) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + attribute.echoError("Provided world is not an end world!"); + return null; + } + return new ElementTag(battle.getGatewayCount()); + }); + + // <--[tag] + // @attribute + // @returns ListTag(EntityTag) + // @description + // Returns a ListTag of the healing crystals located on top of the obsidian towers. + // Only works in end worlds. + // --> + WorldTag.tagProcessor.registerTag(ListTag.class, "healing_crystals", (attribute, object) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + attribute.echoError("Provided world is not an end world!"); + return null; + } + return new ListTag(battle.getHealingCrystals(), EntityTag::new); + }); + + // <--[tag] + // @attribute + // @returns ListTag(EntityTag) + // @description + // Returns a ListTag of the respawn crystals located at the end exit portal. + // Only works in end worlds. + // --> + WorldTag.tagProcessor.registerTag(ListTag.class, "respawn_crystals", (attribute, object) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + attribute.echoError("Provided world is not an end world!"); + return null; + } + return new ListTag(battle.getRespawnCrystals(), EntityTag::new); + }); + + // <--[mechanism] + // @object WorldTag + // @name spawn_gateway + // @input LocationTag + // @Plugin Paper + // @group paper + // @description + // If no location is specified, tries to spawn a new end gateway using default game mechanics. Otherwise, spawns a new end gateway portal at the specified location. + // Only works in end worlds. + // --> + WorldTag.tagProcessor.registerMechanism("spawn_gateway", false, (object, mechanism) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + mechanism.echoError("Cannot spawn gateway in non-end world!"); + return; + } + if (!mechanism.hasValue()) { + battle.spawnNewGateway(); + } + else if (mechanism.requireObject(LocationTag.class)) { + battle.spawnNewGateway(mechanism.getValue().asType(LocationTag.class, mechanism.context)); + } + }); + } + // <--[mechanism] // @object WorldTag // @name view_distance diff --git a/plugin/src/main/java/com/denizenscript/denizen/objects/WorldTag.java b/plugin/src/main/java/com/denizenscript/denizen/objects/WorldTag.java index 0053c516df..3938c26f38 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/objects/WorldTag.java +++ b/plugin/src/main/java/com/denizenscript/denizen/objects/WorldTag.java @@ -2,6 +2,7 @@ import com.denizenscript.denizen.events.BukkitScriptEvent; import com.denizenscript.denizen.nms.NMSHandler; +import com.denizenscript.denizen.nms.NMSVersion; import com.denizenscript.denizen.nms.abstracts.BiomeNMS; import com.denizenscript.denizen.utilities.flags.WorldFlagHandler; import com.denizenscript.denizencore.flags.AbstractFlagTracker; @@ -837,6 +838,7 @@ else if (time >= 12500) { registerTag(LocationTag.class, "dragon_portal_location", (attribute, object) -> { DragonBattle battle = object.getWorld().getEnderDragonBattle(); if (battle == null) { + attribute.echoError("Provided world is not an end world!"); return null; } if (battle.getEndPortalLocation() == null) { @@ -1006,6 +1008,92 @@ else if (time >= 12500) { registerTag(ElementTag.class, "is_night", (attribute, world) -> { return new ElementTag(NMSHandler.worldHelper.isNight(world.getWorld())); }); + + // <--[tag] + // @attribute + // @returns ElementTag(Boolean) + // @description + // Returns whether the ender dragon has been killed in this world before. + // Only works in end worlds. + // --> + registerTag(ElementTag.class, "first_dragon_killed", (attribute, object) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + attribute.echoError("Provided world is not an end world!"); + return null; + } + return new ElementTag(battle.hasBeenPreviouslyKilled()); + }); + + // <--[mechanism] + // @object WorldTag + // @name respawn_dragon + // @description + // Initiates the respawn sequence of the ender dragon as if a player placed 4 end crystals on the portal. + // Only works in end worlds. + // --> + tagProcessor.registerMechanism("respawn_dragon", false, (object, mechanism) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + mechanism.echoError("Provided world is not an end world!"); + return; + } + battle.initiateRespawn(); + }); + + // <--[mechanism] + // @object WorldTag + // @name reset_crystals + // @description + // Resets the end crystals located on the obsidian pillars in this world. + // Only works in end worlds. + // --> + tagProcessor.registerMechanism("reset_crystals", false, (object, mechanism) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + mechanism.echoError("Provided world is not an end world!"); + return; + } + battle.resetCrystals(); + }); + + // <--[mechanism] + // @object WorldTag + // @name respawn_phase + // @input ElementTag + // @description + // Set the current respawn phase of the ender dragon. Valid phases can be found at <@link url https://jd.papermc.io/paper/1.20/org/bukkit/boss/DragonBattle.RespawnPhase.html> + // Only works in end worlds. + // --> + tagProcessor.registerMechanism("respawn_phase", false, ElementTag.class, (object, mechanism, input) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + mechanism.echoError("Provided world is not an end world!"); + return; + } + battle.setRespawnPhase(input.asEnum(DragonBattle.RespawnPhase.class)); + }); + + if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20)) { + + // <--[mechanism] + // @object WorldTag + // @name respawn_phase + // @input ElementTag(Boolean) + // @description + // Set whether the first ender dragon was killed already. + // Only works in end worlds. + // --> + tagProcessor.registerMechanism("first_dragon_killed", false, ElementTag.class, (object, mechanism, input) -> { + DragonBattle battle = object.getWorld().getEnderDragonBattle(); + if (battle == null) { + mechanism.echoError("Provided world is not an end world!"); + return; + } + battle.setPreviouslyKilled(input.asBoolean()); + }); + } + } public static ObjectTagProcessor tagProcessor = new ObjectTagProcessor<>();