feat: implemented dialog
This commit is contained in:
parent
cbbccbda99
commit
37a0ec22fe
@ -1,9 +1,14 @@
|
|||||||
package ru.dragonestia.msb3.api.boot;
|
package ru.dragonestia.msb3.api.boot;
|
||||||
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.entity.GameMode;
|
import net.minestom.server.entity.GameMode;
|
||||||
import net.minestom.server.event.player.PlayerChatEvent;
|
import net.minestom.server.event.player.PlayerChatEvent;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.Dialog;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogButton;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogCondition;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogRegistry;
|
||||||
import ru.dragonestia.msb3.api.module.FlatWorldModule;
|
import ru.dragonestia.msb3.api.module.FlatWorldModule;
|
||||||
import ru.dragonestia.msb3.api.module.MotdModule;
|
import ru.dragonestia.msb3.api.module.MotdModule;
|
||||||
import ru.dragonestia.msb3.api.module.PrometheusMetricsModule;
|
import ru.dragonestia.msb3.api.module.PrometheusMetricsModule;
|
||||||
@ -14,6 +19,8 @@ import ru.dragonestia.msb3.api.ui.dialogue.DialogueTheme;
|
|||||||
import team.unnamed.creative.ResourcePack;
|
import team.unnamed.creative.ResourcePack;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public class DefaultBootstrap extends ServerInitializer {
|
public class DefaultBootstrap extends ServerInitializer {
|
||||||
@ -30,9 +37,10 @@ public class DefaultBootstrap extends ServerInitializer {
|
|||||||
|
|
||||||
MinecraftServer.getGlobalEventHandler().addListener(PlayerChatEvent.class, event -> {
|
MinecraftServer.getGlobalEventHandler().addListener(PlayerChatEvent.class, event -> {
|
||||||
var player = event.getPlayer();
|
var player = event.getPlayer();
|
||||||
|
var dialog = new Dialog();
|
||||||
|
|
||||||
var render = new DialogueRenderer(player, DialogueTheme.builder().build());
|
dialog.setId(Key.key("msb3", "test_dialog"));
|
||||||
render.setText("""
|
dialog.setText("""
|
||||||
Абсолютно точно.
|
Абсолютно точно.
|
||||||
Я знаю точнo - невозможное возможно
|
Я знаю точнo - невозможное возможно
|
||||||
Сойти с ума, влюбиться так неосторoжно
|
Сойти с ума, влюбиться так неосторoжно
|
||||||
@ -64,13 +72,15 @@ public class DefaultBootstrap extends ServerInitializer {
|
|||||||
Всё невозможное - возможно, знаю точно!
|
Всё невозможное - возможно, знаю точно!
|
||||||
На-на-на-на (на-на-на-на), а-а, а-а
|
На-на-на-на (на-на-на-на), а-а, а-а
|
||||||
На-на-на-на (на-на-на-на), а-а, а-а""");
|
На-на-на-на (на-на-на-на), а-а, а-а""");
|
||||||
|
dialog.setRememberId(true);
|
||||||
|
|
||||||
render.setButton(ButtonNumber.BUTTON_1, "Всем привет!", ctx -> {});
|
var buttons = new ArrayList<DialogButton>();
|
||||||
render.setButton(ButtonNumber.BUTTON_2, "I am a teapot", ctx -> {});
|
buttons.add(new DialogButton(null, "Какая-то кнопочка", null, new HashMap<>(), new ArrayList<>()));
|
||||||
render.setButton(ButtonNumber.BUTTON_3, "I love pizza\nMamma mia\nPeperoni\nPapa carlo\nZaebumba\nZaebumba", ctx -> {});
|
buttons.add(new DialogButton(Key.key("msb3", "test_button"), "2 Какая-то кнопочка", "aboba", new HashMap<>(), new ArrayList<>()));
|
||||||
render.setButton(ButtonNumber.BUTTON_4, "msb3 is top!", ctx -> {});
|
buttons.add(new DialogButton(null, "Третья", "close", new HashMap<>(), new ArrayList<>()));
|
||||||
|
dialog.setButtons(buttons);
|
||||||
|
|
||||||
render.show();
|
dialog.open(player);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,10 @@ import net.minestom.server.MinecraftServer;
|
|||||||
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
|
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
|
||||||
import net.minestom.server.event.player.PlayerDisconnectEvent;
|
import net.minestom.server.event.player.PlayerDisconnectEvent;
|
||||||
import net.minestom.server.event.player.PlayerSpawnEvent;
|
import net.minestom.server.event.player.PlayerSpawnEvent;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogRegistry;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.action.CloseDialogActionHandler;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.condition.AlwaysDialogConditionHandler;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.condition.NeverDialogConditionHandler;
|
||||||
import ru.dragonestia.msb3.api.entity.PickableItem;
|
import ru.dragonestia.msb3.api.entity.PickableItem;
|
||||||
import ru.dragonestia.msb3.api.item.ItemUtil;
|
import ru.dragonestia.msb3.api.item.ItemUtil;
|
||||||
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
||||||
@ -89,6 +93,8 @@ public final class ServerBootstrap {
|
|||||||
initPlayerContextManager();
|
initPlayerContextManager();
|
||||||
|
|
||||||
initDefaultSkins();
|
initDefaultSkins();
|
||||||
|
|
||||||
|
initDefaultDialogActionsAndConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDefaultModules() {
|
private void initDefaultModules() {
|
||||||
@ -188,4 +194,11 @@ public final class ServerBootstrap {
|
|||||||
private void initDefaultSkins() {
|
private void initDefaultSkins() {
|
||||||
SkinStorage.loadSkin(SkinStorage.DEFAULT, ResourceFromJar.of("skins/default.msb3skin"));
|
SkinStorage.loadSkin(SkinStorage.DEFAULT, ResourceFromJar.of("skins/default.msb3skin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initDefaultDialogActionsAndConditions() {
|
||||||
|
DialogRegistry.registerActionHandler("close", new CloseDialogActionHandler());
|
||||||
|
|
||||||
|
DialogRegistry.registerConditionHandler("always", new AlwaysDialogConditionHandler());
|
||||||
|
DialogRegistry.registerConditionHandler("never", new NeverDialogConditionHandler());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
76
api/src/main/java/ru/dragonestia/msb3/api/dialog/Dialog.java
Normal file
76
api/src/main/java/ru/dragonestia/msb3/api/dialog/Dialog.java
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.player.PlayerContext;
|
||||||
|
import ru.dragonestia.msb3.api.player.defaults.TalksContext;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||||
|
import ru.dragonestia.msb3.api.ui.TalksThemes;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueTheme;
|
||||||
|
import ru.dragonestia.msb3.api.util.DebugMessage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Dialog {
|
||||||
|
|
||||||
|
private final static DialogueTheme DEFAULT_THEME = DialogueTheme.builder().build();
|
||||||
|
|
||||||
|
private Key id;
|
||||||
|
private String text;
|
||||||
|
private boolean rememberId;
|
||||||
|
private String themeId;
|
||||||
|
private List<DialogButton> buttons;
|
||||||
|
|
||||||
|
public void switchDialog(Player player, DialogueRenderer renderer) {
|
||||||
|
var ctx = PlayerContext.of(player, TalksContext.class);
|
||||||
|
if (rememberId) {
|
||||||
|
ctx.getOpenedDialogues().add(id.asString());
|
||||||
|
DebugMessage.send(player, "Идентификатор диалога %s был сохранен в список просмотренных диалогов".formatted(id.asString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogueTheme theme = DEFAULT_THEME;
|
||||||
|
if (themeId != null) {
|
||||||
|
var themeOpt = TalksThemes.getDialogueTheme(themeId);
|
||||||
|
if (themeOpt.isPresent()) {
|
||||||
|
theme = themeOpt.get();
|
||||||
|
} else {
|
||||||
|
DebugMessage.sendError(player, "Не найдена тема для диалога с идентификатором %s".formatted(themeId));
|
||||||
|
log.error("Unknown theme: {}", themeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderer.setTheme(theme);
|
||||||
|
|
||||||
|
renderer.setText(text);
|
||||||
|
|
||||||
|
for (var buttonNumber: ButtonNumber.values()) renderer.removeButton(buttonNumber);
|
||||||
|
var buttons = new ArrayList<>(getButtons());
|
||||||
|
buttons.removeIf(button -> !button.checkConditions(player, this, button, renderer));
|
||||||
|
for (var buttonNumber: ButtonNumber.values()) {
|
||||||
|
if (buttons.isEmpty()) break;
|
||||||
|
var button = buttons.removeFirst();
|
||||||
|
renderer.setButton(buttonNumber, button.getText(), buttonCtx -> {
|
||||||
|
var click = new DialogButtonClick(buttonCtx.player(), this, button, buttonCtx.buttonNumber(), buttonCtx.renderer());
|
||||||
|
button.onClick(click);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open(Player player) {
|
||||||
|
var renderer = new DialogueRenderer(player, DEFAULT_THEME);
|
||||||
|
switchDialog(player, renderer);
|
||||||
|
renderer.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.player.PlayerContext;
|
||||||
|
import ru.dragonestia.msb3.api.player.defaults.TalksContext;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
import ru.dragonestia.msb3.api.util.DebugMessage;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class DialogButton {
|
||||||
|
|
||||||
|
private final UUID uuid = UUID.randomUUID();
|
||||||
|
private Key id;
|
||||||
|
private String text;
|
||||||
|
private String actionId;
|
||||||
|
private Map<String, String> params;
|
||||||
|
private List<DialogCondition> conditions;
|
||||||
|
|
||||||
|
public boolean checkConditions(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer) {
|
||||||
|
for (var condition: conditions) {
|
||||||
|
if (!condition.check(player, dialog, button, renderer)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick(DialogButtonClick click) {
|
||||||
|
var player = click.player();
|
||||||
|
|
||||||
|
if (actionId == null) {
|
||||||
|
DebugMessage.sendError(player, "actionId для кнопки имеет значение null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var action = DialogRegistry.findActionHandler(actionId);
|
||||||
|
if (action.isEmpty()) {
|
||||||
|
DebugMessage.sendError(player, "Действие кнопки с идентификатором '%s' не зарегистрировано".formatted(actionId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success;
|
||||||
|
try {
|
||||||
|
action.get().handle(click, params);
|
||||||
|
DebugMessage.send(player, "Выполнение действия диалога actionId=%s params=%s".formatted(actionId, params));
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error(ex.getMessage(), ex);
|
||||||
|
DebugMessage.sendError(player, "Произошла ошибка при выполнении действия клика кнопки. actionId=%s params=%s\n%s: %s".formatted(action, params, ex.getClass().getSimpleName(), ex.getMessage()));
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) return;
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
var ctx = PlayerContext.of(player, TalksContext.class);
|
||||||
|
ctx.getClickedButtons().add(id.asString());
|
||||||
|
DebugMessage.send(player, "Идентификатор кнопки диалога %s был сохранен в список нажатых кнопок".formatted(id.asString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null) return false;
|
||||||
|
if (obj == this) return true;
|
||||||
|
if (obj instanceof DialogButton button) {
|
||||||
|
return uuid.equals(button.uuid);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return uuid.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
|
||||||
|
public record DialogButtonClick(
|
||||||
|
Player player,
|
||||||
|
Dialog dialog,
|
||||||
|
DialogButton dialogButton,
|
||||||
|
ButtonNumber buttonNumber,
|
||||||
|
DialogueRenderer renderer
|
||||||
|
) { }
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
import ru.dragonestia.msb3.api.util.DebugMessage;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class DialogCondition {
|
||||||
|
|
||||||
|
private String conditionId;
|
||||||
|
private Map<String, String> params;
|
||||||
|
|
||||||
|
public boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer) {
|
||||||
|
if (conditionId == null) {
|
||||||
|
DebugMessage.sendError(player, "Идентификатор условия для кнопки имеет значение null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var condition = DialogRegistry.findConditionHandler(conditionId);
|
||||||
|
if (condition.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return condition.get().check(player, dialog, renderer, params);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error(ex.getMessage(), ex);
|
||||||
|
DebugMessage.send(player, "Во время проверки условия для кнопки произошла ошибка conditionId=%s params=%s".formatted(conditionId, params));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.action.DialogActionHandler;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.condition.DialogConditionHandler;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.data.VoidPlayerDataProvider;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.data.PlayerDataProvider;
|
||||||
|
import ru.dragonestia.msb3.api.util.DebugMessage;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@UtilityClass
|
||||||
|
public class DialogRegistry {
|
||||||
|
|
||||||
|
private final Map<Key, Dialog> dialogs = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, DialogActionHandler> actionHandlers = new HashMap<>();
|
||||||
|
private final Map<String, DialogConditionHandler> conditionHandlers = new HashMap<>();
|
||||||
|
@Getter @Setter private PlayerDataProvider playerDataProvider = new VoidPlayerDataProvider();
|
||||||
|
|
||||||
|
public void storeDialog(Dialog dialog) {
|
||||||
|
if (dialog.getId() == null) {
|
||||||
|
throw new NullPointerException("Dialog id is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogs.put(dialog.getId(), dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Dialog> findDialog(Key key) {
|
||||||
|
return Optional.ofNullable(dialogs.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void findDialogAndSend(Player player, Key key) {
|
||||||
|
var opt = findDialog(key);
|
||||||
|
if (opt.isEmpty()) {
|
||||||
|
log.error("Dialog {} not found", key.asString());
|
||||||
|
DebugMessage.sendError(player, "Диалог с идентификатором %s не найден".formatted(key.asString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.get().open(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerActionHandler(String id, DialogActionHandler handler) {
|
||||||
|
var prev = actionHandlers.put(id, handler);
|
||||||
|
if (prev != null) {
|
||||||
|
log.warn("Duplicate action handler for id '{}'. Removing prev", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<DialogActionHandler> findActionHandler(String id) {
|
||||||
|
return Optional.ofNullable(actionHandlers.get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerConditionHandler(String id, DialogConditionHandler handler) {
|
||||||
|
var prev = conditionHandlers.put(id, handler);
|
||||||
|
if (prev != null) {
|
||||||
|
log.warn("Duplicate condition handler for id '{}'. Removing prev", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<DialogConditionHandler> findConditionHandler(String id) {
|
||||||
|
return Optional.ofNullable(conditionHandlers.get(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.action;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CloseDialogActionHandler implements DialogActionHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(DialogButtonClick click, Map<String, String> params) {
|
||||||
|
click.renderer().close(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.action;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface DialogActionHandler {
|
||||||
|
|
||||||
|
void handle(DialogButtonClick click, Map<String, String> params);
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.condition;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.Dialog;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class AlwaysDialogConditionHandler implements DialogConditionHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(Player player, Dialog dialog, DialogueRenderer renderer, Map<String, String> params) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.condition;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.Dialog;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface DialogConditionHandler {
|
||||||
|
|
||||||
|
boolean check(Player player, Dialog dialog, DialogueRenderer renderer, Map<String, String> params);
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.condition;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.Dialog;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class NeverDialogConditionHandler implements DialogConditionHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(Player player, Dialog dialog, DialogueRenderer renderer, Map<String, String> params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.data;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
||||||
|
|
||||||
|
public interface PlayerDataProvider {
|
||||||
|
|
||||||
|
TalksPlayerData load(MsbPlayer player);
|
||||||
|
|
||||||
|
void save(MsbPlayer player, TalksPlayerData data);
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public record TalksPlayerData(
|
||||||
|
Set<String> openedDialogs,
|
||||||
|
Set<String> clickedButtons
|
||||||
|
) {}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.data;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
public class VoidPlayerDataProvider implements PlayerDataProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TalksPlayerData load(MsbPlayer player) {
|
||||||
|
return new TalksPlayerData(
|
||||||
|
new HashSet<>(),
|
||||||
|
new HashSet<>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(MsbPlayer player, TalksPlayerData data) {}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package ru.dragonestia.msb3.api.player.defaults;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogRegistry;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.data.TalksPlayerData;
|
||||||
|
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
||||||
|
import ru.dragonestia.msb3.api.player.PlayerContext;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class TalksContext extends PlayerContext {
|
||||||
|
|
||||||
|
private final Set<String> openedDialogues = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
private final Set<String> clickedButtons = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
|
public TalksContext(MsbPlayer player) {
|
||||||
|
super(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
var data = DialogRegistry.getPlayerDataProvider().load(getPlayer());
|
||||||
|
|
||||||
|
openedDialogues.addAll(data.openedDialogs());
|
||||||
|
clickedButtons.addAll(data.clickedButtons());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
DialogRegistry.getPlayerDataProvider().save(getPlayer(), new TalksPlayerData(
|
||||||
|
openedDialogues,
|
||||||
|
clickedButtons
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,21 +1,32 @@
|
|||||||
package ru.dragonestia.msb3.api.ui;
|
package ru.dragonestia.msb3.api.ui;
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueTheme;
|
||||||
import ru.dragonestia.msb3.api.ui.monologue.MonologueTheme;
|
import ru.dragonestia.msb3.api.ui.monologue.MonologueTheme;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class TalksThemes {
|
public class TalksThemes {
|
||||||
|
|
||||||
private final Map<String, MonologueTheme> monologueThemes = new HashMap<>();
|
private final Map<String, MonologueTheme> monologueThemes = new HashMap<>();
|
||||||
|
private final Map<String, DialogueTheme> dialogueThemes = new HashMap<>();
|
||||||
|
|
||||||
public void registerMonologueTheme(String identifier, MonologueTheme theme) {
|
public void registerMonologueTheme(String identifier, MonologueTheme theme) {
|
||||||
monologueThemes.put(identifier, theme);
|
monologueThemes.put(identifier, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonologueTheme getMonologueTheme(String identifier) {
|
public Optional<MonologueTheme> getMonologueTheme(String identifier) {
|
||||||
return monologueThemes.get(identifier);
|
return Optional.ofNullable(monologueThemes.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerDialogueTheme(String identifier, DialogueTheme theme) {
|
||||||
|
dialogueThemes.put(identifier, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<DialogueTheme> getDialogueTheme(String identifier) {
|
||||||
|
return Optional.ofNullable(dialogueThemes.get(identifier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import ru.dragonestia.msb3.api.item.ItemUtil;
|
|||||||
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||||
import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions;
|
import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions;
|
||||||
import ru.dragonestia.msb3.api.resource.dialog.TextureProperties;
|
import ru.dragonestia.msb3.api.resource.dialog.TextureProperties;
|
||||||
|
import ru.dragonestia.msb3.api.util.DebugMessage;
|
||||||
import ru.dragonestia.msb3.api.util.StringUtil;
|
import ru.dragonestia.msb3.api.util.StringUtil;
|
||||||
import ru.dragonestia.msb3.resource.glyph.GlyphComponent;
|
import ru.dragonestia.msb3.resource.glyph.GlyphComponent;
|
||||||
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
|
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
|
||||||
@ -32,6 +33,7 @@ import ru.dragonestia.msb3.resource.glyph.MinecraftFont;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
@ -52,6 +54,8 @@ public class DialogueRenderer extends Inventory {
|
|||||||
private EventListener<InventoryCloseEvent> onCloseListener;
|
private EventListener<InventoryCloseEvent> onCloseListener;
|
||||||
private EventListener<InventoryPreClickEvent> onClickListener;
|
private EventListener<InventoryPreClickEvent> onClickListener;
|
||||||
@Getter @Setter private OnCloseDialog onClose;
|
@Getter @Setter private OnCloseDialog onClose;
|
||||||
|
private final Object lockButtonExecution = new Object();
|
||||||
|
private volatile boolean lockedExecution = false;
|
||||||
|
|
||||||
public DialogueRenderer(Player player, DialogueTheme theme) {
|
public DialogueRenderer(Player player, DialogueTheme theme) {
|
||||||
super(InventoryType.CHEST_6_ROW, Component.empty());
|
super(InventoryType.CHEST_6_ROW, Component.empty());
|
||||||
@ -221,16 +225,25 @@ public class DialogueRenderer extends Inventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ANSWER_1, ANSWER_2, ANSWER_3, ANSWER_4 -> {
|
case ANSWER_1, ANSWER_2, ANSWER_3, ANSWER_4 -> {
|
||||||
var buttonNumber = type.buttonNumber;
|
if (tryLockButtonActionExecution()) {
|
||||||
if (onAnswerClick.containsKey(buttonNumber)) {
|
try {
|
||||||
var ctx = new AnswerClickContext(
|
var buttonNumber = type.buttonNumber;
|
||||||
player,
|
if (onAnswerClick.containsKey(buttonNumber)) {
|
||||||
this,
|
var ctx = new AnswerClickContext(
|
||||||
buttonNumber,
|
player,
|
||||||
buttonsText.get(buttonNumber)
|
this,
|
||||||
);
|
buttonNumber,
|
||||||
|
buttonsText.get(buttonNumber)
|
||||||
|
);
|
||||||
|
|
||||||
onAnswerClick.get(buttonNumber).accept(ctx);
|
onAnswerClick.get(buttonNumber).accept(ctx);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
DebugMessage.sendError(player, "Во время обработки нажатия кнопки произошла ошибка: " + ex.getMessage());
|
||||||
|
log.error(ex.getMessage(), ex);
|
||||||
|
} finally {
|
||||||
|
unlockButtonActionExecution();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,13 +265,17 @@ public class DialogueRenderer extends Inventory {
|
|||||||
itemBackup = null;
|
itemBackup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (onCloseListener != null) {
|
||||||
|
player.eventNode().removeListener(onCloseListener);
|
||||||
|
onCloseListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onClickListener != null) {
|
||||||
|
player.eventNode().removeListener(onClickListener);
|
||||||
|
onClickListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!closedByPlayer) player.closeInventory();
|
if (!closedByPlayer) player.closeInventory();
|
||||||
|
|
||||||
player.eventNode().removeListener(onCloseListener);
|
|
||||||
onCloseListener = null;
|
|
||||||
|
|
||||||
player.eventNode().removeListener(onClickListener);
|
|
||||||
onClickListener = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onClose != null) onClose.onClose(closedByPlayer);
|
if (onClose != null) onClose.onClose(closedByPlayer);
|
||||||
@ -365,7 +382,6 @@ public class DialogueRenderer extends Inventory {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<TextLine> breakIntoLines(String input, TextureProperties firstLineProperties, int fontHeight, int maxWidth) throws IllegalStateException {
|
private List<TextLine> breakIntoLines(String input, TextureProperties firstLineProperties, int fontHeight, int maxWidth) throws IllegalStateException {
|
||||||
var resolvedText = MiniMessage.miniMessage().deserialize(input);
|
var resolvedText = MiniMessage.miniMessage().deserialize(input);
|
||||||
var lines = StringUtil.splitIntoParts(resolvedText, maxWidth, line -> {
|
var lines = StringUtil.splitIntoParts(resolvedText, maxWidth, line -> {
|
||||||
@ -425,6 +441,25 @@ public class DialogueRenderer extends Inventory {
|
|||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean tryLockButtonActionExecution() {
|
||||||
|
synchronized (lockButtonExecution) {
|
||||||
|
if (lockedExecution) return false; // fail
|
||||||
|
|
||||||
|
lockedExecution = true;
|
||||||
|
return true; // success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unlockButtonActionExecution() {
|
||||||
|
synchronized (lockButtonExecution) {
|
||||||
|
lockedExecution = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeButton(ButtonNumber buttonNumber) {
|
||||||
|
setButton(buttonNumber, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
public void setButton(ButtonNumber buttonNumber, String text, Consumer<AnswerClickContext> onClick) {
|
public void setButton(ButtonNumber buttonNumber, String text, Consumer<AnswerClickContext> onClick) {
|
||||||
if (text == null) {
|
if (text == null) {
|
||||||
buttonsText.remove(buttonNumber);
|
buttonsText.remove(buttonNumber);
|
||||||
@ -436,6 +471,15 @@ public class DialogueRenderer extends Inventory {
|
|||||||
onAnswerClick.put(buttonNumber, onClick);
|
onAnswerClick.put(buttonNumber, onClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Optional<DialogueRenderer> getRenderer(Player player) {
|
||||||
|
var inv = player.getOpenInventory();
|
||||||
|
if (inv == null) return Optional.empty();
|
||||||
|
if (inv instanceof DialogueRenderer renderer) {
|
||||||
|
return Optional.of(renderer);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
private record TextLine(DialogueTheme theme, String text, TextureProperties textureProperties) {
|
private record TextLine(DialogueTheme theme, String text, TextureProperties textureProperties) {
|
||||||
|
|
||||||
public GlyphComponent toGlyphList(TextureProperties parentProperties, int lineShift, boolean cutEnding) {
|
public GlyphComponent toGlyphList(TextureProperties parentProperties, int lineShift, boolean cutEnding) {
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
package ru.dragonestia.msb3.api.util;
|
package ru.dragonestia.msb3.api.util;
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.kyori.adventure.sound.Sound;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
import net.kyori.adventure.text.format.TextDecoration;
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.sound.SoundEvent;
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class DebugMessage {
|
public class DebugMessage {
|
||||||
@ -23,6 +25,9 @@ public class DebugMessage {
|
|||||||
public void sendError(Player player, String message) {
|
public void sendError(Player player, String message) {
|
||||||
if (disabled) return;
|
if (disabled) return;
|
||||||
|
|
||||||
|
var sound = Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_BIT, Sound.Source.NEUTRAL, 1f, 0f);
|
||||||
|
player.playSound(sound);
|
||||||
|
|
||||||
player.sendMessage(Component.text()
|
player.sendMessage(Component.text()
|
||||||
.append(Component.text("[DEBUG] ", TextColor.color(0xFFC909), TextDecoration.BOLD))
|
.append(Component.text("[DEBUG] ", TextColor.color(0xFFC909), TextDecoration.BOLD))
|
||||||
.append(Component.text("Error: ", TextColor.color(0xFF3F3F)))
|
.append(Component.text("Error: ", TextColor.color(0xFF3F3F)))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user