From ce0cca4005d75e23c7f68a7a51ac218c139d15fa Mon Sep 17 00:00:00 2001 From: altrisi Date: Sun, 29 Nov 2020 02:40:16 +0100 Subject: [PATCH] [Scarpet] Add display_title function (#555) --- docs/scarpet/api/Auxiliary.md | 14 +++- docs/scarpet/resources/editors/idea/2.txt | 1 + .../scarpet/resources/editors/npp/scarpet.xml | 2 + .../java/carpet/script/api/Auxiliary.java | 71 +++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index 3f2ddcd9..8f52ecee 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -245,6 +245,18 @@ Example usages: // not a problem in apps +### `display_title(players, type, title?, fadeInTicks?, stayTicks?, fadeOutTicks),` + +Sends the player (or players if `players` is a list) a title of a specific type, with optionally some times. + * `players` is either an online player or a list of players. When sending a single player, it will throw if the player is invalid or offline. + * `type` is either `'title'`, `'subtitle'`, `actionbar` or `clear`. + Note: `subtitle` will only be displayed if there is a title being displayed (can be an empty one) + * `title` is what title to send to the player. It is required except for `clear` type. Can be a text formatted using `format()` + * `...Ticks` are the number of ticks the title will stay in that state. + If not specified, it will use current defaults (those defaults may have changed from a previous `/title times` execution). + Executing with those will set the times to the specified ones. + Note that `actionbar` type doesn't support changing times (vanilla bug, see [MC-106167](https://bugs.mojang.com/browse/MC-106167)). + ### `logger(msg), logger(type, msg)` Prints the message to system logs, and not to chat. By default prints an info, unless you specify otherwise in the `type` parameter. @@ -469,7 +481,7 @@ Available options in the scarpet app space: * `world_seed` - a numeric seed of the world * `world_path` - full path to the world saves folder * `world_folder` - name of the direct folder in the saves that holds world files - * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_change(rule, newValue)` event. + * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event. Relevant gameplay related properties * `game_difficulty` - current difficulty of the game: `'peacefu'`, `'easy'`, `'normal'`, or `'hard'` diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index ae16592b..05473aff 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -21,6 +21,7 @@ custom_dimension day_time destroy diamond +display_title draw_shape drop_item emitted_light diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 92da9036..f4cd1ddd 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -58,6 +58,8 @@ structure_eligibility structures structure_references set_structure reset_chunk reload_chunk custom_dimension add_chunk_ticket + display_title + scoreboard scoreboard_add scoreboard_remove scoreboard_display team_add team_list team_remove team_leave team_property diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index edf922c3..ab652360 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -43,10 +43,13 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket; +import net.minecraft.network.packet.s2c.play.TitleS2CPacket; +import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action; import net.minecraft.particle.ParticleEffect; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.CommandOutput; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; @@ -455,6 +458,74 @@ public class Auxiliary { Value finalRes = res; return (_c, _t) -> finalRes; // pass through for variables }); + expression.addLazyFunction("display_title", -1, (c, t, lv) -> { + if (lv.size() < 2) throw new InternalExpressionException("'display_title' needs at least a target, type and message, and optionally times"); + Value pVal = lv.get(0).evalValue(c); + TitleS2CPacket.Action action; + List targets; + Text title = null; + int in, out, stay; + MinecraftServer server = ((CarpetContext)c).s.getMinecraftServer(); + if (pVal instanceof ListValue) + { + targets = ((ListValue) pVal).getItems().stream() + .map(v -> EntityValue.getPlayerByValue(server, v)) + .filter(v -> v != null).collect(Collectors.toList()); + } + else + { + targets = Collections.singletonList(EntityValue.getPlayerByValue(server, pVal)); + if (targets.get(0) == null) + throw new InternalExpressionException("'display_title' requires an online player or a list of players as first argument"); + } + pVal = lv.get(1).evalValue(c); + switch (pVal.getString()) + { + case "title": + action = Action.TITLE; + break; + case "subtitle": + action = Action.SUBTITLE; + break; + case "actionbar": + action = Action.ACTIONBAR; + break; + case "clear": + action = Action.CLEAR; + break; + default: + throw new InternalExpressionException("'display_title' requires 'title', 'subtitle', 'actionbar' or 'clear' as second argument"); + } + if (action != Action.CLEAR && lv.size() < 3) + throw new InternalExpressionException("Third argument of 'display_title' must be present except for 'clear' type"); + if (lv.size() > 2) + { + pVal = lv.get(2).evalValue(c); + if (pVal instanceof FormattedTextValue) + title = ((FormattedTextValue) pVal).getText(); + else + title = Text.of(pVal.getString()); + } + TitleS2CPacket timesPacket = null; + if (lv.size() > 3) + { + try { + in = Integer.valueOf(lv.get(3).evalValue(c).getString()); + stay = Integer.valueOf(lv.get(4).evalValue(c).getString()); + out = Integer.valueOf(lv.get(5).evalValue(c).getString()); + timesPacket = new TitleS2CPacket(Action.TIMES, null, in, stay, out); + } catch (NumberFormatException e) { + throw new InternalExpressionException("If specified, 'display_title' requires integers as fourth, fifth and sixth arguments"); + } + } + TitleS2CPacket packet = new TitleS2CPacket(action, title); + for (ServerPlayerEntity player:targets) + { + if (timesPacket != null) player.networkHandler.sendPacket(timesPacket); + player.networkHandler.sendPacket(packet); + } + return (cc, tt) -> Value.NULL; + }); expression.addLazyFunction("format", -1, (c, t, lv) -> { if (lv.size() == 0 ) throw new InternalExpressionException("'format' requires at least one component");