Skip to content

Commit 467e8e6

Browse files
committed
Sound element
1 parent e3d3183 commit 467e8e6

File tree

4 files changed

+160
-8
lines changed

4 files changed

+160
-8
lines changed

src/main/java/cloud/grabsky/dialogs/Dialog.java

+23-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import cloud.grabsky.dialogs.elements.CommandElement;
2020
import cloud.grabsky.dialogs.elements.MessageElement;
2121
import cloud.grabsky.dialogs.elements.PauseElement;
22+
import cloud.grabsky.dialogs.elements.SoundElement;
2223
import me.clip.placeholderapi.PlaceholderAPI;
2324
import net.kyori.adventure.audience.Audience;
2425
import net.kyori.adventure.text.Component;
@@ -29,6 +30,7 @@
2930

3031
import java.util.Collection;
3132
import java.util.Iterator;
33+
import java.util.List;
3234
import java.util.UUID;
3335

3436
import org.jetbrains.annotations.NotNull;
@@ -122,12 +124,29 @@ else if (element instanceof CommandElement commandElement) {
122124
// Cancelling task in case Player connection has been reset OR other dialog has been started in the meanwhile.
123125
if (isDialogStillValid(target, dialogIdentifier) == false)
124126
return;
125-
// Preparing command string.
126-
final String command = (Dialogs.isPlaceholderAPI() == true) ? PlaceholderAPI.setPlaceholders(target, commandElement.value()) : commandElement.value();
127+
// Setting placeholders in commands.
128+
final List<String> commands = (Dialogs.isPlaceholderAPI() == true)
129+
? commandElement.value().stream().map(command -> PlaceholderAPI.setPlaceholders(target, command)).toList()
130+
: commandElement.value();
127131
// Getting the command sender.
128132
final CommandSender sender = (commandElement.type() == CommandElement.Type.PLAYER_COMMAND) ? target : plugin.getServer().getConsoleSender();
129-
// Dispatching the command.
130-
plugin.getServer().dispatchCommand(sender, command);
133+
// Dispatching commands.
134+
commands.forEach(command -> plugin.getServer().dispatchCommand(sender, command));
135+
});
136+
// Calculating "start" time of the next element.
137+
nextTaskStartsIn += element.ticksToWait();
138+
}
139+
140+
else if (element instanceof SoundElement soundElement) {
141+
// Scheduling a new run task. Command dispatch have to be called on the main thread.
142+
plugin.getBedrockScheduler().run(nextTaskStartsIn, (task) -> {
143+
// Cancelling task in case Player connection has been reset OR other dialog has been started in the meanwhile.
144+
if (isDialogStillValid(target, dialogIdentifier) == false)
145+
return;
146+
// Getting the audience.
147+
final Audience audience = (soundElement.audience() == SoundElement.AudienceType.PLAYER) ? target : plugin.getServer();
148+
// Playing sounds.
149+
soundElement.value().forEach(audience::playSound);
131150
});
132151
// Calculating "start" time of the next element.
133152
nextTaskStartsIn += element.ticksToWait();

src/main/java/cloud/grabsky/dialogs/configuration/adapter/DialogElementAdapterFactory.java

+42-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import cloud.grabsky.dialogs.elements.CommandElement;
2222
import cloud.grabsky.dialogs.elements.MessageElement;
2323
import cloud.grabsky.dialogs.elements.PauseElement;
24+
import cloud.grabsky.dialogs.elements.SoundElement;
2425
import cloud.grabsky.dialogs.util.Enums;
2526
import com.squareup.moshi.JsonAdapter;
2627
import com.squareup.moshi.JsonReader;
@@ -33,6 +34,7 @@
3334
import java.io.IOException;
3435
import java.lang.annotation.Annotation;
3536
import java.lang.reflect.Type;
37+
import java.util.Collections;
3638
import java.util.List;
3739
import java.util.Set;
3840

@@ -49,6 +51,8 @@ public final class DialogElementAdapterFactory implements JsonAdapter.Factory {
4951
/* SINGLETON */ public static DialogElementAdapterFactory INSTANCE = new DialogElementAdapterFactory();
5052

5153
private static final Type LIST_OF_CONDITIONS = Types.newParameterizedType(List.class, Condition.class);
54+
private static final Type LIST_OF_STRINGS = Types.newParameterizedType(List.class, String.class);
55+
private static final Type LIST_OF_SOUNDS = Types.newParameterizedType(List.class, Sound.class);
5256

5357
@Override
5458
public @Nullable JsonAdapter<DialogElement> create(final @NotNull Type type, final @NotNull Set<? extends Annotation> annotations, final @NotNull Moshi moshi) {
@@ -57,6 +61,8 @@ public final class DialogElementAdapterFactory implements JsonAdapter.Factory {
5761
// ...
5862
final var adapter0 = moshi.adapter(Sound.class);
5963
final var adapter1 = moshi.adapter(LIST_OF_CONDITIONS);
64+
final var adapter2 = moshi.adapter(LIST_OF_STRINGS);
65+
final var adapter3 = moshi.adapter(LIST_OF_SOUNDS);
6066
// ...
6167
return new JsonAdapter<>() {
6268

@@ -131,7 +137,42 @@ public final class DialogElementAdapterFactory implements JsonAdapter.Factory {
131137
final String name = in.nextName().toLowerCase();
132138
// ...
133139
switch (name) {
134-
case "value" -> init.value = in.nextString();
140+
case "value" -> {
141+
if (in.peek() == Token.BEGIN_ARRAY) {
142+
init.value = (List<String>) adapter2.nullSafe().fromJson(in);
143+
continue;
144+
}
145+
init.value = List.of(in.nextString());
146+
}
147+
case "ticks_to_wait_before_continuing" -> init.ticks_to_wait_before_continuing = in.nextInt();
148+
case "conditions" -> {
149+
// Parsing the Sound object.
150+
final @Nullable List<Condition> conditions = (List<Condition>) adapter1.nullSafe().fromJson(in);
151+
// Skipping when specified as null.
152+
if (conditions != null) init.conditions = conditions;
153+
}
154+
}
155+
}
156+
// Ending the JSON object.
157+
in.endObject();
158+
// Initializing and returning the value.
159+
yield init.init();
160+
}
161+
case "sound" -> {
162+
final SoundElement.Init init = new SoundElement.Init();
163+
// ...
164+
while (in.hasNext() == true) {
165+
final String name = in.nextName().toLowerCase();
166+
// ...
167+
switch (name) {
168+
case "audience" -> init.audience = Enums.fromName(SoundElement.AudienceType.class, in.nextString());
169+
case "value" -> {
170+
if (in.peek() == Token.BEGIN_ARRAY) {
171+
init.value = (List<Sound>) adapter3.nullSafe().fromJson(in);
172+
continue;
173+
}
174+
init.value = Collections.singletonList(adapter0.nullSafe().fromJson(in));
175+
}
135176
case "ticks_to_wait_before_continuing" -> init.ticks_to_wait_before_continuing = in.nextInt();
136177
case "conditions" -> {
137178
// Parsing the Sound object.

src/main/java/cloud/grabsky/dialogs/elements/CommandElement.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ public class CommandElement implements DialogElement {
4141
private final CommandElement.Type type;
4242

4343
/**
44-
* Command {@link String} to be executed.
44+
* List of commands to be executed.
4545
*/
4646
@Getter(AccessLevel.PUBLIC)
47-
private final String value;
47+
private final List<String> value;
4848

4949
@Getter(AccessLevel.PUBLIC)
5050
private final int ticksToWait;
@@ -70,7 +70,7 @@ public static final class Init implements LazyInit<CommandElement> {
7070
private final @NotNull CommandElement.Type type;
7171

7272
// Nullability cannot be determined because it depends entirely on the end-user.
73-
public @UnknownNullability String value;
73+
public @UnknownNullability List<String> value;
7474

7575
// Following field(s) have defaults and can be omitted or defined as null by the end-user.
7676
public @NotNull Integer ticks_to_wait_before_continuing = 1;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Dialogs (https://github.com/Grabsky/Dialogs)
3+
*
4+
* Copyright (C) 2024 Grabsky <[email protected]>
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License v3 as published by
8+
* the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License v3 for more details.
14+
*/
15+
package cloud.grabsky.dialogs.elements;
16+
17+
import cloud.grabsky.configuration.util.LazyInit;
18+
import cloud.grabsky.dialogs.Condition;
19+
import cloud.grabsky.dialogs.DialogElement;
20+
import net.kyori.adventure.sound.Sound;
21+
22+
import java.util.Collections;
23+
import java.util.List;
24+
25+
import org.jetbrains.annotations.ApiStatus.Internal;
26+
import org.jetbrains.annotations.NotNull;
27+
import org.jetbrains.annotations.UnknownNullability;
28+
29+
import lombok.AccessLevel;
30+
import lombok.Getter;
31+
import lombok.RequiredArgsConstructor;
32+
import lombok.experimental.Accessors;
33+
34+
@Accessors(fluent = true)
35+
@RequiredArgsConstructor(access = AccessLevel.PUBLIC)
36+
public final class SoundElement implements DialogElement {
37+
38+
/**
39+
* Audience to forward this message to.
40+
*/
41+
@Getter(AccessLevel.PUBLIC)
42+
private final transient AudienceType audience;
43+
44+
/**
45+
* List of sounds to be played.
46+
*/
47+
@Getter(AccessLevel.PUBLIC)
48+
private final List<Sound> value;
49+
50+
@Getter(AccessLevel.PUBLIC)
51+
private final int ticksToWait;
52+
53+
@Getter(AccessLevel.PUBLIC)
54+
private final List<Condition> conditions;
55+
56+
57+
/**
58+
* Defines support audience types to forward the {@link SoundElement} to.
59+
*/
60+
public enum AudienceType {
61+
PLAYER, SERVER;
62+
}
63+
64+
65+
@Internal
66+
@RequiredArgsConstructor(access = AccessLevel.PUBLIC)
67+
// NOTE: Field names does not follow Java Naming Convention to provide 1:1 mapping with JSON keys.
68+
public static final class Init implements LazyInit<SoundElement> {
69+
70+
// Following field(s) have defaults and can be omitted or defined as null by the end-user.
71+
public @NotNull AudienceType audience = AudienceType.PLAYER;
72+
73+
// Nullability cannot be determined because it depends entirely on the end-user.
74+
public @UnknownNullability List<Sound> value;
75+
76+
// Following field(s) have defaults and can be omitted or defined as null by the end-user.
77+
public @NotNull Integer ticks_to_wait_before_continuing = 1;
78+
public @NotNull List<Condition> conditions = Collections.emptyList();
79+
80+
81+
@Override
82+
public @NotNull SoundElement init() throws IllegalStateException {
83+
// Throwing an error in case "value" field is invalid.
84+
if (value == null)
85+
throw new IllegalStateException("Field \"value\" is required but is either null or has not been found.");
86+
// Creating and returning element.
87+
return new SoundElement(audience, value, ticks_to_wait_before_continuing, conditions);
88+
}
89+
90+
}
91+
92+
}

0 commit comments

Comments
 (0)