Search in sources :

Example 61 with ListTag

use of com.denizenscript.denizencore.objects.core.ListTag in project Denizen-For-Bukkit by DenizenScript.

the class WorldTag method registerTags.

public static void registerTags() {
    AbstractFlagTracker.registerFlagHandlers(tagProcessor);
    // ///////////////////
    // ENTITY LIST ATTRIBUTES
    // ///////////////
    // <--[tag]
    // @attribute <WorldTag.entities[(<matcher>)]>
    // @returns ListTag(EntityTag)
    // @description
    // Returns a list of entities in this world.
    // Optionally specify an entity type matcher to filter down to.
    // -->
    registerTag(ListTag.class, "entities", (attribute, object) -> {
        ListTag entities = new ListTag();
        String matcher = attribute.hasParam() ? attribute.getParam() : null;
        for (Entity entity : object.getEntitiesForTag()) {
            EntityTag current = new EntityTag(entity);
            if (matcher == null || BukkitScriptEvent.tryEntity(current, matcher)) {
                entities.addObject(current.getDenizenObject());
            }
        }
        return entities;
    });
    // <--[tag]
    // @attribute <WorldTag.living_entities>
    // @returns ListTag(EntityTag)
    // @description
    // Returns a list of living entities in this world.
    // This includes Players, mobs, NPCs, etc., but excludes dropped items, experience orbs, etc.
    // -->
    registerTag(ListTag.class, "living_entities", (attribute, object) -> {
        ListTag entities = new ListTag();
        for (Entity entity : object.getLivingEntitiesForTag()) {
            entities.addObject(new EntityTag(entity).getDenizenObject());
        }
        return entities;
    });
    // <--[tag]
    // @attribute <WorldTag.players>
    // @returns ListTag(PlayerTag)
    // @description
    // Returns a list of online players in this world.
    // -->
    registerTag(ListTag.class, "players", (attribute, object) -> {
        ListTag players = new ListTag();
        for (Player player : object.getWorld().getPlayers()) {
            if (!EntityTag.isNPC(player)) {
                players.addObject(new PlayerTag(player));
            }
        }
        return players;
    });
    // <--[tag]
    // @attribute <WorldTag.spawned_npcs>
    // @returns ListTag(NPCTag)
    // @description
    // Returns a list of spawned NPCs in this world.
    // -->
    registerTag(ListTag.class, "spawned_npcs", (attribute, object) -> {
        ListTag npcs = new ListTag();
        World thisWorld = object.getWorld();
        for (NPC npc : CitizensAPI.getNPCRegistry()) {
            if (npc.isSpawned() && npc.getStoredLocation().getWorld().equals(thisWorld)) {
                npcs.addObject(new NPCTag(npc));
            }
        }
        return npcs;
    });
    // <--[tag]
    // @attribute <WorldTag.npcs>
    // @returns ListTag(NPCTag)
    // @description
    // Returns a list of all NPCs in this world.
    // -->
    registerTag(ListTag.class, "npcs", (attribute, object) -> {
        ListTag npcs = new ListTag();
        World thisWorld = object.getWorld();
        for (NPC npc : CitizensAPI.getNPCRegistry()) {
            Location location = npc.getStoredLocation();
            if (location != null) {
                World world = location.getWorld();
                if (world != null && world.equals(thisWorld)) {
                    npcs.addObject(new NPCTag(npc));
                }
            }
        }
        return npcs;
    });
    // ///////////////////
    // GEOGRAPHY ATTRIBUTES
    // ///////////////
    // <--[tag]
    // @attribute <WorldTag.can_generate_structures>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether the world will generate structures.
    // -->
    registerTag(ElementTag.class, "can_generate_structures", (attribute, object) -> {
        return new ElementTag(object.getWorld().canGenerateStructures());
    });
    // <--[tag]
    // @attribute <WorldTag.loaded_chunks>
    // @returns ListTag(ChunkTag)
    // @description
    // Returns a list of all the currently loaded chunks.
    // -->
    registerTag(ListTag.class, "loaded_chunks", (attribute, object) -> {
        ListTag chunks = new ListTag();
        for (Chunk ent : object.getWorld().getLoadedChunks()) {
            chunks.addObject(new ChunkTag(ent));
        }
        return chunks;
    });
    registerTag(ChunkTag.class, "random_loaded_chunk", (attribute, object) -> {
        Deprecations.worldRandomLoadedChunkTag.warn(attribute.context);
        int random = CoreUtilities.getRandom().nextInt(object.getWorld().getLoadedChunks().length);
        return new ChunkTag(object.getWorld().getLoadedChunks()[random]);
    });
    // <--[tag]
    // @attribute <WorldTag.sea_level>
    // @returns ElementTag(Number)
    // @description
    // Returns the level of the sea.
    // -->
    registerTag(ElementTag.class, "sea_level", (attribute, object) -> {
        return new ElementTag(object.getWorld().getSeaLevel());
    });
    // <--[tag]
    // @attribute <WorldTag.max_height>
    // @returns ElementTag(Number)
    // @description
    // Returns the maximum block height of the world.
    // -->
    registerTag(ElementTag.class, "max_height", (attribute, object) -> {
        return new ElementTag(object.getWorld().getMaxHeight());
    });
    // <--[tag]
    // @attribute <WorldTag.min_height>
    // @returns ElementTag(Number)
    // @description
    // Returns the minimum block height of the world.
    // -->
    registerTag(ElementTag.class, "min_height", (attribute, object) -> {
        return new ElementTag(object.getWorld().getMinHeight());
    });
    // <--[tag]
    // @attribute <WorldTag.spawn_location>
    // @returns LocationTag
    // @mechanism WorldTag.spawn_location
    // @description
    // Returns the spawn location of the world.
    // -->
    registerTag(LocationTag.class, "spawn_location", (attribute, object) -> {
        return new LocationTag(object.getWorld().getSpawnLocation());
    });
    // <--[tag]
    // @attribute <WorldTag.world_type>
    // @returns ElementTag
    // @description
    // Returns the world type of the world.
    // Can return any enum from: <@link url https://hub.spigotmc.org/javadocs/spigot/org/bukkit/WorldType.html>
    // -->
    registerTag(ElementTag.class, "world_type", (attribute, object) -> {
        return new ElementTag(object.getWorld().getWorldType().getName());
    });
    // ///////////////////
    // IDENTIFICATION ATTRIBUTES
    // ///////////////
    // <--[tag]
    // @attribute <WorldTag.name>
    // @returns ElementTag
    // @description
    // Returns the name of the world.
    // -->
    tagProcessor.registerTag(ElementTag.class, "name", (attribute, object) -> {
        return new ElementTag(object.world_name);
    });
    // <--[tag]
    // @attribute <WorldTag.seed>
    // @returns ElementTag
    // @description
    // Returns the world seed.
    // -->
    registerTag(ElementTag.class, "seed", (attribute, object) -> {
        return new ElementTag(object.getWorld().getSeed());
    });
    // ///////////////////
    // SETTINGS ATTRIBUTES
    // ///////////////
    // <--[tag]
    // @attribute <WorldTag.allows_animals>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether animals can spawn in this world.
    // -->
    registerTag(ElementTag.class, "allows_animals", (attribute, object) -> {
        return new ElementTag(object.getWorld().getAllowAnimals());
    });
    // <--[tag]
    // @attribute <WorldTag.allows_monsters>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether monsters can spawn in this world.
    // -->
    registerTag(ElementTag.class, "allows_monsters", (attribute, object) -> {
        return new ElementTag(object.getWorld().getAllowMonsters());
    });
    // <--[tag]
    // @attribute <WorldTag.allows_pvp>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether player versus player combat is allowed in this world.
    // -->
    registerTag(ElementTag.class, "allows_pvp", (attribute, object) -> {
        return new ElementTag(object.getWorld().getPVP());
    });
    // <--[tag]
    // @attribute <WorldTag.auto_save>
    // @returns ElementTag(Boolean)
    // @mechanism WorldTag.auto_save
    // @description
    // Returns whether the world automatically saves.
    // -->
    registerTag(ElementTag.class, "auto_save", (attribute, object) -> {
        return new ElementTag(object.getWorld().isAutoSave());
    });
    // <--[tag]
    // @attribute <WorldTag.ambient_spawn_limit>
    // @returns ElementTag(Number)
    // @mechanism WorldTag.ambient_spawn_limit
    // @description
    // Returns the number of ambient mobs that can spawn in a chunk in this world.
    // -->
    registerTag(ElementTag.class, "ambient_spawn_limit", (attribute, object) -> {
        return new ElementTag(object.getWorld().getAmbientSpawnLimit());
    });
    // <--[tag]
    // @attribute <WorldTag.animal_spawn_limit>
    // @returns ElementTag(Number)
    // @mechanism WorldTag.animal_spawn_limit
    // @description
    // Returns the number of animals that can spawn in a chunk in this world.
    // -->
    registerTag(ElementTag.class, "animal_spawn_limit", (attribute, object) -> {
        return new ElementTag(object.getWorld().getAnimalSpawnLimit());
    });
    // <--[tag]
    // @attribute <WorldTag.monster_spawn_limit>
    // @returns ElementTag(Number)
    // @mechanism WorldTag.monster_spawn_limit
    // @description
    // Returns the number of monsters that can spawn in a chunk in this world.
    // -->
    registerTag(ElementTag.class, "monster_spawn_limit", (attribute, object) -> {
        return new ElementTag(object.getWorld().getMonsterSpawnLimit());
    });
    // <--[tag]
    // @attribute <WorldTag.water_animal_spawn_limit>
    // @returns ElementTag(Number)
    // @mechanism WorldTag.water_animal_spawn_limit
    // @description
    // Returns the number of water animals that can spawn in a chunk in this world.
    // -->
    registerTag(ElementTag.class, "water_animal_spawn_limit", (attribute, object) -> {
        return new ElementTag(object.getWorld().getWaterAnimalSpawnLimit());
    });
    // <--[tag]
    // @attribute <WorldTag.difficulty>
    // @returns ElementTag
    // @mechanism WorldTag.difficulty
    // @description
    // Returns the name of the difficulty level.
    // -->
    registerTag(ElementTag.class, "difficulty", (attribute, object) -> {
        return new ElementTag(object.getWorld().getDifficulty().name());
    });
    // <--[tag]
    // @attribute <WorldTag.hardcore>
    // @returns ElementTag(Boolean)
    // @mechanism WorldTag.hardcore
    // @description
    // Returns whether the world is in hardcore mode.
    // -->
    registerTag(ElementTag.class, "hardcore", (attribute, object) -> {
        return new ElementTag(object.getWorld().isHardcore());
    });
    // <--[tag]
    // @attribute <WorldTag.keep_spawn>
    // @returns ElementTag(Boolean)
    // @mechanism WorldTag.keep_spawn
    // @description
    // Returns whether the world's spawn area should be kept loaded into memory.
    // -->
    registerTag(ElementTag.class, "keep_spawn", (attribute, object) -> {
        return new ElementTag(object.getWorld().getKeepSpawnInMemory());
    });
    // <--[tag]
    // @attribute <WorldTag.ticks_per_animal_spawn>
    // @returns DurationTag
    // @mechanism WorldTag.ticks_per_animal_spawns
    // @description
    // Returns the world's ticks per animal spawn value.
    // -->
    registerTag(DurationTag.class, "ticks_per_animal_spawn", (attribute, object) -> {
        return new DurationTag(object.getWorld().getTicksPerAnimalSpawns());
    });
    // <--[tag]
    // @attribute <WorldTag.ticks_per_monster_spawn>
    // @returns DurationTag
    // @mechanism WorldTag.ticks_per_monster_spawns
    // @description
    // Returns the world's ticks per monster spawn value.
    // -->
    registerTag(DurationTag.class, "ticks_per_monster_spawn", (attribute, object) -> {
        return new DurationTag(object.getWorld().getTicksPerMonsterSpawns());
    });
    // <--[tag]
    // @attribute <WorldTag.duration_since_created>
    // @returns DurationTag
    // @description
    // Returns the total duration of time since this world was first created.
    // -->
    registerTag(DurationTag.class, "duration_since_created", (attribute, object) -> {
        return new DurationTag(object.getWorld().getGameTime());
    });
    // ///////////////////
    // TIME ATTRIBUTES
    // ///////////////
    // <--[tag]
    // @attribute <WorldTag.time>
    // @returns ElementTag(Number)
    // @mechanism WorldTag.time
    // @description
    // Returns the relative in-game time of this world.
    // -->
    registerTag(ObjectTag.class, "time", (attribute, object) -> {
        // -->
        if (attribute.startsWith("duration", 2)) {
            attribute.fulfill(1);
            return new DurationTag(object.getWorld().getTime());
        } else // -->
        if (attribute.startsWith("full", 2)) {
            attribute.fulfill(1);
            return new DurationTag(object.getWorld().getFullTime());
        } else // -->
        if (attribute.startsWith("period", 2)) {
            attribute.fulfill(1);
            long time = object.getWorld().getTime();
            String period;
            if (time >= 23000) {
                period = "dawn";
            } else if (time >= 13500) {
                period = "night";
            } else if (time >= 12500) {
                period = "dusk";
            } else {
                period = "day";
            }
            return new ElementTag(period);
        } else {
            return new ElementTag(object.getWorld().getTime());
        }
    });
    // <--[tag]
    // @attribute <WorldTag.moon_phase>
    // @returns ElementTag(Number)
    // @description
    // Returns the current phase of the moon, as a number from 1 to 8.
    // -->
    registerTag(ElementTag.class, "moon_phase", (attribute, object) -> {
        return new ElementTag((int) ((object.getWorld().getFullTime() / 24000) % 8) + 1);
    }, "moonphase");
    // ///////////////////
    // WEATHER ATTRIBUTES
    // ///////////////
    // <--[tag]
    // @attribute <WorldTag.has_storm>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether there is currently a storm in this world.
    // ie, whether it is raining. To check for thunder, use <@link tag WorldTag.thundering>.
    // -->
    registerTag(ElementTag.class, "has_storm", (attribute, object) -> {
        return new ElementTag(object.getWorld().hasStorm());
    });
    // <--[tag]
    // @attribute <WorldTag.thunder_duration>
    // @returns DurationTag
    // @mechanism WorldTag.thunder_duration
    // @description
    // Returns the duration of thunder.
    // -->
    registerTag(DurationTag.class, "thunder_duration", (attribute, object) -> {
        return new DurationTag((long) object.getWorld().getThunderDuration());
    });
    // <--[tag]
    // @attribute <WorldTag.thundering>
    // @returns ElementTag(Boolean)
    // @mechanism WorldTag.thundering
    // @description
    // Returns whether it is currently thundering in this world.
    // -->
    registerTag(ElementTag.class, "thundering", (attribute, object) -> {
        return new ElementTag(object.getWorld().isThundering());
    });
    // <--[tag]
    // @attribute <WorldTag.weather_duration>
    // @returns DurationTag
    // @mechanism WorldTag.weather_duration
    // @description
    // Returns the duration of storms.
    // -->
    registerTag(DurationTag.class, "weather_duration", (attribute, object) -> {
        return new DurationTag((long) object.getWorld().getWeatherDuration());
    });
    // <--[tag]
    // @attribute <WorldTag.environment>
    // @returns ElementTag
    // @description
    // Returns the environment of the world: NORMAL, NETHER, or THE_END.
    // -->
    registerTag(ElementTag.class, "environment", (attribute, object) -> {
        return new ElementTag(object.getWorld().getEnvironment().name());
    });
    // ///////////////////
    // WORLD BORDER ATTRIBUTES
    // ///////////////
    // <--[tag]
    // @attribute <WorldTag.border_size>
    // @returns ElementTag(Decimal)
    // @description
    // Returns the size of the world border in this world.
    // -->
    registerTag(ElementTag.class, "border_size", (attribute, object) -> {
        return new ElementTag(object.getWorld().getWorldBorder().getSize());
    });
    // <--[tag]
    // @attribute <WorldTag.border_center>
    // @returns LocationTag
    // @description
    // Returns the center of the world border in this world.
    // -->
    registerTag(LocationTag.class, "border_center", (attribute, object) -> {
        return new LocationTag(object.getWorld().getWorldBorder().getCenter());
    });
    // <--[tag]
    // @attribute <WorldTag.border_damage>
    // @returns ElementTag(Decimal)
    // @description
    // Returns the amount of damage caused by crossing the world border in this world.
    // -->
    registerTag(ElementTag.class, "border_damage", (attribute, object) -> {
        return new ElementTag(object.getWorld().getWorldBorder().getDamageAmount());
    });
    // <--[tag]
    // @attribute <WorldTag.border_damage_buffer>
    // @returns ElementTag(Decimal)
    // @description
    // Returns the damage buffer of the world border in this world.
    // -->
    registerTag(ElementTag.class, "border_damage_buffer", (attribute, object) -> {
        return new ElementTag(object.getWorld().getWorldBorder().getDamageBuffer());
    });
    // <--[tag]
    // @attribute <WorldTag.border_warning_distance>
    // @returns ElementTag(Number)
    // @description
    // Returns the warning distance of the world border in this world.
    // -->
    registerTag(ElementTag.class, "border_warning_distance", (attribute, object) -> {
        return new ElementTag(object.getWorld().getWorldBorder().getWarningDistance());
    });
    // <--[tag]
    // @attribute <WorldTag.border_warning_time>
    // @returns DurationTag
    // @description
    // Returns warning time of the world border in this world as a duration.
    // -->
    registerTag(DurationTag.class, "border_warning_time", (attribute, object) -> {
        return new DurationTag(object.getWorld().getWorldBorder().getWarningTime());
    });
    // <--[tag]
    // @attribute <WorldTag.gamerule[<gamerule>]>
    // @returns ElementTag
    // @description
    // Returns the current value of the specified gamerule in the world.
    // Note that the name is case-sensitive... so "doFireTick" is correct, but "dofiretick" is not.
    // -->
    registerTag(ElementTag.class, "gamerule", (attribute, object) -> {
        if (!attribute.hasParam()) {
            attribute.echoError("The tag 'worldtag.gamerule[...]' must have an input value.");
            return null;
        }
        GameRule rule = GameRule.getByName(attribute.getParam());
        Object result = object.getWorld().getGameRuleValue(rule);
        return new ElementTag(result == null ? "null" : result.toString());
    });
    // <--[tag]
    // @attribute <WorldTag.gamerule_map>
    // @returns MapTag
    // @description
    // Returns a map of all the current values of all gamerules in the world.
    // -->
    registerTag(MapTag.class, "gamerule_map", (attribute, object) -> {
        MapTag map = new MapTag();
        for (GameRule rule : GameRule.values()) {
            Object result = object.getWorld().getGameRuleValue(rule);
            if (result != null) {
                map.putObject(rule.getName(), new ElementTag(result.toString()));
            }
        }
        return map;
    });
    // <--[tag]
    // @attribute <WorldTag.dragon_portal_location>
    // @returns LocationTag
    // @description
    // Returns the location of the ender dragon exit portal, if any (only for end worlds).
    // -->
    registerTag(LocationTag.class, "dragon_portal_location", (attribute, object) -> {
        DragonBattle battle = object.getWorld().getEnderDragonBattle();
        if (battle == null) {
            return null;
        }
        if (battle.getEndPortalLocation() == null) {
            return null;
        }
        return new LocationTag(battle.getEndPortalLocation());
    });
    // <--[tag]
    // @attribute <WorldTag.ender_dragon>
    // @returns EntityTag
    // @description
    // Returns the ender dragon entity currently fighting in this world, if any (only for end worlds).
    // -->
    registerTag(EntityTag.class, "ender_dragon", (attribute, object) -> {
        DragonBattle battle = object.getWorld().getEnderDragonBattle();
        if (battle == null) {
            return null;
        }
        if (battle.getEnderDragon() == null) {
            return null;
        }
        return new EntityTag(battle.getEnderDragon());
    });
    // <--[tag]
    // @attribute <WorldTag.gateway_locations>
    // @returns ListTag(LocationTag)
    // @description
    // Returns a list of possible gateway portal locations, if any (only for end worlds).
    // Not all of these will necessarily generate.
    // In current implementation, this is a list of exactly 20 locations in a circle around the world origin (with radius of 96 blocks).
    // -->
    registerTag(ListTag.class, "gateway_locations", (attribute, object) -> {
        DragonBattle battle = object.getWorld().getEnderDragonBattle();
        if (battle == null) {
            return null;
        }
        ListTag list = new ListTag();
        for (int i = 0; i < 20; i++) {
            // This math based on EndDragonFight#spawnNewGateway
            int x = (int) Math.floor(96.0D * Math.cos(2.0D * (-Math.PI + (Math.PI / 20.0) * i)));
            int z = (int) Math.floor(96.0D * Math.sin(2.0D * (-Math.PI + (Math.PI / 20.0) * i)));
            list.addObject(new LocationTag(object.getWorld(), x, 75, z));
        }
        return list;
    });
    // <--[tag]
    // @attribute <WorldTag.biomes>
    // @returns ListTag(BiomeTag)
    // @description
    // Returns a list of all biomes in this world (including custom biomes).
    // -->
    registerTag(ListTag.class, "biomes", (attribute, object) -> {
        ListTag output = new ListTag();
        for (BiomeNMS biome : NMSHandler.getInstance().getBiomes(object.getWorld())) {
            output.addObject(new BiomeTag(biome));
        }
        return output;
    });
    // <--[tag]
    // @attribute <WorldTag.advanced_matches[<matcher>]>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether the world matches some matcher text, using the system behind <@link language Advanced Script Event Matching>.
    // -->
    registerTag(ElementTag.class, "advanced_matches", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        return new ElementTag(BukkitScriptEvent.tryWorld(object, attribute.getParam()));
    });
}
Also used : NPC(net.citizensnpcs.api.npc.NPC) BiomeNMS(com.denizenscript.denizen.nms.abstracts.BiomeNMS) Entity(org.bukkit.entity.Entity) LivingEntity(org.bukkit.entity.LivingEntity) Player(org.bukkit.entity.Player) DragonBattle(org.bukkit.boss.DragonBattle) DurationTag(com.denizenscript.denizencore.objects.core.DurationTag) ListTag(com.denizenscript.denizencore.objects.core.ListTag) MapTag(com.denizenscript.denizencore.objects.core.MapTag) FlaggableObject(com.denizenscript.denizencore.flags.FlaggableObject) ElementTag(com.denizenscript.denizencore.objects.core.ElementTag)

Example 62 with ListTag

use of com.denizenscript.denizencore.objects.core.ListTag in project Denizen-For-Bukkit by DenizenScript.

the class BukkitElementProperties method registerTags.

public static void registerTags() {
    // <--[tag]
    // @attribute <ElementTag.as_biome>
    // @returns BiomeTag
    // @group conversion
    // @description
    // Returns the element as a BiomeTag. Note: the value must be a valid biome.
    // -->
    PropertyParser.<BukkitElementProperties, BiomeTag>registerStaticTag(BiomeTag.class, "as_biome", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), BiomeTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "BiomeTag", attribute.hasAlternative());
    });
    // <--[tag]
    // @attribute <ElementTag.as_chunk>
    // @returns ChunkTag
    // @group conversion
    // @description
    // Returns the element as a chunk. Note: the value must be a valid chunk.
    // -->
    PropertyParser.<BukkitElementProperties, ChunkTag>registerTag(ChunkTag.class, "as_chunk", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), ChunkTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "ChunkTag", attribute.hasAlternative());
    }, "aschunk");
    // <--[tag]
    // @attribute <ElementTag.as_color>
    // @returns ColorTag
    // @group conversion
    // @description
    // Returns the element as a ColorTag. Note: the value must be a valid color.
    // -->
    PropertyParser.<BukkitElementProperties, ColorTag>registerStaticTag(ColorTag.class, "as_color", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), ColorTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "ColorTag", attribute.hasAlternative());
    }, "ascolor");
    // <--[tag]
    // @attribute <ElementTag.as_cuboid>
    // @returns CuboidTag
    // @group conversion
    // @description
    // Returns the element as a cuboid. Note: the value must be a valid cuboid.
    // -->
    PropertyParser.<BukkitElementProperties, CuboidTag>registerTag(CuboidTag.class, "as_cuboid", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), CuboidTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "CuboidTag", attribute.hasAlternative());
    }, "ascuboid");
    // <--[tag]
    // @attribute <ElementTag.as_ellipsoid>
    // @returns EllipsoidTag
    // @group conversion
    // @description
    // Returns the element as an EllipsoidTag. Note: the value must be a valid ellipsoid.
    // -->
    PropertyParser.<BukkitElementProperties, EllipsoidTag>registerTag(EllipsoidTag.class, "as_ellipsoid", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), EllipsoidTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "EllipsoidTag", attribute.hasAlternative());
    });
    // <--[tag]
    // @attribute <ElementTag.as_enchantment>
    // @returns EnchantmentTag
    // @group conversion
    // @description
    // Returns the element as an EnchantmentTag. Note: the value must be a valid enchantment.
    // -->
    PropertyParser.<BukkitElementProperties, EnchantmentTag>registerStaticTag(EnchantmentTag.class, "as_enchantment", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), EnchantmentTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "EnchantmentTag", attribute.hasAlternative());
    });
    // <--[tag]
    // @attribute <ElementTag.as_entity>
    // @returns EntityTag
    // @group conversion
    // @description
    // Returns the element as an entity. Note: the value must be a valid entity.
    // -->
    PropertyParser.<BukkitElementProperties, EntityTag>registerTag(EntityTag.class, "as_entity", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), EntityTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "EntityTag", attribute.hasAlternative());
    }, "asentity");
    // <--[tag]
    // @attribute <ElementTag.as_inventory>
    // @returns InventoryTag
    // @group conversion
    // @description
    // Returns the element as an inventory. Note: the value must be a valid inventory.
    // -->
    PropertyParser.<BukkitElementProperties, InventoryTag>registerTag(InventoryTag.class, "as_inventory", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), InventoryTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "InventoryTag", attribute.hasAlternative());
    }, "asinventory");
    // <--[tag]
    // @attribute <ElementTag.as_item>
    // @returns ItemTag
    // @group conversion
    // @description
    // Returns the element as an item. Note: the value must be a valid item.
    // -->
    PropertyParser.<BukkitElementProperties, ItemTag>registerTag(ItemTag.class, "as_item", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), ItemTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "ItemTag", attribute.hasAlternative());
    }, "asitem");
    // <--[tag]
    // @attribute <ElementTag.as_location>
    // @returns LocationTag
    // @group conversion
    // @description
    // Returns the element as a location. Note: the value must be a valid location.
    // -->
    PropertyParser.<BukkitElementProperties, LocationTag>registerTag(LocationTag.class, "as_location", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), LocationTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "LocationTag", attribute.hasAlternative());
    }, "aslocation");
    // <--[tag]
    // @attribute <ElementTag.as_material>
    // @returns MaterialTag
    // @group conversion
    // @description
    // Returns the element as a material. Note: the value must be a valid material.
    // -->
    PropertyParser.<BukkitElementProperties, MaterialTag>registerStaticTag(MaterialTag.class, "as_material", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), MaterialTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "MaterialTag", attribute.hasAlternative());
    }, "asmaterial");
    // <--[tag]
    // @attribute <ElementTag.as_npc>
    // @returns NPCTag
    // @group conversion
    // @description
    // Returns the element as an NPC. Note: the value must be a valid NPC.
    // -->
    PropertyParser.<BukkitElementProperties, NPCTag>registerTag(NPCTag.class, "as_npc", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), NPCTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "NPCTag", attribute.hasAlternative());
    }, "asnpc");
    // <--[tag]
    // @attribute <ElementTag.as_player>
    // @returns PlayerTag
    // @group conversion
    // @description
    // Returns the element as a player. Note: the value must be a valid player. Can be online or offline.
    // -->
    PropertyParser.<BukkitElementProperties, PlayerTag>registerTag(PlayerTag.class, "as_player", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), PlayerTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "PlayerTag", attribute.hasAlternative());
    }, "asplayer");
    // <--[tag]
    // @attribute <ElementTag.as_plugin>
    // @returns PluginTag
    // @group conversion
    // @description
    // Returns the element as a plugin. Note: the value must be a valid plugin.
    // -->
    PropertyParser.<BukkitElementProperties, PluginTag>registerStaticTag(PluginTag.class, "as_plugin", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), PluginTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "PluginTag", attribute.hasAlternative());
    }, "asplugin");
    // <--[tag]
    // @attribute <ElementTag.as_polygon>
    // @returns PolygonTag
    // @group conversion
    // @description
    // Returns the element as a PolygonTag. Note: the value must be a valid polygon.
    // -->
    PropertyParser.<BukkitElementProperties, PolygonTag>registerTag(PolygonTag.class, "as_polygon", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), PolygonTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "PolygonTag", attribute.hasAlternative());
    });
    // <--[tag]
    // @attribute <ElementTag.as_trade>
    // @returns TradeTag
    // @group conversion
    // @description
    // Returns the element as a TradeTag. Note: the value must be a valid trade.
    // -->
    PropertyParser.<BukkitElementProperties, TradeTag>registerStaticTag(TradeTag.class, "as_trade", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), TradeTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "TradeTag", attribute.hasAlternative());
    });
    // <--[tag]
    // @attribute <ElementTag.as_world>
    // @returns WorldTag
    // @group conversion
    // @description
    // Returns the element as a world. Note: the value must be a valid world.
    // -->
    PropertyParser.<BukkitElementProperties, WorldTag>registerTag(WorldTag.class, "as_world", (attribute, object) -> {
        return ElementTag.handleNull(object.asString(), WorldTag.valueOf(object.asString(), new BukkitTagContext(attribute.getScriptEntry())), "WorldTag", attribute.hasAlternative());
    }, "asworld");
    // <--[tag]
    // @attribute <ElementTag.format[<script>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Returns the text re-formatted according to a format script.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerTag(ElementTag.class, "format", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        FormatScriptContainer format = ScriptRegistry.getScriptContainer(attribute.getParam());
        if (format == null) {
            attribute.echoError("Could not find format script matching '" + attribute.getParam() + "'");
            return null;
        } else {
            return new ElementTag(format.getFormattedText(object.asString(), attribute.getScriptEntry() != null ? ((BukkitScriptEntryData) attribute.getScriptEntry().entryData).getNPC() : null, attribute.getScriptEntry() != null ? ((BukkitScriptEntryData) attribute.getScriptEntry().entryData).getPlayer() : null));
        }
    });
    // <--[tag]
    // @attribute <ElementTag.split_lines_by_width[<#>]>
    // @returns ElementTag
    // @group element manipulation
    // @description
    // Returns the element split into separate lines based on a maximum width in pixels per line.
    // This uses character width, so for example 20 "W"s and 20 "i"s will be treated differently.
    // The width used is based on the vanilla minecraft font. This will not be accurate for other fonts.
    // This only currently supports ASCII symbols properly. Unicode symbols will be estimated as 6 pixels.
    // Spaces will be preferred to become newlines, unless a line does not contain any spaces.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "split_lines_by_width", (attribute, object) -> {
        int width = attribute.getIntParam();
        return new ElementTag(TextWidthHelper.splitLines(object.asString(), width));
    });
    // <--[tag]
    // @attribute <ElementTag.text_width>
    // @returns ElementTag(Number)
    // @group element manipulation
    // @description
    // Returns the width, in pixels, of the text.
    // The width used is based on the vanilla minecraft font. This will not be accurate for other fonts.
    // This only currently supports ASCII symbols properly. Unicode symbols will be estimated as 6 pixels.
    // If the element contains newlines, will return the widest line width.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "text_width", (attribute, object) -> {
        return new ElementTag(TextWidthHelper.getWidth(object.asString()));
    });
    // <--[tag]
    // @attribute <ElementTag.lines_to_colored_list>
    // @returns ListTag
    // @group element manipulation
    // @description
    // Returns a list of lines in the element, with colors spread over the lines manually.
    // Useful for things like item lore.
    // -->
    PropertyParser.<BukkitElementProperties, ListTag>registerStaticTag(ListTag.class, "lines_to_colored_list", (attribute, object) -> {
        ListTag output = new ListTag();
        String colors = "";
        for (String line : CoreUtilities.split(object.asString(), '\n')) {
            output.add(colors + line);
            colors = org.bukkit.ChatColor.getLastColors(colors + line);
        }
        return output;
    });
    // <--[tag]
    // @attribute <ElementTag.last_color>
    // @returns ElementTag
    // @group text checking
    // @description
    // Returns the ChatColors used last in an element.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "last_color", (attribute, object) -> {
        return new ElementTag(org.bukkit.ChatColor.getLastColors(object.asString()));
    });
    // <--[tag]
    // @attribute <ElementTag.strip_color>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Returns the element with all color encoding stripped.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "strip_color", (attribute, object) -> {
        return new ElementTag(FormattedTextHelper.parse(object.asString(), ChatColor.WHITE)[0].toPlainText());
    });
    // <--[tag]
    // @attribute <ElementTag.parse_color[(<prefix>)]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Returns the element with all color codes parsed.
    // Optionally, specify a character to prefix the color ids. Defaults to '&' if not specified.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "parse_color", (attribute, object) -> {
        char prefix = '&';
        if (attribute.hasParam()) {
            prefix = attribute.getParam().charAt(0);
        }
        String parsed = ChatColor.translateAlternateColorCodes(prefix, object.asString());
        parsed = replaceEssentialsHexColors(prefix, parsed);
        return new ElementTag(parsed);
    });
    // <--[tag]
    // @attribute <ElementTag.to_itemscript_hash>
    // @returns ElementTag
    // @group conversion
    // @description
    // Shortens the element down to an itemscript hash ID, made of invisible color codes.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerTag(ElementTag.class, "to_itemscript_hash", (attribute, object) -> {
        return new ElementTag(ItemScriptHelper.createItemScriptID(object.asString()));
    });
    // <--[tag]
    // @attribute <ElementTag.to_secret_colors>
    // @returns ElementTag
    // @group conversion
    // @description
    // Hides the element's text in invisible color codes.
    // Inverts <@link tag ElementTag.from_secret_colors>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "to_secret_colors", (attribute, object) -> {
        String text = object.asString();
        byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
        String hex = CoreUtilities.hexEncode(bytes);
        StringBuilder colors = new StringBuilder(text.length() * 2);
        for (int i = 0; i < hex.length(); i++) {
            colors.append(ChatColor.COLOR_CHAR).append(hex.charAt(i));
        }
        return new ElementTag(colors.toString());
    });
    // <--[tag]
    // @attribute <ElementTag.from_secret_colors>
    // @returns ElementTag
    // @group conversion
    // @description
    // Un-hides the element's text from invisible color codes back to normal text.
    // Inverts <@link tag ElementTag.to_secret_colors>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "from_secret_colors", (attribute, object) -> {
        String text = object.asString().replace(String.valueOf(ChatColor.COLOR_CHAR), "");
        byte[] bytes = CoreUtilities.hexDecode(text);
        return new ElementTag(new String(bytes, StandardCharsets.UTF_8));
    });
    // <--[tag]
    // @attribute <ElementTag.to_raw_json>
    // @returns ElementTag
    // @group conversion
    // @description
    // Converts normal colored text to Minecraft-style "raw JSON" format.
    // Inverts <@link tag ElementTag.from_raw_json>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "to_raw_json", (attribute, object) -> {
        return new ElementTag(ComponentSerializer.toString(FormattedTextHelper.parse(object.asString(), ChatColor.WHITE)));
    });
    // <--[tag]
    // @attribute <ElementTag.from_raw_json>
    // @returns ElementTag
    // @group conversion
    // @description
    // Un-hides the element's text from invisible color codes back to normal text.
    // Inverts <@link tag ElementTag.to_raw_json>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "from_raw_json", (attribute, object) -> {
        return new ElementTag(FormattedTextHelper.stringify(ComponentSerializer.parse(object.asString()), ChatColor.WHITE));
    });
    // <--[tag]
    // @attribute <ElementTag.on_hover[<message>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Adds a hover message to the element, which makes the element display the input hover text when the mouse is left over it.
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerTag(ElementTag.class, "on_hover", (attribute, object) -> {
        // non-static due to hacked sub-tag
        if (!attribute.hasParam()) {
            return null;
        }
        String hoverText = attribute.getParam();
        String type = "SHOW_TEXT";
        // -->
        if (attribute.startsWith("type", 2)) {
            type = attribute.getContext(2);
            attribute.fulfill(1);
        }
        return new ElementTag(ChatColor.COLOR_CHAR + "[hover=" + type + ";" + FormattedTextHelper.escape(hoverText) + "]" + object.asString() + ChatColor.COLOR_CHAR + "[/hover]");
    });
    // <--[tag]
    // @attribute <ElementTag.on_click[<command>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Adds a click command to the element, which makes the element execute the input command when clicked.
    // To execute a command "/" should be used at the start. Otherwise, it will display as chat.
    // For example: - narrate "You can <element[click here].on_click[wow]> to say wow!"
    // For example: - narrate "You can <element[click here].on_click[/help]> for help!"
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerTag(ElementTag.class, "on_click", (attribute, object) -> {
        // non-static due to hacked sub-tag
        if (!attribute.hasParam()) {
            return null;
        }
        String clickText = attribute.getParam();
        String type = "RUN_COMMAND";
        // -->
        if (attribute.startsWith("type", 2)) {
            type = attribute.getContext(2);
            attribute.fulfill(1);
        }
        return new ElementTag(ChatColor.COLOR_CHAR + "[click=" + type + ";" + FormattedTextHelper.escape(clickText) + "]" + object.asString() + ChatColor.COLOR_CHAR + "[/click]");
    });
    // <--[tag]
    // @attribute <ElementTag.with_insertion[<message>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Adds an insertion message to the element, which makes the element insert the input message to chat when shift-clicked.
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "with_insertion", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        String insertionText = attribute.getParam();
        return new ElementTag(ChatColor.COLOR_CHAR + "[insertion=" + FormattedTextHelper.escape(insertionText) + "]" + object.asString() + ChatColor.COLOR_CHAR + "[/insertion]");
    });
    // <--[tag]
    // @attribute <ElementTag.no_reset>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes a color code (&0123456789abcdef) not reset other formatting details.
    // Use like '<&c.no_reset>' or '<red.no_reset>'.
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "no_reset", (attribute, object) -> {
        if (object.asString().length() == 2 && object.asString().charAt(0) == ChatColor.COLOR_CHAR) {
            return new ElementTag(ChatColor.COLOR_CHAR + "[color=" + object.asString().charAt(1) + "]");
        }
        return null;
    });
    // <--[tag]
    // @attribute <ElementTag.end_format>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes a chat format code (&klmno, or &[font=...]) be the end of a format, as opposed to the start.
    // Use like '<&o.end_format>' or '<italic.end_format>'.
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "end_format", (attribute, object) -> {
        if (object.asString().length() == 2 && object.asString().charAt(0) == ChatColor.COLOR_CHAR) {
            return new ElementTag(ChatColor.COLOR_CHAR + "[reset=" + object.asString().charAt(1) + "]");
        } else if (object.asString().startsWith(ChatColor.COLOR_CHAR + "[font=") && object.asString().endsWith("]")) {
            return new ElementTag(ChatColor.COLOR_CHAR + "[reset=font]");
        }
        return null;
    });
    // <--[tag]
    // @attribute <ElementTag.italicize>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text italic. Equivalent to "<&o><ELEMENT_HERE><&o.end_format>"
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "italicize", (attribute, object) -> {
        return new ElementTag(ChatColor.ITALIC + object.asString() + ChatColor.COLOR_CHAR + "[reset=o]");
    });
    // <--[tag]
    // @attribute <ElementTag.bold>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text bold. Equivalent to "<&l><ELEMENT_HERE><&l.end_format>"
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "bold", (attribute, object) -> {
        return new ElementTag(ChatColor.BOLD + object.asString() + ChatColor.COLOR_CHAR + "[reset=l]");
    });
    // <--[tag]
    // @attribute <ElementTag.underline>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text underlined. Equivalent to "<&n><ELEMENT_HERE><&n.end_format>"
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "underline", (attribute, object) -> {
        return new ElementTag(ChatColor.UNDERLINE + object.asString() + ChatColor.COLOR_CHAR + "[reset=n]");
    });
    // <--[tag]
    // @attribute <ElementTag.strikethrough>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text struck-through. Equivalent to "<&m><ELEMENT_HERE><&m.end_format>"
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "strikethrough", (attribute, object) -> {
        return new ElementTag(ChatColor.STRIKETHROUGH + object.asString() + ChatColor.COLOR_CHAR + "[reset=m]");
    });
    // <--[tag]
    // @attribute <ElementTag.obfuscate>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text obfuscated. Equivalent to "<&k><ELEMENT_HERE><&k.end_format>"
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "obfuscate", (attribute, object) -> {
        return new ElementTag(ChatColor.MAGIC + object.asString() + ChatColor.COLOR_CHAR + "[reset=k]");
    });
    // <--[tag]
    // @attribute <ElementTag.custom_color[<name>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text colored by the custom color value based on the common base color names defined in the Denizen config file.
    // If the color name is unrecognized, returns the value of color named 'default'.
    // Default color names are 'base', 'emphasis', 'warning', 'error'.
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "custom_color", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        return new ElementTag(ChatColor.COLOR_CHAR + "[color=f]" + CustomColorTagBase.getColor(attribute.getParam(), attribute.context) + object.asString() + ChatColor.COLOR_CHAR + "[reset=color]");
    });
    // <--[tag]
    // @attribute <ElementTag.color[<color>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text colored by the input color. Equivalent to "<COLOR><ELEMENT_HERE><COLOR.end_format>"
    // Color can be a color name, color code, hex, or ColorTag... that is: ".color[gold]", ".color[6]", and ".color[#AABB00]" are all valid.
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "color", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        String colorName = attribute.getParam();
        String colorOut = null;
        if (colorName.length() == 1) {
            ChatColor color = ChatColor.getByChar(colorName.charAt(0));
            if (color != null) {
                colorOut = color.toString();
            }
        } else if (colorName.length() == 7 && colorName.startsWith("#")) {
            return new ElementTag(ChatColor.COLOR_CHAR + "[color=" + colorName + "]" + object.asString() + ChatColor.COLOR_CHAR + "[reset=color]");
        } else if (colorName.length() == 14 && colorName.startsWith(ChatColor.COLOR_CHAR + "x")) {
            return new ElementTag(ChatColor.COLOR_CHAR + "[color=#" + CoreUtilities.replace(colorName.substring(2), String.valueOf(ChatColor.COLOR_CHAR), "") + "]" + object.asString() + ChatColor.COLOR_CHAR + "[reset=color]");
        } else if (colorName.startsWith("co@")) {
            ColorTag color = ColorTag.valueOf(colorName, attribute.context);
            StringBuilder hex = new StringBuilder(Integer.toHexString(color.getColor().asRGB()));
            while (hex.length() < 6) {
                hex.insert(0, "0");
            }
            return new ElementTag(ChatColor.COLOR_CHAR + "[color=#" + hex + "]" + object.asString() + ChatColor.COLOR_CHAR + "[reset=color]");
        }
        if (colorOut == null) {
            try {
                ChatColor color = ChatColor.of(colorName.toUpperCase());
                String colorStr = color.toString().replace(String.valueOf(ChatColor.COLOR_CHAR), "").replace("x", "#");
                colorOut = ChatColor.COLOR_CHAR + "[color=" + colorStr + "]";
            } catch (IllegalArgumentException ex) {
                attribute.echoError("Color '" + colorName + "' doesn't exist (for ElementTag.color[...]).");
                return null;
            }
        }
        return new ElementTag(colorOut + object.asString() + ChatColor.COLOR_CHAR + "[reset=color]");
    });
    // <--[tag]
    // @attribute <ElementTag.font[<font>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Makes the input text display with the input font name. Equivalent to "<&font[new-font]><ELEMENT_HERE><&font[new-font].end_format>"
    // The default font is "minecraft:default".
    // Note that this is a magic Denizen tool - refer to <@link language Denizen Text Formatting>.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "font", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        String fontName = attribute.getParam();
        return new ElementTag(ChatColor.COLOR_CHAR + "[font=" + fontName + "]" + object.asString() + ChatColor.COLOR_CHAR + "[reset=font]");
    });
    // <--[tag]
    // @attribute <ElementTag.rainbow[(<pattern>)]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Returns the element with rainbow colors applied.
    // Optionally, specify a color pattern to follow. By default, this is "4c6e2ab319d5".
    // That is, a repeating color of: Red, Orange, Yellow, Green, Cyan, Blue, Purple.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "rainbow", (attribute, object) -> {
        String str = object.asString();
        String pattern = "4c6e2ab319d5";
        if (attribute.hasParam()) {
            pattern = attribute.getParam();
        }
        StringBuilder output = new StringBuilder(str.length() * 3);
        for (int i = 0; i < str.length(); i++) {
            output.append(ChatColor.COLOR_CHAR).append(pattern.charAt(i % pattern.length())).append(str.charAt(i));
        }
        return new ElementTag(output.toString());
    });
    // <--[tag]
    // @attribute <ElementTag.hex_rainbow[(<length>)]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Returns the element with RGB rainbow colors applied.
    // Optionally, specify a length (how many characters before the colors repeat). If unspecified, will use the input element length.
    // If the element starts with a hex color code, that will be used as the starting color of the rainbow.
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "hex_rainbow", (attribute, object) -> {
        String str = object.asString();
        int[] HSB = new int[] { 0, 255, 255 };
        if (str.startsWith(ChatColor.COLOR_CHAR + "x") && str.length() > 14) {
            char[] colors = new char[6];
            for (int i = 0; i < 6; i++) {
                colors[i] = str.charAt(3 + (i * 2));
            }
            int rgb = Integer.parseInt(new String(colors), 16);
            HSB = new ColorTag(Color.fromRGB(rgb)).toHSB();
            str = str.substring(14);
        }
        float hue = HSB[0] / 255f;
        int length = ChatColor.stripColor(str).length();
        if (length == 0) {
            return new ElementTag("");
        }
        if (attribute.hasParam()) {
            length = attribute.getIntParam();
        }
        float increment = 1.0f / length;
        String addedFormat = "";
        StringBuilder output = new StringBuilder(str.length() * 8);
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c == ChatColor.COLOR_CHAR && i + 1 < str.length()) {
                char c2 = str.charAt(i + 1);
                if (FORMAT_CODES_MATCHER.isMatch(c2)) {
                    addedFormat += String.valueOf(ChatColor.COLOR_CHAR) + c2;
                } else {
                    addedFormat = "";
                }
                i++;
                continue;
            }
            String hex = Integer.toHexString(ColorTag.fromHSB(HSB).getColor().asRGB());
            output.append(FormattedTextHelper.stringifyRGBSpigot(hex)).append(addedFormat).append(c);
            hue += increment;
            HSB[0] = Math.round(hue * 255f);
        }
        return new ElementTag(output.toString());
    });
    // <--[tag]
    // @attribute <ElementTag.color_gradient[from=<color>;to=<color>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Returns the element with an RGB color gradient applied - tends to produce smooth gradients, as opposed to <@link tag ElementTag.hsb_color_gradient>,
    // with a unique color per character.
    // Specify the input as a map with keys 'from' and 'to' both set to hex colors (or any valid ColorTag).
    // For example: <element[these are the shades of gray].color_gradient[from=white;to=black]>
    // Or: <element[this looks kinda like fire doesn't it].color_gradient[from=#FF0000;to=#FFFF00]>
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "color_gradient", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        String str = object.asString();
        int length = ChatColor.stripColor(str).length();
        if (length == 0) {
            return new ElementTag("");
        }
        MapTag inputMap = attribute.paramAsType(MapTag.class);
        if (inputMap == null) {
            return null;
        }
        ObjectTag fromObj = inputMap.getObject("from");
        ObjectTag toObj = inputMap.getObject("to");
        if (fromObj == null || toObj == null) {
            return null;
        }
        ColorTag fromColor = fromObj.asType(ColorTag.class, attribute.context);
        ColorTag toColor = toObj.asType(ColorTag.class, attribute.context);
        if (fromColor == null || toColor == null) {
            return null;
        }
        float red = ColorTag.fromSRGB(fromColor.getColor().getRed());
        float green = ColorTag.fromSRGB(fromColor.getColor().getGreen());
        float blue = ColorTag.fromSRGB(fromColor.getColor().getBlue());
        float targetRed = ColorTag.fromSRGB(toColor.getColor().getRed());
        float targetGreen = ColorTag.fromSRGB(toColor.getColor().getGreen());
        float targetBlue = ColorTag.fromSRGB(toColor.getColor().getBlue());
        float brightness = (float) Math.pow(red + green + blue, 0.43);
        float toBrightness = (float) Math.pow(targetRed + targetGreen + targetBlue, 0.43);
        float brightnessMove = (toBrightness - brightness) / length;
        float redMove = (targetRed - red) / length;
        float greenMove = (targetGreen - green) / length;
        float blueMove = (targetBlue - blue) / length;
        String addedFormat = "";
        StringBuilder output = new StringBuilder(str.length() * 15);
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c == ChatColor.COLOR_CHAR && i + 1 < str.length()) {
                char c2 = str.charAt(i + 1);
                if (FORMAT_CODES_MATCHER.isMatch(c2)) {
                    addedFormat += String.valueOf(ChatColor.COLOR_CHAR) + c2;
                } else {
                    addedFormat = "";
                }
                i++;
                continue;
            }
            // Based on https://stackoverflow.com/questions/22607043/color-gradient-algorithm/49321304#49321304
            float newRed = red, newGreen = green, newBlue = blue;
            float sum = newRed + newGreen + newBlue;
            if (sum > 0) {
                float multiplier = (float) Math.pow(brightness, 1f / 0.43f) / sum;
                newRed *= multiplier;
                newGreen *= multiplier;
                newBlue *= multiplier;
            }
            newRed = ColorTag.toSRGB(newRed);
            newGreen = ColorTag.toSRGB(newGreen);
            newBlue = ColorTag.toSRGB(newBlue);
            String hex = Integer.toHexString((((int) newRed) << 16) | (((int) newGreen) << 8) | ((int) newBlue));
            output.append(FormattedTextHelper.stringifyRGBSpigot(hex)).append(addedFormat).append(str.charAt(i));
            brightness += brightnessMove;
            red += redMove;
            green += greenMove;
            blue += blueMove;
        }
        return new ElementTag(output.toString());
    });
    // <--[tag]
    // @attribute <ElementTag.hsb_color_gradient[from=<color>;to=<color>]>
    // @returns ElementTag
    // @group text manipulation
    // @description
    // Returns the element with an HSB color gradient applied - tends to produce bright rainbow-like color patterns, as opposed to <@link tag ElementTag.color_gradient>,
    // with a unique color per character.
    // Specify the input as a map with keys 'from' and 'to' both set to hex colors (or any valid ColorTag).
    // -->
    PropertyParser.<BukkitElementProperties, ElementTag>registerStaticTag(ElementTag.class, "hsb_color_gradient", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        String str = object.asString();
        int length = ChatColor.stripColor(str).length();
        if (length == 0) {
            return new ElementTag("");
        }
        MapTag inputMap = attribute.paramAsType(MapTag.class);
        if (inputMap == null) {
            return null;
        }
        ObjectTag fromObj = inputMap.getObject("from");
        ObjectTag toObj = inputMap.getObject("to");
        if (fromObj == null || toObj == null) {
            return null;
        }
        ColorTag fromColor = fromObj.asType(ColorTag.class, attribute.context);
        ColorTag toColor = toObj.asType(ColorTag.class, attribute.context);
        if (fromColor == null || toColor == null) {
            return null;
        }
        int[] fromHSB = fromColor.toHSB();
        int[] toHSB = toColor.toHSB();
        float hue = fromHSB[0];
        float saturation = fromHSB[1];
        float brightness = fromHSB[2];
        float targetHue = toHSB[0];
        float targetSaturation = toHSB[1];
        float targetBrightness = toHSB[2];
        float hueMove = (targetHue - hue) / length;
        float saturationMove = (targetSaturation - saturation) / length;
        float brightnessMove = (targetBrightness - brightness) / length;
        String addedFormat = "";
        StringBuilder output = new StringBuilder(str.length() * 15);
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c == ChatColor.COLOR_CHAR && i + 1 < str.length()) {
                char c2 = str.charAt(i + 1);
                if (FORMAT_CODES_MATCHER.isMatch(c2)) {
                    addedFormat += String.valueOf(ChatColor.COLOR_CHAR) + c2;
                } else {
                    addedFormat = "";
                }
                i++;
                continue;
            }
            fromHSB[0] = (int) hue;
            fromHSB[1] = (int) saturation;
            fromHSB[2] = (int) brightness;
            ColorTag currentColor = ColorTag.fromHSB(fromHSB);
            String hex = Integer.toHexString(currentColor.getColor().asRGB());
            output.append(FormattedTextHelper.stringifyRGBSpigot(hex)).append(addedFormat).append(c);
            hue += hueMove;
            saturation += saturationMove;
            brightness += brightnessMove;
        }
        return new ElementTag(output.toString());
    });
}
Also used : FormatScriptContainer(com.denizenscript.denizen.scripts.containers.core.FormatScriptContainer) MapTag(com.denizenscript.denizencore.objects.core.MapTag) ObjectTag(com.denizenscript.denizencore.objects.ObjectTag) ListTag(com.denizenscript.denizencore.objects.core.ListTag) BukkitTagContext(com.denizenscript.denizen.tags.BukkitTagContext) ElementTag(com.denizenscript.denizencore.objects.core.ElementTag) ChatColor(net.md_5.bungee.api.ChatColor)

Example 63 with ListTag

use of com.denizenscript.denizencore.objects.core.ListTag in project Denizen-For-Bukkit by DenizenScript.

the class EntityAreaEffectCloud method getObjectAttribute.

@Override
public ObjectTag getObjectAttribute(Attribute attribute) {
    if (attribute == null) {
        return null;
    }
    // -->
    if (attribute.startsWith("base_potion")) {
        attribute = attribute.fulfill(1);
        // -->
        if (attribute.startsWith("type")) {
            return new ElementTag(getHelper().getBPName()).getObjectAttribute(attribute.fulfill(1));
        }
        // -->
        if (attribute.startsWith("is_upgraded")) {
            return new ElementTag(getHelper().getBPUpgraded()).getObjectAttribute(attribute.fulfill(1));
        }
        // -->
        if (attribute.startsWith("is_extended")) {
            return new ElementTag(getHelper().getBPExtended()).getObjectAttribute(attribute.fulfill(1));
        }
        return new ElementTag(getHelper().getBPName() + "," + getHelper().getBPUpgraded() + "," + getHelper().getBPExtended()).getObjectAttribute(attribute);
    }
    // -->
    if (attribute.startsWith("particle")) {
        attribute = attribute.fulfill(1);
        // -->
        if (attribute.startsWith("color")) {
            return new ColorTag(getHelper().getColor()).getObjectAttribute(attribute.fulfill(1));
        }
        return new ElementTag(getHelper().getParticle()).getObjectAttribute(attribute);
    }
    // -->
    if (attribute.startsWith("duration")) {
        attribute = attribute.fulfill(1);
        // -->
        if (attribute.startsWith("on_use")) {
            return new DurationTag(getHelper().getDurationOnUse()).getObjectAttribute(attribute.fulfill(1));
        }
        return new DurationTag(getHelper().getDuration()).getObjectAttribute(attribute);
    }
    // -->
    if (attribute.startsWith("radius")) {
        attribute = attribute.fulfill(1);
        // -->
        if (attribute.startsWith("on_use")) {
            return new ElementTag(getHelper().getRadiusOnUse()).getObjectAttribute(attribute.fulfill(1));
        }
        // -->
        if (attribute.startsWith("per_tick")) {
            return new ElementTag(getHelper().getRadiusPerTick()).getObjectAttribute(attribute.fulfill(1));
        }
        return new ElementTag(getHelper().getRadius()).getObjectAttribute(attribute);
    }
    // -->
    if (attribute.startsWith("reapplication_delay")) {
        return new DurationTag(getHelper().getReappDelay()).getObjectAttribute(attribute.fulfill(1));
    }
    // -->
    if (attribute.startsWith("wait_time")) {
        return new DurationTag(getHelper().getWaitTime()).getObjectAttribute(attribute.fulfill(1));
    }
    // -->
    if (attribute.startsWith("has_custom_effect")) {
        if (attribute.hasParam()) {
            PotionEffectType effectType = PotionEffectType.getByName(attribute.getParam());
            for (PotionEffect effect : getHelper().getCustomEffects()) {
                if (effect.getType().equals(effectType)) {
                    return new ElementTag(true).getObjectAttribute(attribute.fulfill(1));
                }
            }
            return new ElementTag(false).getObjectAttribute(attribute.fulfill(1));
        }
        return new ElementTag(getHelper().hasCustomEffects()).getObjectAttribute(attribute.fulfill(1));
    }
    // -->
    if (attribute.startsWith("source")) {
        ProjectileSource shooter = getHelper().getSource();
        if (shooter instanceof LivingEntity) {
            return new EntityTag((LivingEntity) shooter).getObjectAttribute(attribute.fulfill(1));
        }
    }
    // -->
    if (attribute.startsWith("custom_effects")) {
        List<PotionEffect> effects = getHelper().getCustomEffects();
        if (!attribute.hasParam()) {
            ListTag list = new ListTag();
            for (PotionEffect effect : effects) {
                list.add(effect.getType().getName() + "," + effect.getAmplifier() + "," + new DurationTag((long) effect.getDuration()).identify() + "," + effect.isAmbient() + "," + effect.hasParticles());
            }
            return list.getObjectAttribute(attribute.fulfill(1));
        }
        int val = attribute.getIntParam() - 1;
        if (val < 0 || val >= effects.size()) {
            return null;
        }
        attribute = attribute.fulfill(1);
        PotionEffect effect = effects.get(val);
        // -->
        if (attribute.startsWith("type")) {
            return new ElementTag(effect.getType().getName()).getObjectAttribute(attribute.fulfill(1));
        }
        // -->
        if (attribute.startsWith("amplifier")) {
            return new ElementTag(effect.getAmplifier()).getObjectAttribute(attribute.fulfill(1));
        }
        // -->
        if (attribute.startsWith("duration")) {
            return new DurationTag((long) effect.getDuration()).getObjectAttribute(attribute.fulfill(1));
        }
        // -->
        if (attribute.startsWith("has_particles")) {
            return new ElementTag(effect.hasParticles()).getObjectAttribute(attribute.fulfill(1));
        }
        // -->
        if (attribute.startsWith("is_ambient")) {
            return new ElementTag(effect.isAmbient()).getObjectAttribute(attribute.fulfill(1));
        }
        return new ElementTag(effect.getType().getName() + "," + effect.getAmplifier() + "," + new DurationTag((long) effect.getDuration()).identify() + "," + effect.isAmbient() + "," + effect.hasParticles()).getObjectAttribute(attribute);
    }
    return null;
}
Also used : LivingEntity(org.bukkit.entity.LivingEntity) PotionEffect(org.bukkit.potion.PotionEffect) PotionEffectType(org.bukkit.potion.PotionEffectType) ColorTag(com.denizenscript.denizen.objects.ColorTag) EntityTag(com.denizenscript.denizen.objects.EntityTag) ElementTag(com.denizenscript.denizencore.objects.core.ElementTag) ProjectileSource(org.bukkit.projectiles.ProjectileSource) DurationTag(com.denizenscript.denizencore.objects.core.DurationTag) ListTag(com.denizenscript.denizencore.objects.core.ListTag)

Example 64 with ListTag

use of com.denizenscript.denizencore.objects.core.ListTag in project Denizen-For-Bukkit by DenizenScript.

the class InventoryTag method registerTags.

public static void registerTags() {
    AbstractFlagTracker.registerFlagHandlers(tagProcessor);
    PropertyParser.registerPropertyTagHandlers(InventoryTag.class, tagProcessor);
    // <--[tag]
    // @attribute <InventoryTag.empty_slots>
    // @returns ElementTag(Number)
    // @description
    // Returns the number of empty slots in an inventory.
    // -->
    tagProcessor.registerTag(ElementTag.class, "empty_slots", (attribute, object) -> {
        InventoryTag dummyInv;
        if (object.inventory.getType() == InventoryType.PLAYER) {
            ItemStack[] contents = object.getStorageContents();
            dummyInv = new InventoryTag(contents.length);
            if (contents.length != dummyInv.getSize()) {
                contents = Arrays.copyOf(contents, dummyInv.getSize());
            }
            dummyInv.setContents(contents);
        } else {
            dummyInv = object;
        }
        int full = dummyInv.count(null, true);
        return new ElementTag(dummyInv.getSize() - full);
    });
    // <--[tag]
    // @attribute <InventoryTag.can_fit[<item>|...]>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether the inventory can fit an item.
    // -->
    tagProcessor.registerTag(ElementTag.class, "can_fit", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        List<ItemTag> items = attribute.paramAsType(ListTag.class).filter(ItemTag.class, attribute.context, !attribute.hasAlternative());
        if (items == null || items.isEmpty()) {
            return null;
        }
        InventoryType type = object.inventory.getType();
        InventoryTag dummyInv = new InventoryTag(type == InventoryType.PLAYER ? InventoryType.CHEST : type, AdvancedTextImpl.instance.getTitle(object.inventory));
        ItemStack[] contents = object.getStorageContents();
        if (dummyInv.getInventoryType() == InventoryType.CHEST) {
            dummyInv.setSize(contents.length);
        }
        if (contents.length != dummyInv.getSize()) {
            contents = Arrays.copyOf(contents, dummyInv.getSize());
        }
        dummyInv.setContents(contents);
        // -->
        if (attribute.startsWith("count", 2)) {
            ItemStack toAdd = items.get(0).getItemStack().clone();
            // Technically nothing stops us from ridiculous numbers in an ItemStack amount.
            int totalCount = 64 * 64 * 4;
            toAdd.setAmount(totalCount);
            List<ItemStack> leftovers = dummyInv.addWithLeftovers(0, true, toAdd);
            int result = 0;
            if (leftovers.size() > 0) {
                result += leftovers.get(0).getAmount();
            }
            attribute.fulfill(1);
            return new ElementTag(totalCount - result);
        }
        // -->
        if ((attribute.startsWith("quantity", 2) || attribute.startsWith("qty", 2)) && attribute.hasContext(2)) {
            if (attribute.startsWith("qty", 2)) {
                Deprecations.qtyTags.warn(attribute.context);
            }
            int qty = attribute.getIntContext(2);
            ItemTag itemZero = new ItemTag(items.get(0).getItemStack().clone());
            itemZero.setAmount(qty);
            items.set(0, itemZero);
            attribute.fulfill(1);
        }
        // NOTE: Could just also convert items to an array and pass it all in at once...
        for (ItemTag itm : items) {
            List<ItemStack> leftovers = dummyInv.addWithLeftovers(0, true, itm.getItemStack().clone());
            if (!leftovers.isEmpty()) {
                return new ElementTag(false);
            }
        }
        return new ElementTag(true);
    });
    // <--[tag]
    // @attribute <InventoryTag.include[<item>|...]>
    // @returns InventoryTag
    // @description
    // Returns a copy of the InventoryTag with items added.
    // -->
    tagProcessor.registerTag(InventoryTag.class, "include", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        List<ItemTag> items = ListTag.getListFor(attribute.getParamObject(), attribute.context).filter(ItemTag.class, attribute.context);
        InventoryTag dummyInv = new InventoryTag(object.inventory.getType(), AdvancedTextImpl.instance.getTitle(object.inventory));
        if (object.inventory.getType() == InventoryType.CHEST) {
            dummyInv.setSize(object.inventory.getSize());
        }
        dummyInv.setContents(object.getContents());
        if (object.idHolder instanceof ScriptTag) {
            dummyInv.idType = "script";
            dummyInv.idHolder = object.idHolder;
        }
        trackTemporaryInventory(dummyInv);
        // -->
        if ((attribute.startsWith("quantity", 2) || attribute.startsWith("qty", 2)) && attribute.hasContext(2)) {
            if (attribute.startsWith("qty", 2)) {
                Deprecations.qtyTags.warn(attribute.context);
            }
            int qty = attribute.getIntContext(2);
            ItemTag itemZero = new ItemTag(items.get(0).getItemStack().clone());
            itemZero.setAmount(qty);
            items.set(0, itemZero);
            attribute.fulfill(1);
        }
        for (ItemTag item : items) {
            dummyInv.add(0, item.getItemStack().clone());
        }
        return dummyInv;
    });
    // <--[tag]
    // @attribute <InventoryTag.exclude_item[<item_matcher>]>
    // @returns InventoryTag
    // @description
    // Returns a copy of the InventoryTag with all matching items excluded.
    // -->
    tagProcessor.registerTag(InventoryTag.class, "exclude_item", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        String matcher = attribute.getParam();
        InventoryTag dummyInv = new InventoryTag(object.inventory.getType(), AdvancedTextImpl.instance.getTitle(object.inventory));
        if (object.inventory.getType() == InventoryType.CHEST) {
            dummyInv.setSize(object.inventory.getSize());
        }
        dummyInv.setContents(object.getContents());
        if (object.idHolder instanceof ScriptTag) {
            dummyInv.idType = "script";
            dummyInv.idHolder = object.idHolder;
        }
        trackTemporaryInventory(dummyInv);
        int quantity = Integer.MAX_VALUE;
        // -->
        if (attribute.startsWith("quantity", 2) && attribute.hasContext(2)) {
            quantity = attribute.getIntContext(2);
            attribute.fulfill(1);
        }
        for (int slot = 0; slot < dummyInv.inventory.getSize(); slot++) {
            ItemStack item = dummyInv.inventory.getItem(slot);
            if (item != null && BukkitScriptEvent.tryItem(new ItemTag(item), matcher)) {
                quantity -= item.getAmount();
                if (quantity >= 0) {
                    dummyInv.inventory.setItem(slot, null);
                } else {
                    item = item.clone();
                    item.setAmount(-quantity);
                    dummyInv.inventory.setItem(slot, item);
                }
                if (quantity <= 0) {
                    break;
                }
            }
        }
        return dummyInv;
    });
    tagProcessor.registerTag(InventoryTag.class, "exclude", (attribute, object) -> {
        Deprecations.inventoryNonMatcherTags.warn(attribute.context);
        if (!attribute.hasParam()) {
            return null;
        }
        List<ItemTag> items = ListTag.getListFor(attribute.getParamObject(), attribute.context).filter(ItemTag.class, attribute.context);
        InventoryTag dummyInv = new InventoryTag(object.inventory.getType(), AdvancedTextImpl.instance.getTitle(object.inventory));
        if (object.inventory.getType() == InventoryType.CHEST) {
            dummyInv.setSize(object.inventory.getSize());
        }
        dummyInv.setContents(object.getContents());
        if (object.idHolder instanceof ScriptTag) {
            dummyInv.idType = "script";
            dummyInv.idHolder = object.idHolder;
        }
        trackTemporaryInventory(dummyInv);
        if ((attribute.startsWith("quantity", 2) || attribute.startsWith("qty", 2)) && attribute.hasContext(2)) {
            if (attribute.startsWith("qty", 2)) {
                Deprecations.qtyTags.warn(attribute.context);
            }
            int qty = attribute.getIntContext(2);
            ItemTag itemZero = new ItemTag(items.get(0).getItemStack().clone());
            itemZero.setAmount(qty);
            items.set(0, itemZero);
            attribute.fulfill(1);
        }
        for (ItemTag item : items) {
            dummyInv.inventory.removeItem(item.getItemStack().clone());
        }
        return dummyInv;
    });
    // <--[tag]
    // @attribute <InventoryTag.is_empty>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether the inventory is empty.
    // -->
    tagProcessor.registerTag(ElementTag.class, "is_empty", (attribute, object) -> {
        boolean empty = true;
        for (ItemStack item : object.getStorageContents()) {
            if (item != null && item.getType() != Material.AIR) {
                empty = false;
                break;
            }
        }
        return new ElementTag(empty);
    });
    // <--[tag]
    // @attribute <InventoryTag.is_full>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether the inventory is completely full.
    // -->
    tagProcessor.registerTag(ElementTag.class, "is_full", (attribute, object) -> {
        boolean full = true;
        for (ItemStack item : object.getStorageContents()) {
            if ((item == null) || (item.getType() == Material.AIR) || (item.getAmount() < item.getMaxStackSize())) {
                full = false;
                break;
            }
        }
        return new ElementTag(full);
    });
    // <--[tag]
    // @attribute <InventoryTag.contains_item[<matcher>]>
    // @returns ElementTag(Boolean)
    // @description
    // Returns whether the inventory contains any item that matches the specified item matcher.
    // Uses the system behind <@link language Advanced Script Event Matching>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "contains_item", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        int qty = 1;
        String matcher = attribute.getParam();
        // -->
        if (attribute.startsWith("quantity", 2) && attribute.hasContext(2)) {
            qty = attribute.getIntContext(2);
            attribute.fulfill(1);
        }
        int found_items = 0;
        for (ItemStack item : object.getContents()) {
            if (item != null) {
                if (BukkitScriptEvent.tryItem(new ItemTag(item), matcher)) {
                    found_items += item.getAmount();
                    if (found_items >= qty) {
                        break;
                    }
                }
            }
        }
        return new ElementTag(found_items >= qty);
    });
    tagProcessor.registerTag(ElementTag.class, "contains", (attribute, object) -> {
        // -->
        if (attribute.startsWith("display", 2)) {
            if (!attribute.hasContext(2)) {
                return null;
            }
            String search_string = attribute.getContext(2);
            boolean strict = false;
            if (CoreUtilities.toLowerCase(search_string).startsWith("strict:") && search_string.length() > 7) {
                strict = true;
                search_string = search_string.substring(7);
            }
            if (search_string.length() == 0) {
                return null;
            }
            int qty = 1;
            // -->
            if ((attribute.startsWith("quantity", 3) || attribute.startsWith("qty", 3)) && attribute.hasContext(3)) {
                if (attribute.startsWith("qty", 3)) {
                    Deprecations.qtyTags.warn(attribute.context);
                }
                qty = attribute.getIntContext(3);
                attribute.fulfill(1);
            }
            int found_items = 0;
            if (strict) {
                for (ItemStack item : object.getContents()) {
                    if (item == null || !item.hasItemMeta()) {
                        continue;
                    }
                    ItemMeta meta = item.getItemMeta();
                    if (item.getType() == Material.WRITTEN_BOOK && ((BookMeta) meta).getTitle().equalsIgnoreCase(search_string)) {
                        found_items += item.getAmount();
                        if (found_items >= qty) {
                            break;
                        }
                    } else if (meta.hasDisplayName() && meta.getDisplayName().equalsIgnoreCase(search_string)) {
                        found_items += item.getAmount();
                        if (found_items >= qty) {
                            break;
                        }
                    }
                }
            } else {
                for (ItemStack item : object.getContents()) {
                    if (item == null || !item.hasItemMeta()) {
                        continue;
                    }
                    ItemMeta meta = item.getItemMeta();
                    if (item.getType() == Material.WRITTEN_BOOK && CoreUtilities.toLowerCase(((BookMeta) meta).getTitle()).contains(CoreUtilities.toLowerCase(search_string))) {
                        found_items += item.getAmount();
                        if (found_items >= qty) {
                            break;
                        }
                    } else if (meta.hasDisplayName() && CoreUtilities.toLowerCase(meta.getDisplayName()).contains(CoreUtilities.toLowerCase(search_string))) {
                        found_items += item.getAmount();
                        if (found_items >= qty) {
                            break;
                        }
                    }
                }
            }
            attribute.fulfill(1);
            return new ElementTag(found_items >= qty);
        }
        // -->
        if (attribute.startsWith("lore", 2)) {
            if (!attribute.hasContext(2)) {
                return null;
            }
            String search_string = attribute.getContext(2);
            boolean strict = false;
            if (CoreUtilities.toLowerCase(search_string).startsWith("strict:")) {
                strict = true;
                search_string = search_string.substring("strict:".length());
            }
            if (search_string.length() == 0) {
                return null;
            }
            ListTag lore = ListTag.valueOf(search_string, attribute.context);
            int qty = 1;
            // -->
            if ((attribute.startsWith("quantity", 3) || attribute.startsWith("qty", 3)) && attribute.hasContext(3)) {
                if (attribute.startsWith("qty", 3)) {
                    Deprecations.qtyTags.warn(attribute.context);
                }
                qty = attribute.getIntContext(3);
                attribute.fulfill(1);
            }
            int found_items = 0;
            if (strict) {
                strict_items: for (ItemStack item : object.getContents()) {
                    if (item == null || !item.hasItemMeta()) {
                        continue;
                    }
                    ItemMeta meta = item.getItemMeta();
                    if (meta.hasLore()) {
                        List<String> item_lore = meta.getLore();
                        if (lore.size() != item_lore.size()) {
                            continue;
                        }
                        for (int i = 0; i < item_lore.size(); i++) {
                            if (!lore.get(i).equalsIgnoreCase(item_lore.get(i))) {
                                continue strict_items;
                            }
                        }
                        found_items += item.getAmount();
                        if (found_items >= qty) {
                            break;
                        }
                    }
                }
            } else {
                for (ItemStack item : object.getContents()) {
                    if (item == null || !item.hasItemMeta()) {
                        continue;
                    }
                    ItemMeta meta = item.getItemMeta();
                    if (meta.hasLore()) {
                        List<String> item_lore = meta.getLore();
                        int loreCount = 0;
                        lines: for (String line : lore) {
                            for (String item_line : item_lore) {
                                if (CoreUtilities.toLowerCase(item_line).contains(CoreUtilities.toLowerCase(line))) {
                                    loreCount++;
                                    continue lines;
                                }
                            }
                        }
                        if (loreCount == lore.size()) {
                            found_items += item.getAmount();
                            if (found_items >= qty) {
                                break;
                            }
                        }
                    }
                }
            }
            attribute.fulfill(1);
            return new ElementTag(found_items >= qty);
        }
        if (attribute.startsWith("scriptname", 2)) {
            Deprecations.inventoryNonMatcherTags.warn(attribute.context);
            if (!attribute.hasContext(2)) {
                return null;
            }
            ListTag scrNameList = attribute.contextAsType(2, ListTag.class);
            HashSet<String> scrNames = new HashSet<>();
            for (String name : scrNameList) {
                scrNames.add(CoreUtilities.toLowerCase(name));
            }
            int qty = 1;
            if ((attribute.startsWith("quantity", 3) || attribute.startsWith("qty", 3)) && attribute.hasContext(3)) {
                if (attribute.startsWith("qty", 3)) {
                    Deprecations.qtyTags.warn(attribute.context);
                }
                qty = attribute.getIntContext(3);
                attribute.fulfill(1);
            }
            int found_items = 0;
            for (ItemStack item : object.getContents()) {
                if (item != null) {
                    String itemName = new ItemTag(item).getScriptName();
                    if (itemName != null && scrNames.contains(CoreUtilities.toLowerCase(itemName))) {
                        found_items += item.getAmount();
                        if (found_items >= qty) {
                            break;
                        }
                    }
                }
            }
            attribute.fulfill(1);
            return new ElementTag(found_items >= qty);
        }
        if (attribute.startsWith("flagged", 2)) {
            Deprecations.inventoryNonMatcherTags.warn(attribute.context);
            if (!attribute.hasContext(2)) {
                return null;
            }
            ListTag scrNameList = attribute.contextAsType(2, ListTag.class);
            String[] flags = scrNameList.toArray(new String[0]);
            int qty = 1;
            if (attribute.startsWith("quantity", 3) && attribute.hasContext(3)) {
                qty = attribute.getIntContext(3);
                attribute.fulfill(1);
            }
            int found_items = 0;
            for (ItemStack item : object.getContents()) {
                if (item != null) {
                    ItemTag itemTag = new ItemTag(item);
                    for (String flag : flags) {
                        if (itemTag.getFlagTracker().hasFlag(flag)) {
                            found_items += item.getAmount();
                            break;
                        }
                    }
                    if (found_items >= qty) {
                        break;
                    }
                }
            }
            attribute.fulfill(1);
            return new ElementTag(found_items >= qty);
        }
        if (attribute.startsWith("nbt", 2)) {
            Deprecations.itemNbt.warn(attribute.context);
            if (!attribute.hasContext(2)) {
                return null;
            }
            String keyName = attribute.getContext(2);
            int qty = 1;
            if ((attribute.startsWith("quantity", 3) || attribute.startsWith("qty", 3)) && attribute.hasContext(3)) {
                if (attribute.startsWith("qty", 3)) {
                    Deprecations.qtyTags.warn(attribute.context);
                }
                qty = attribute.getIntContext(3);
                attribute.fulfill(1);
            }
            int found_items = 0;
            for (ItemStack item : object.getContents()) {
                if (CustomNBT.hasCustomNBT(item, keyName, CustomNBT.KEY_DENIZEN)) {
                    found_items += item.getAmount();
                    if (found_items >= qty) {
                        break;
                    }
                }
            }
            attribute.fulfill(1);
            return new ElementTag(found_items >= qty);
        }
        if (attribute.startsWith("material", 2)) {
            Deprecations.inventoryNonMatcherTags.warn(attribute.context);
            if (!attribute.hasContext(2)) {
                return null;
            }
            List<MaterialTag> materials = attribute.contextAsType(2, ListTag.class).filter(MaterialTag.class, attribute.context);
            int qty = 1;
            if ((attribute.startsWith("quantity", 3) || attribute.startsWith("qty", 3)) && attribute.hasContext(3)) {
                if (attribute.startsWith("qty", 3)) {
                    Deprecations.qtyTags.warn(attribute.context);
                }
                qty = attribute.getIntContext(3);
                attribute.fulfill(1);
            }
            int found_items = 0;
            mainLoop: for (ItemStack item : object.getContents()) {
                if (item == null) {
                    continue;
                }
                for (MaterialTag material : materials) {
                    if (item.getType() == material.getMaterial() && !(new ItemTag(item).isItemscript())) {
                        found_items += item.getAmount();
                        if (found_items >= qty) {
                            break mainLoop;
                        }
                    }
                }
            }
            attribute.fulfill(1);
            return new ElementTag(found_items >= qty);
        }
        if (!attribute.hasParam()) {
            return null;
        }
        ListTag list = attribute.paramAsType(ListTag.class);
        if (list.isEmpty()) {
            return null;
        }
        int qty = 1;
        Deprecations.inventoryNonMatcherTags.warn(attribute.context);
        if ((attribute.startsWith("quantity", 2) || attribute.startsWith("qty", 2)) && attribute.hasContext(2)) {
            if (attribute.startsWith("qty", 2)) {
                Deprecations.qtyTags.warn(attribute.context);
            }
            qty = attribute.getIntContext(2);
            attribute.fulfill(1);
        }
        List<ItemTag> contains = list.filter(ItemTag.class, attribute.context, !attribute.hasAlternative());
        if (contains.size() == list.size()) {
            for (ItemTag item : contains) {
                if (!object.containsItem(item, qty)) {
                    return new ElementTag(false);
                }
            }
            return new ElementTag(true);
        }
        return new ElementTag(false);
    });
    tagProcessor.registerTag(ElementTag.class, "contains_any", (attribute, object) -> {
        Deprecations.inventoryNonMatcherTags.warn(attribute.context);
        if (!attribute.hasParam()) {
            return null;
        }
        ListTag list = attribute.paramAsType(ListTag.class);
        if (list.isEmpty()) {
            return null;
        }
        int qty = 1;
        if ((attribute.startsWith("quantity", 2) || attribute.startsWith("qty", 2)) && attribute.hasContext(2)) {
            if (attribute.startsWith("qty", 2)) {
                Deprecations.qtyTags.warn(attribute.context);
            }
            qty = attribute.getIntContext(2);
            attribute.fulfill(1);
        }
        List<ItemTag> contains = list.filter(ItemTag.class, attribute.context, !attribute.hasAlternative());
        if (!contains.isEmpty()) {
            for (ItemTag item : contains) {
                if (object.containsItem(item, qty)) {
                    return new ElementTag(true);
                }
            }
        }
        return new ElementTag(false);
    });
    // <--[tag]
    // @attribute <InventoryTag.first_empty>
    // @returns ElementTag(Number)
    // @description
    // Returns the location of the first empty slot.
    // Returns -1 if the inventory is full.
    // -->
    tagProcessor.registerTag(ElementTag.class, "first_empty", (attribute, object) -> {
        int val = object.firstEmpty(0);
        return new ElementTag(val >= 0 ? (val + 1) : -1);
    });
    // <--[tag]
    // @attribute <InventoryTag.find_item[<matcher>]>
    // @returns ElementTag(Number)
    // @description
    // Returns the location of the first slot that contains an item that matches the given item matcher.
    // Returns -1 if there's no match.
    // Uses the system behind <@link language Advanced Script Event Matching>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "find_item", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        String matcher = attribute.getParam();
        for (int i = 0; i < object.inventory.getSize(); i++) {
            ItemStack item = object.inventory.getItem(i);
            if (item != null) {
                if (BukkitScriptEvent.tryItem(new ItemTag(item), matcher)) {
                    return new ElementTag(i + 1);
                }
            }
        }
        return new ElementTag(-1);
    });
    // <--[tag]
    // @attribute <InventoryTag.find_all_items[<matcher>]>
    // @returns ListTag
    // @description
    // Returns a list of the location of all slots that contains an item that matches the given item matcher.
    // Returns an empty list if there's no match.
    // Uses the system behind <@link language Advanced Script Event Matching>.
    // -->
    tagProcessor.registerTag(ListTag.class, "find_all_items", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        ListTag result = new ListTag();
        String matcher = attribute.getParam();
        for (int i = 0; i < object.inventory.getSize(); i++) {
            ItemStack item = object.inventory.getItem(i);
            if (item != null) {
                if (BukkitScriptEvent.tryItem(new ItemTag(item), matcher)) {
                    result.addObject(new ElementTag(i + 1));
                }
            }
        }
        return result;
    });
    tagProcessor.registerTag(ElementTag.class, "find", (attribute, object) -> {
        Deprecations.inventoryNonMatcherTags.warn(attribute.context);
        if (attribute.startsWith("material", 2)) {
            ListTag list = attribute.contextAsType(2, ListTag.class);
            if (list == null) {
                return null;
            }
            HashSet<Material> materials = new HashSet<>();
            for (ObjectTag obj : list.objectForms) {
                materials.add(obj.asType(MaterialTag.class, attribute.context).getMaterial());
            }
            int slot = -1;
            for (int i = 0; i < object.inventory.getSize(); i++) {
                if (object.inventory.getItem(i) != null && materials.contains(object.inventory.getItem(i).getType())) {
                    slot = i + 1;
                    break;
                }
            }
            attribute.fulfill(1);
            return new ElementTag(slot);
        }
        if (attribute.startsWith("scriptname", 2)) {
            String scrname = attribute.contextAsType(2, ItemTag.class).getScriptName();
            if (scrname == null) {
                return null;
            }
            int slot = -1;
            for (int i = 0; i < object.inventory.getSize(); i++) {
                if (object.inventory.getItem(i) != null && scrname.equalsIgnoreCase(new ItemTag(object.inventory.getItem(i)).getScriptName())) {
                    slot = i + 1;
                    break;
                }
            }
            attribute.fulfill(1);
            return new ElementTag(slot);
        }
        if (!attribute.hasParam() || !ItemTag.matches(attribute.getParam())) {
            return null;
        }
        ItemTag item = attribute.paramAsType(ItemTag.class);
        item.setAmount(1);
        int slot = -1;
        for (int i = 0; i < object.inventory.getSize(); i++) {
            if (object.inventory.getItem(i) != null) {
                ItemTag compare_to = new ItemTag(object.inventory.getItem(i).clone());
                compare_to.setAmount(1);
                if (item.identify().equalsIgnoreCase(compare_to.identify())) {
                    slot = i + 1;
                    break;
                }
            }
        }
        return new ElementTag(slot);
    });
    tagProcessor.registerTag(ElementTag.class, "find_imperfect", (attribute, object) -> {
        Deprecations.inventoryNonMatcherTags.warn(attribute.context);
        if (!attribute.hasParam() || !ItemTag.matches(attribute.getParam())) {
            return null;
        }
        ItemTag item = attribute.paramAsType(ItemTag.class);
        item.setAmount(1);
        int slot = -1;
        for (int i = 0; i < object.inventory.getSize(); i++) {
            if (object.inventory.getItem(i) != null) {
                ItemTag compare_to = new ItemTag(object.inventory.getItem(i).clone());
                compare_to.setAmount(1);
                if (item.identify().equalsIgnoreCase(compare_to.identify()) || item.getScriptName().equalsIgnoreCase(compare_to.getScriptName())) {
                    slot = i + 1;
                    break;
                }
            }
        }
        return new ElementTag(slot);
    });
    // <--[tag]
    // @attribute <InventoryTag.id_type>
    // @returns ElementTag
    // @description
    // Returns Denizen's type ID for this inventory (player, location, etc.).
    // -->
    tagProcessor.registerTag(ElementTag.class, "id_type", (attribute, object) -> {
        return new ElementTag(object.idType);
    });
    // <--[tag]
    // @attribute <InventoryTag.note_name>
    // @returns ElementTag
    // @description
    // Gets the name of a noted InventoryTag. If the inventory isn't noted, this is null.
    // -->
    tagProcessor.registerTag(ElementTag.class, "note_name", (attribute, object) -> {
        String noteName = NoteManager.getSavedId(object);
        if (noteName == null) {
            return null;
        }
        return new ElementTag(noteName);
    }, "notable_name");
    // <--[tag]
    // @attribute <InventoryTag.location>
    // @returns LocationTag
    // @description
    // Returns the location of this inventory's holder.
    // -->
    tagProcessor.registerTag(LocationTag.class, "location", (attribute, object) -> {
        return object.getLocation();
    });
    // <--[tag]
    // @attribute <InventoryTag.quantity_item[(<matcher>)]>
    // @returns ElementTag(Number)
    // @description
    // Returns the combined quantity of itemstacks that match an item matcher if one is specified,
    // or the combined quantity of all itemstacks if one is not.
    // Uses the system behind <@link language Advanced Script Event Matching>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "quantity_item", (attribute, object) -> {
        String matcher = attribute.hasParam() ? attribute.getParam() : null;
        int found_items = 0;
        for (ItemStack item : object.getContents()) {
            if (item != null) {
                if (matcher == null || BukkitScriptEvent.tryItem(new ItemTag(item), matcher)) {
                    found_items += item.getAmount();
                }
            }
        }
        return new ElementTag(found_items);
    });
    tagProcessor.registerTag(ElementTag.class, "quantity", (attribute, object) -> {
        Deprecations.inventoryNonMatcherTags.warn(attribute.context);
        if (attribute.startsWith("scriptname", 2)) {
            if (!attribute.hasContext(2)) {
                return null;
            }
            String scriptName = attribute.getContext(2);
            attribute.fulfill(1);
            return new ElementTag(object.countByScriptName(scriptName));
        }
        if (attribute.startsWith("flagged", 2)) {
            if (!attribute.hasContext(2)) {
                return null;
            }
            String flag = attribute.getContext(2);
            attribute.fulfill(1);
            return new ElementTag(object.countByFlag(flag));
        }
        if (attribute.startsWith("material", 2)) {
            if (!attribute.hasContext(2) || !MaterialTag.matches(attribute.getContext(2))) {
                return null;
            }
            MaterialTag material = attribute.contextAsType(2, MaterialTag.class);
            attribute.fulfill(1);
            return new ElementTag(object.countByMaterial(material.getMaterial()));
        }
        if (attribute.hasParam() && ItemTag.matches(attribute.getParam())) {
            return new ElementTag(object.count(attribute.paramAsType(ItemTag.class).getItemStack(), false));
        } else {
            return new ElementTag(object.count(null, false));
        }
    }, "qty");
    // <--[tag]
    // @attribute <InventoryTag.stacks[(<item>)]>
    // @returns ElementTag(Number)
    // @description
    // Returns the number of itemstacks that match an item if one is specified, or the number of all itemstacks if one is not.
    // -->
    tagProcessor.registerTag(ElementTag.class, "stacks", (attribute, object) -> {
        if (attribute.hasParam() && ItemTag.matches(attribute.getParam())) {
            return new ElementTag(object.count(attribute.paramAsType(ItemTag.class).getItemStack(), true));
        } else {
            return new ElementTag(object.count(null, true));
        }
    });
    // <--[tag]
    // @attribute <InventoryTag.slot[<#>|...]>
    // @returns ObjectTag
    // @description
    // If one slot is specified, returns the ItemTag in the specified slot.
    // If more than one slot is specified, returns a ListTag(ItemTag) of the item in each given slot.
    // -->
    tagProcessor.registerTag(ObjectTag.class, "slot", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        ListTag slots = ListTag.getListFor(attribute.getParamObject(), attribute.context);
        if (slots.isEmpty()) {
            if (!attribute.hasAlternative()) {
                Debug.echoError("Cannot get a list of zero slots.");
            }
            return null;
        } else if (slots.size() == 1) {
            int slot = SlotHelper.nameToIndexFor(attribute.getParam(), object.getInventory().getHolder());
            if (slot < 0) {
                slot = 0;
            } else if (slot > object.getInventory().getSize() - 1) {
                slot = object.getInventory().getSize() - 1;
            }
            return new ItemTag(object.getInventory().getItem(slot));
        } else {
            ListTag result = new ListTag();
            for (String slotText : slots) {
                int slot = SlotHelper.nameToIndexFor(slotText, object.getInventory().getHolder());
                if (slot < 0) {
                    slot = 0;
                } else if (slot > object.getInventory().getSize() - 1) {
                    slot = object.getInventory().getSize() - 1;
                }
                result.addObject(new ItemTag(object.getInventory().getItem(slot)));
            }
            return result;
        }
    });
    // <--[tag]
    // @attribute <InventoryTag.inventory_type>
    // @returns ElementTag
    // @description
    // Returns the type of the inventory (e.g. "PLAYER", "CRAFTING", "HORSE").
    // -->
    tagProcessor.registerTag(ElementTag.class, "inventory_type", (attribute, object) -> {
        return new ElementTag(object.inventory instanceof HorseInventory ? "HORSE" : object.getInventory().getType().name());
    });
    // <--[tag]
    // @attribute <InventoryTag.equipment_map>
    // @returns MapTag
    // @description
    // Returns a MapTag containing the inventory's equipment.
    // Output keys for players are boots, leggings,  chestplate, helmet.
    // Output keys for horses are saddle, armor.
    // Air items will be left out of the map.
    // -->
    tagProcessor.registerTag(MapTag.class, "equipment_map", (attribute, object) -> {
        return object.getEquipmentMap();
    });
    // <--[tag]
    // @attribute <InventoryTag.equipment>
    // @returns ListTag(ItemTag)
    // @description
    // Returns the equipment of an inventory as a list of items.
    // For players, the order is boots|leggings|chestplate|helmet.
    // For horses, the order is saddle|armor.
    // -->
    tagProcessor.registerTag(ListTag.class, "equipment", (attribute, object) -> {
        return object.getEquipment();
    });
    // <--[tag]
    // @attribute <InventoryTag.matrix>
    // @returns ListTag(ItemTag)
    // @mechanism InventoryTag.matrix
    // @description
    // Returns the items currently in a crafting inventory's matrix.
    // -->
    tagProcessor.registerTag(ListTag.class, "matrix", (attribute, object) -> {
        if (!(object.inventory instanceof CraftingInventory)) {
            return null;
        }
        ListTag recipeList = new ListTag();
        for (ItemStack item : ((CraftingInventory) object.inventory).getMatrix()) {
            if (item != null) {
                recipeList.addObject(new ItemTag(item));
            } else {
                recipeList.addObject(new ItemTag(Material.AIR));
            }
        }
        return recipeList;
    });
    // <--[tag]
    // @attribute <InventoryTag.recipe>
    // @returns ElementTag
    // @description
    // Returns the recipe ID for the recipe currently formed in a crafting inventory.
    // Returns a list in the Namespace:Key format, for example "minecraft:stick".
    // -->
    tagProcessor.registerTag(ElementTag.class, "recipe", (attribute, object) -> {
        Recipe recipe;
        if ((object.inventory instanceof CraftingInventory)) {
            recipe = ((CraftingInventory) object.inventory).getRecipe();
        } else {
            return null;
        }
        if (recipe == null) {
            return null;
        }
        return new ElementTag(((Keyed) recipe).getKey().toString());
    });
    // <--[tag]
    // @attribute <InventoryTag.craftable_quantity>
    // @returns ElementTag(Number)
    // @description
    // Returns the quantity of items that would be received if this crafting inventory were fully crafted (eg via a shift click).
    // -->
    tagProcessor.registerTag(ElementTag.class, "craftable_quantity", (attribute, object) -> {
        Recipe recipe;
        if ((object.inventory instanceof CraftingInventory)) {
            recipe = ((CraftingInventory) object.inventory).getRecipe();
        } else {
            return null;
        }
        if (recipe == null) {
            return null;
        }
        return new ElementTag(RecipeHelper.getMaximumOutputQuantity(recipe, (CraftingInventory) object.inventory) * recipe.getResult().getAmount());
    });
    // <--[tag]
    // @attribute <InventoryTag.result>
    // @returns ItemTag
    // @mechanism InventoryTag.result
    // @description
    // Returns the item currently in the result section of a crafting inventory or furnace inventory.
    // -->
    tagProcessor.registerTag(ItemTag.class, "result", (attribute, object) -> {
        ItemStack result;
        if ((object.inventory instanceof CraftingInventory)) {
            result = ((CraftingInventory) object.inventory).getResult();
        } else if ((object.inventory instanceof FurnaceInventory)) {
            result = ((FurnaceInventory) object.inventory).getResult();
        } else {
            return null;
        }
        if (result == null) {
            return null;
        }
        return new ItemTag(result);
    });
    // <--[tag]
    // @attribute <InventoryTag.anvil_repair_cost>
    // @returns ElementTag(Number)
    // @mechanism InventoryTag.anvil_repair_cost
    // @description
    // Returns the current repair cost on an anvil.
    // -->
    tagProcessor.registerTag(ElementTag.class, "anvil_repair_cost", (attribute, object) -> {
        if (!(object.inventory instanceof AnvilInventory)) {
            return null;
        }
        return new ElementTag(((AnvilInventory) object.inventory).getRepairCost());
    });
    // <--[tag]
    // @attribute <InventoryTag.anvil_max_repair_cost>
    // @returns ElementTag(Number)
    // @mechanism InventoryTag.anvil_max_repair_cost
    // @description
    // Returns the maximum repair cost on an anvil.
    // -->
    tagProcessor.registerTag(ElementTag.class, "anvil_max_repair_cost", (attribute, object) -> {
        if (!(object.inventory instanceof AnvilInventory)) {
            return null;
        }
        return new ElementTag(((AnvilInventory) object.inventory).getMaximumRepairCost());
    });
    // <--[tag]
    // @attribute <InventoryTag.anvil_rename_text>
    // @returns ElementTag
    // @description
    // Returns the current entered renaming text on an anvil.
    // -->
    tagProcessor.registerTag(ElementTag.class, "anvil_rename_text", (attribute, object) -> {
        if (!(object.inventory instanceof AnvilInventory)) {
            return null;
        }
        return new ElementTag(((AnvilInventory) object.inventory).getRenameText(), true);
    });
    // <--[tag]
    // @attribute <InventoryTag.fuel>
    // @returns ItemTag
    // @mechanism InventoryTag.fuel
    // @description
    // Returns the item currently in the fuel section of a furnace or brewing stand inventory.
    // -->
    tagProcessor.registerTag(ItemTag.class, "fuel", (attribute, object) -> {
        if (object.getInventory() instanceof FurnaceInventory) {
            return new ItemTag(((FurnaceInventory) object.getInventory()).getFuel());
        }
        if (object.getInventory() instanceof BrewerInventory) {
            return new ItemTag(((BrewerInventory) object.getInventory()).getFuel());
        }
        return null;
    });
    // <--[tag]
    // @attribute <InventoryTag.input>
    // @returns ItemTag
    // @mechanism InventoryTag.input
    // @description
    // Returns the item currently in the smelting slot of a furnace inventory, or the ingredient slot of a brewing stand inventory.
    // -->
    tagProcessor.registerTag(ItemTag.class, "input", (attribute, object) -> {
        if (object.getInventory() instanceof FurnaceInventory) {
            return new ItemTag(((FurnaceInventory) object.getInventory()).getSmelting());
        }
        if (object.getInventory() instanceof BrewerInventory) {
            return new ItemTag(((BrewerInventory) object.getInventory()).getIngredient());
        }
        return null;
    });
    tagProcessor.registerFutureTagDeprecation("input", "smelting");
    // <--[tag]
    // @attribute <InventoryTag.advanced_matches[<matcher>]>
    // @returns ElementTag(Boolean)
    // @group element checking
    // @description
    // Returns whether the inventory matches some matcher text, using the system behind <@link language Advanced Script Event Matching>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "advanced_matches", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        return new ElementTag(BukkitScriptEvent.tryInventory(object, attribute.getParam()));
    });
    // <--[tag]
    // @attribute <InventoryTag.viewers>
    // @returns ListTag(PlayerTag)
    // @description
    // Returns a list of players viewing the inventory.
    // -->
    tagProcessor.registerTag(ListTag.class, "viewers", (attribute, object) -> {
        ListTag list = new ListTag();
        for (HumanEntity viewer : object.getInventory().getViewers()) {
            if (!EntityTag.isNPC(viewer) && viewer instanceof Player) {
                list.addObject(new PlayerTag((Player) viewer));
            }
        }
        return list;
    });
}
Also used : Keyed(org.bukkit.Keyed) HumanEntity(org.bukkit.entity.HumanEntity) ItemMeta(org.bukkit.inventory.meta.ItemMeta) Player(org.bukkit.entity.Player) ImprovedOfflinePlayer(com.denizenscript.denizen.nms.abstracts.ImprovedOfflinePlayer) InventoryType(org.bukkit.event.inventory.InventoryType) Material(org.bukkit.Material) ListTag(com.denizenscript.denizencore.objects.core.ListTag) ScriptTag(com.denizenscript.denizencore.objects.core.ScriptTag) ElementTag(com.denizenscript.denizencore.objects.core.ElementTag) BookMeta(org.bukkit.inventory.meta.BookMeta)

Example 65 with ListTag

use of com.denizenscript.denizencore.objects.core.ListTag in project Denizen-For-Bukkit by DenizenScript.

the class ItemTag method registerTags.

public static void registerTags() {
    AbstractFlagTracker.registerFlagHandlers(tagProcessor);
    PropertyParser.registerPropertyTagHandlers(ItemTag.class, tagProcessor);
    // <--[tag]
    // @attribute <ItemTag.repairable>
    // @returns ElementTag(Boolean)
    // @group properties
    // @description
    // Returns whether the item can be repaired.
    // If this returns true, it will enable access to:
    // <@link mechanism ItemTag.durability>,
    // <@link tag ItemTag.max_durability>, and <@link tag ItemTag.durability>.
    // Note that due to odd design choices in Spigot, this is effectively true for all items, even though the durability value of most items is locked at zero.
    // -->
    tagProcessor.registerTag(ElementTag.class, "repairable", (attribute, object) -> {
        return new ElementTag(ItemDurability.describes(object));
    });
    // <--[tag]
    // @attribute <ItemTag.is_book>
    // @returns ElementTag(Boolean)
    // @group properties
    // @description
    // Returns whether the item is considered an editable book.
    // If this returns true, it will enable access to:
    // <@link mechanism ItemTag.book>,
    // <@link tag ItemTag.book_author>, <@link tag ItemTag.book_title>, and <@link tag ItemTag.book_pages>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "is_book", (attribute, object) -> {
        return new ElementTag(ItemBook.describes(object));
    });
    // <--[tag]
    // @attribute <ItemTag.is_colorable>
    // @returns ElementTag(Boolean)
    // @group properties
    // @description
    // Returns whether the item can have a custom color.
    // If this returns true, it will enable access to:
    // <@link mechanism ItemTag.color>, and <@link tag ItemTag.color>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "is_colorable", (attribute, object) -> {
        return new ElementTag(ItemColor.describes(object));
    });
    tagProcessor.registerTag(ElementTag.class, "is_dyeable", (attribute, object) -> {
        return new ElementTag(ItemColor.describes(object));
    });
    // <--[tag]
    // @attribute <ItemTag.is_firework>
    // @returns ElementTag(Boolean)
    // @group properties
    // @description
    // Returns whether the item is a firework.
    // If this returns true, it will enable access to:
    // <@link mechanism ItemTag.firework>, and <@link tag ItemTag.firework>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "is_firework", (attribute, object) -> {
        return new ElementTag(ItemFirework.describes(object));
    });
    // <--[tag]
    // @attribute <ItemTag.has_inventory>
    // @returns ElementTag(Boolean)
    // @group properties
    // @description
    // Returns whether the item has an inventory.
    // If this returns true, it will enable access to:
    // <@link mechanism ItemTag.inventory_contents>, and <@link tag ItemTag.inventory_contents>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "has_inventory", (attribute, object) -> {
        return new ElementTag(ItemInventory.describes(object));
    });
    // <--[tag]
    // @attribute <ItemTag.is_lockable>
    // @returns ElementTag(Boolean)
    // @group properties
    // @description
    // Returns whether the item is lockable.
    // If this returns true, it will enable access to:
    // <@link mechanism ItemTag.lock>, and <@link tag ItemTag.lock>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "is_lockable", (attribute, object) -> {
        return new ElementTag(ItemLock.describes(object));
    });
    // <--[tag]
    // @attribute <ItemTag.material>
    // @returns MaterialTag
    // @mechanism ItemTag.material
    // @group conversion
    // @description
    // Returns the MaterialTag that is the basis of the item.
    // EG, a stone with lore and a display name, etc. will return only "m@stone".
    // -->
    tagProcessor.registerTag(ObjectTag.class, "material", (attribute, object) -> {
        if (attribute.getAttribute(2).equals("formatted")) {
            return object;
        }
        if (object.getItemMeta() instanceof BlockStateMeta) {
            if (object.getBukkitMaterial() == Material.SHIELD) {
                MaterialTag material = new MaterialTag(Material.SHIELD);
                material.setModernData(((BlockStateMeta) object.getItemMeta()).getBlockState().getBlockData());
                return material;
            }
            return new MaterialTag(((BlockStateMeta) object.getItemMeta()).getBlockState());
        }
        return object.getMaterial();
    });
    // <--[tag]
    // @attribute <ItemTag.json>
    // @returns ElementTag
    // @group conversion
    // @description
    // Returns the item converted to a raw JSON object with one layer of escaping for network transmission.
    // EG, via /tellraw.
    // Generally, prefer tags like <@link tag ElementTag.on_hover.type> with type 'show_item'.
    // -->
    tagProcessor.registerTag(ElementTag.class, "json", (attribute, object) -> {
        return new ElementTag(NMSHandler.getItemHelper().getJsonString(object.item));
    });
    // <--[tag]
    // @attribute <ItemTag.meta_type>
    // @returns ElementTag
    // @group conversion
    // @description
    // Returns the name of the Bukkit item meta type that applies to this item.
    // This is for debugging purposes.
    // -->
    tagProcessor.registerTag(ElementTag.class, "meta_type", (attribute, object) -> {
        return new ElementTag(object.getItemMeta().getClass().getName());
    });
    // <--[tag]
    // @attribute <ItemTag.bukkit_serial>
    // @returns ElementTag
    // @group conversion
    // @description
    // Returns a YAML text section representing the Bukkit serialization of the item, under subkey "item".
    // -->
    tagProcessor.registerTag(ElementTag.class, "bukkit_serial", (attribute, object) -> {
        YamlConfiguration config = new YamlConfiguration();
        config.set("item", object.getItemStack());
        return new ElementTag(config.saveToString());
    });
    // <--[tag]
    // @attribute <ItemTag.simple>
    // @returns ElementTag
    // @group conversion
    // @description
    // Returns a simple reusable item identification for this item, with minimal extra data.
    // -->
    tagProcessor.registerTag(ElementTag.class, "simple", (attribute, object) -> {
        return new ElementTag(object.identifySimple());
    });
    // <--[tag]
    // @attribute <ItemTag.recipe_ids[(<type>)]>
    // @returns ListTag
    // @description
    // If the item is a scripted item, returns a list of all recipe IDs created by the item script.
    // Others, returns a list of all recipe IDs that the server lists as capable of crafting the item.
    // Returns a list in the Namespace:Key format, for example "minecraft:gold_nugget".
    // Optionally, specify a recipe type (CRAFTING, FURNACE, COOKING, BLASTING, SHAPED, SHAPELESS, SMOKING, STONECUTTING)
    // to limit to just recipes of that type.
    // -->
    tagProcessor.registerTag(ListTag.class, "recipe_ids", (attribute, object) -> {
        String type = attribute.hasParam() ? CoreUtilities.toLowerCase(attribute.getParam()) : null;
        ItemScriptContainer container = object.isItemscript() ? ItemScriptHelper.getItemScriptContainer(object.getItemStack()) : null;
        ListTag list = new ListTag();
        for (Recipe recipe : Bukkit.getRecipesFor(object.getItemStack())) {
            if (!Utilities.isRecipeOfType(recipe, type)) {
                continue;
            }
            if (recipe instanceof Keyed) {
                NamespacedKey key = ((Keyed) recipe).getKey();
                if (key.getNamespace().equalsIgnoreCase("denizen")) {
                    if (container != ItemScriptHelper.recipeIdToItemScript.get(key.toString())) {
                        continue;
                    }
                } else if (container != null) {
                    continue;
                }
                list.add(key.toString());
            }
        }
        return list;
    });
    // <--[tag]
    // @attribute <ItemTag.formatted>
    // @returns ElementTag
    // @group formatting
    // @description
    // Returns the formatted material name of the item to be used in a sentence.
    // Correctly uses singular and plural forms of item names, among other things.
    // -->
    tagProcessor.registerTag(ElementTag.class, "formatted", (attribute, object) -> {
        return new ElementTag(object.formattedName());
    });
    // <--[tag]
    // @attribute <ItemTag.advanced_matches[<matcher>]>
    // @returns ElementTag(Boolean)
    // @group element checking
    // @description
    // Returns whether the item matches some matcher text, using the system behind <@link language Advanced Script Event Matching>.
    // -->
    tagProcessor.registerTag(ElementTag.class, "advanced_matches", (attribute, object) -> {
        if (!attribute.hasParam()) {
            return null;
        }
        return new ElementTag(BukkitScriptEvent.tryItem(object, attribute.getParam()));
    });
}
Also used : BlockStateMeta(org.bukkit.inventory.meta.BlockStateMeta) ItemScriptContainer(com.denizenscript.denizen.scripts.containers.core.ItemScriptContainer) NamespacedKey(org.bukkit.NamespacedKey) ElementTag(com.denizenscript.denizencore.objects.core.ElementTag) YamlConfiguration(org.bukkit.configuration.file.YamlConfiguration) ListTag(com.denizenscript.denizencore.objects.core.ListTag) Keyed(org.bukkit.Keyed)

Aggregations

ListTag (com.denizenscript.denizencore.objects.core.ListTag)122 ElementTag (com.denizenscript.denizencore.objects.core.ElementTag)71 MapTag (com.denizenscript.denizencore.objects.core.MapTag)21 ItemStack (org.bukkit.inventory.ItemStack)18 EntityTag (com.denizenscript.denizen.objects.EntityTag)16 ObjectTag (com.denizenscript.denizencore.objects.ObjectTag)14 List (java.util.List)14 ItemTag (com.denizenscript.denizen.objects.ItemTag)13 PlayerTag (com.denizenscript.denizen.objects.PlayerTag)13 Player (org.bukkit.entity.Player)13 DurationTag (com.denizenscript.denizencore.objects.core.DurationTag)12 LocationTag (com.denizenscript.denizen.objects.LocationTag)11 NPCTag (com.denizenscript.denizen.objects.NPCTag)9 InvalidArgumentsException (com.denizenscript.denizencore.exceptions.InvalidArgumentsException)9 ArrayList (java.util.ArrayList)9 ScriptTag (com.denizenscript.denizencore.objects.core.ScriptTag)8 MaterialTag (com.denizenscript.denizen.objects.MaterialTag)7 ScriptEntry (com.denizenscript.denizencore.scripts.ScriptEntry)7 NPC (net.citizensnpcs.api.npc.NPC)7 Entity (org.bukkit.entity.Entity)7