Merge branch 'feat/dialogues' into 'master'
Implemented dialogues See merge request Dragonestia/Minestom/msb!1
This commit is contained in:
commit
bdc0aeb31e
2
.gitignore
vendored
2
.gitignore
vendored
@ -25,3 +25,5 @@ bin/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
run/
|
run/
|
||||||
|
|
||||||
|
tarkov/
|
||||||
@ -1,16 +1,42 @@
|
|||||||
|
plugins {
|
||||||
|
id 'com.github.johnrengelman.shadow' version '7.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
shadowJar {
|
||||||
|
manifest {
|
||||||
|
attributes (
|
||||||
|
"Multi-Release": true,
|
||||||
|
"Add-Opens": "java.base/java.lang " +
|
||||||
|
"java.base/java.lang.reflect"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeServiceFiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
targetCompatibility = JavaVersion.VERSION_21
|
||||||
|
}
|
||||||
|
|
||||||
|
assemble {
|
||||||
|
dependsOn shadowJar, processResources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'net.minestom:minestom-snapshots:bb7acc2e77'
|
api project(":resource-compiler")
|
||||||
|
|
||||||
|
api 'net.minestom:minestom-snapshots:1_21_4-6490538291'
|
||||||
|
|
||||||
api 'org.slf4j:slf4j-api:2.0.16'
|
api 'org.slf4j:slf4j-api:2.0.16'
|
||||||
api 'org.apache.logging.log4j:log4j-slf4j2-impl:2.24.0'
|
api 'org.apache.logging.log4j:log4j-slf4j2-impl:2.24.0'
|
||||||
api 'org.apache.logging.log4j:log4j-api:2.24.0'
|
api 'org.apache.logging.log4j:log4j-api:2.24.0'
|
||||||
api 'org.apache.logging.log4j:log4j-core:2.19.0'
|
api 'org.apache.logging.log4j:log4j-core:2.19.0'
|
||||||
|
|
||||||
api 'net.kyori:adventure-api:4.17.0'
|
api 'org.spongepowered:configurate-hocon:4.2.0'
|
||||||
api 'net.kyori:adventure-text-minimessage:4.17.0'
|
api 'org.spongepowered:configurate-gson:4.2.0'
|
||||||
api 'team.unnamed:creative-api:1.7.3'
|
api 'org.spongepowered:configurate-yaml:4.2.0'
|
||||||
api 'team.unnamed:creative-serializer-minecraft:1.7.3'
|
|
||||||
api 'team.unnamed:creative-server:1.7.3'
|
|
||||||
|
|
||||||
api 'org.sql2o:sql2o:1.8.0'
|
api 'org.sql2o:sql2o:1.8.0'
|
||||||
api 'com.clickhouse:clickhouse-jdbc:0.7.1'
|
api 'com.clickhouse:clickhouse-jdbc:0.7.1'
|
||||||
@ -19,4 +45,6 @@ dependencies {
|
|||||||
api 'io.prometheus:prometheus-metrics-core:1.3.1'
|
api 'io.prometheus:prometheus-metrics-core:1.3.1'
|
||||||
api 'io.prometheus:prometheus-metrics-instrumentation-jvm:1.3.1'
|
api 'io.prometheus:prometheus-metrics-instrumentation-jvm:1.3.1'
|
||||||
api 'io.prometheus:prometheus-metrics-exporter-httpserver:1.3.1'
|
api 'io.prometheus:prometheus-metrics-exporter-httpserver:1.3.1'
|
||||||
|
|
||||||
|
implementation 'org.apache.commons:commons-text:1.10.0'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api;
|
|
||||||
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
|
||||||
import net.minestom.server.entity.GameMode;
|
|
||||||
import ru.dragonestia.msb3.api.module.FlatWorldModule;
|
|
||||||
import ru.dragonestia.msb3.api.module.MotdModule;
|
|
||||||
import ru.dragonestia.msb3.api.module.ResourcePackRepositoryModule;
|
|
||||||
|
|
||||||
@Log4j2
|
|
||||||
public class Bootstrap {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
var boot = new ServerBootstrap();
|
|
||||||
|
|
||||||
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
|
|
||||||
FlatWorldModule.init(GameMode.ADVENTURE);
|
|
||||||
|
|
||||||
ResourcePackRepositoryModule.init(boot, "0.0.0.0", 7270);
|
|
||||||
|
|
||||||
boot.start("0.0.0.0", 25565);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
|
||||||
import net.minestom.server.MinecraftServer;
|
|
||||||
import ru.dragonestia.msb3.api.item.ItemPrefabManager;
|
|
||||||
import ru.dragonestia.msb3.api.item.ItemUtil;
|
|
||||||
import ru.dragonestia.msb3.api.resource.ResourcePackManager;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
@Log4j2
|
|
||||||
public class ServerBootstrap {
|
|
||||||
|
|
||||||
public static final ClassLoader CLASS_LOADER = ServerBootstrap.class.getClassLoader();
|
|
||||||
|
|
||||||
private static final String[] LOGO = {
|
|
||||||
"==============================================",
|
|
||||||
"Minestom Server Base v3 - by ScarletRedMan",
|
|
||||||
"",
|
|
||||||
" 8b d8 .dP\"Y8 88\"\"Yb 88888 ",
|
|
||||||
" 88b d88 `Ybo.\" 88__dP .dP ",
|
|
||||||
" 88YbdP88 o.`Y8b 88\"\"Yb o `Yb ",
|
|
||||||
" 88 YY 88 8bodP' 88oodP YbodP ",
|
|
||||||
"",
|
|
||||||
"Minecraft version: " + MinecraftServer.VERSION_NAME + " (protocol: " + MinecraftServer.PROTOCOL_VERSION + ")",
|
|
||||||
"==============================================",
|
|
||||||
};
|
|
||||||
|
|
||||||
@Getter private static ServerBootstrap instance;
|
|
||||||
@Getter private static boolean started = false;
|
|
||||||
|
|
||||||
private final MinecraftServer server;
|
|
||||||
@Getter private final ResourcePackManager resourcePackManager;
|
|
||||||
@Getter private final ItemPrefabManager itemPrefabManager;
|
|
||||||
|
|
||||||
public ServerBootstrap() {
|
|
||||||
instance = this;
|
|
||||||
|
|
||||||
for (var line: LOGO) log.info(line);
|
|
||||||
|
|
||||||
server = MinecraftServer.init();
|
|
||||||
|
|
||||||
MinecraftServer.setBrandName("Dragonestia");
|
|
||||||
|
|
||||||
resourcePackManager = new ResourcePackManager();
|
|
||||||
itemPrefabManager = new ItemPrefabManager();
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start(String address, int port) {
|
|
||||||
started = true;
|
|
||||||
server.start(new InetSocketAddress(address, port));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
ItemUtil.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package ru.dragonestia.msb3.api.boot;
|
||||||
|
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.minestom.server.entity.GameMode;
|
||||||
|
import ru.dragonestia.msb3.api.module.FlatWorldModule;
|
||||||
|
import ru.dragonestia.msb3.api.module.MotdModule;
|
||||||
|
import ru.dragonestia.msb3.api.module.PrometheusMetricsModule;
|
||||||
|
import ru.dragonestia.msb3.api.module.ResourcePackRepositoryModule;
|
||||||
|
import team.unnamed.creative.ResourcePack;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
public class DefaultBootstrap extends ServerInitializer {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ServerBootstrap.start("0.0.0.0", 25565, new DefaultBootstrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDefaultModulesLoaded() {
|
||||||
|
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
|
||||||
|
FlatWorldModule.init(GameMode.ADVENTURE);
|
||||||
|
PrometheusMetricsModule.init(new InetSocketAddress("0.0.0.0", 7500));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitializeResources(ResourcePack resourcePack) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResourcePackCompiled(ResourcePack resourcePack) {
|
||||||
|
ResourcePackRepositoryModule.init("0.0.0.0", 7270);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,207 @@
|
|||||||
|
package ru.dragonestia.msb3.api.boot;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerDisconnectEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerSpawnEvent;
|
||||||
|
import ru.dragonestia.msb3.api.command.DebugCommands;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogRegistry;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.action.CloseDialogActionHandler;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.action.DialogDialogActionHandler;
|
||||||
|
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.event.NPCClickEvent;
|
||||||
|
import ru.dragonestia.msb3.api.item.ItemUtil;
|
||||||
|
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
||||||
|
import ru.dragonestia.msb3.api.player.PlayerContextManager;
|
||||||
|
import ru.dragonestia.msb3.api.player.defaults.KeyedBossBarContext;
|
||||||
|
import ru.dragonestia.msb3.api.player.defaults.NavigatorContext;
|
||||||
|
import ru.dragonestia.msb3.api.player.defaults.TalksContext;
|
||||||
|
import ru.dragonestia.msb3.api.resource.DialogueResources;
|
||||||
|
import ru.dragonestia.msb3.api.resource.MonologueResources;
|
||||||
|
import ru.dragonestia.msb3.api.skin.SkinStorage;
|
||||||
|
import ru.dragonestia.msb3.api.ui.BlackScreen;
|
||||||
|
import ru.dragonestia.msb3.api.ui.bossbar.KeyedBossBars;
|
||||||
|
import ru.dragonestia.msb3.api.ui.navigator.Navigators;
|
||||||
|
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
||||||
|
import ru.dragonestia.msb3.resource.Resources;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphCharacterFactory;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.MinecraftFont;
|
||||||
|
import ru.dragonestia.msb3.resource.utils.ClassPreLoader;
|
||||||
|
import team.unnamed.creative.lang.Language;
|
||||||
|
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@UtilityClass
|
||||||
|
public final class ServerBootstrap {
|
||||||
|
|
||||||
|
public static final ClassLoader CLASS_LOADER = ServerBootstrap.class.getClassLoader();
|
||||||
|
|
||||||
|
private static final String[] LOGO = {
|
||||||
|
"==============================================",
|
||||||
|
"Minestom Server Base v3 - by ScarletRedMan",
|
||||||
|
"",
|
||||||
|
" 8b d8 .dP\"Y8 88\"\"Yb 88888 ",
|
||||||
|
" 88b d88 `Ybo.\" 88__dP .dP ",
|
||||||
|
" 88YbdP88 o.`Y8b 88\"\"Yb o `Yb ",
|
||||||
|
" 88 YY 88 8bodP' 88oodP YbodP ",
|
||||||
|
"",
|
||||||
|
"Minecraft version: " + MinecraftServer.VERSION_NAME + " (protocol: " + MinecraftServer.PROTOCOL_VERSION + ")",
|
||||||
|
"==============================================",
|
||||||
|
};
|
||||||
|
|
||||||
|
@Getter private boolean started = false;
|
||||||
|
|
||||||
|
private final MinecraftServer server = preInitialize();
|
||||||
|
|
||||||
|
private MinecraftServer preInitialize() {
|
||||||
|
for (var line: LOGO) log.info(line);
|
||||||
|
|
||||||
|
var server = MinecraftServer.init();
|
||||||
|
MinecraftServer.setBrandName("Dragonestia");
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void start(String address, int port, ServerInitializer initializer) {
|
||||||
|
if (started) return;
|
||||||
|
|
||||||
|
started = true;
|
||||||
|
init(initializer);
|
||||||
|
server.start(new InetSocketAddress(address, port));
|
||||||
|
initializer.onServerStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(ServerInitializer initializer) {
|
||||||
|
initializer.onLoad();
|
||||||
|
|
||||||
|
initDefaultModules();
|
||||||
|
initializer.onDefaultModulesLoaded();
|
||||||
|
|
||||||
|
initDefaultGlyphs();
|
||||||
|
initializer.onInitializeResources(Resources.getResourcePack());
|
||||||
|
|
||||||
|
compileResourcePack();
|
||||||
|
initializer.onResourcePackCompiled(Resources.getResourcePack());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDefaultModules() {
|
||||||
|
ItemUtil.init();
|
||||||
|
PickableItem.registerEvent();
|
||||||
|
initPlayerContextManager();
|
||||||
|
initDefaultSkins();
|
||||||
|
initDefaultDialogActionsAndConditions();
|
||||||
|
NPCClickEvent.init();
|
||||||
|
DebugCommands.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compileResourcePack() {
|
||||||
|
var resourcePack = Resources.compile();
|
||||||
|
|
||||||
|
log.info("Used {}/{} glyphs", GlyphCharacterFactory.countUsedChars(), GlyphCharacterFactory.countCharRange());
|
||||||
|
|
||||||
|
var file = new File("./resource-pack.zip");
|
||||||
|
MinecraftResourcePackWriter.minecraft().writeToZipFile(file, resourcePack);
|
||||||
|
|
||||||
|
log.info("Compiled resource pack. File: ./resource-pack.zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDefaultGlyphs() {
|
||||||
|
ClassPreLoader.preload(BlackScreen.class);
|
||||||
|
ClassPreLoader.preload(Navigators.class);
|
||||||
|
ClassPreLoader.preload(KeyedBossBars.class);
|
||||||
|
|
||||||
|
MonologueResources.registerAvatar(MonologueResources.DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/default_avatar.png"));
|
||||||
|
MonologueResources.registerFrame(MonologueResources.DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/avatar_frame.png"));
|
||||||
|
|
||||||
|
DialogueResources.registerAvatar(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/default_avatar.png"));
|
||||||
|
DialogueResources.registerAvatarFrame(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/avatar_frame.png"));
|
||||||
|
DialogueResources.registerScrollUp(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_up_button.png"));
|
||||||
|
DialogueResources.registerScrollDown(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/scroll_phrase_down_button.png"));
|
||||||
|
DialogueResources.registerBackground(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/background.png"));
|
||||||
|
DialogueResources.registerSubstrate(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/phrase_substrate.png"));
|
||||||
|
DialogueResources.registerActiveTextField(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_active_text_field.png"));
|
||||||
|
DialogueResources.registerNotActiveTextField(DialogueResources.DEFAULT, ResourceFromJar.of("glyphs/dialogue/answer_not_active_text_field.png"));
|
||||||
|
DialogueResources.registerButton(DialogueResources.DEFAULT + 1, ResourceFromJar.of("glyphs/dialogue/answer_button_active_1.png"));
|
||||||
|
DialogueResources.registerButton(DialogueResources.DEFAULT + 2, ResourceFromJar.of("glyphs/dialogue/answer_button_active_2.png"));
|
||||||
|
DialogueResources.registerButton(DialogueResources.DEFAULT + 3, ResourceFromJar.of("glyphs/dialogue/answer_button_active_3.png"));
|
||||||
|
DialogueResources.registerButton(DialogueResources.DEFAULT + 4, ResourceFromJar.of("glyphs/dialogue/answer_button_active_4.png"));
|
||||||
|
initDialogueFont();
|
||||||
|
initLocales();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDialogueFont() {
|
||||||
|
MinecraftFont.compileFontProvider(8, -28);
|
||||||
|
MinecraftFont.compileFontProvider(8, -37);
|
||||||
|
MinecraftFont.compileFontProvider(8, -46);
|
||||||
|
MinecraftFont.compileFontProvider(8, -55);
|
||||||
|
MinecraftFont.compileFontProvider(8, -64);
|
||||||
|
MinecraftFont.compileFontProvider(8, -73);
|
||||||
|
MinecraftFont.compileFontProvider(8, -82);
|
||||||
|
|
||||||
|
MinecraftFont.compileFontProvider(8, -122);
|
||||||
|
MinecraftFont.compileFontProvider(8, -131);
|
||||||
|
MinecraftFont.compileFontProvider(8, -140);
|
||||||
|
MinecraftFont.compileFontProvider(8, -149);
|
||||||
|
|
||||||
|
MinecraftFont.compileFontProvider(8, -170);
|
||||||
|
MinecraftFont.compileFontProvider(8, -179);
|
||||||
|
MinecraftFont.compileFontProvider(8, -188);
|
||||||
|
MinecraftFont.compileFontProvider(8, -197);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initLocales() {
|
||||||
|
var locales = new HashMap<String, String>();
|
||||||
|
locales.put("container.inventory", " ");
|
||||||
|
|
||||||
|
for (var lang: List.of("en_us", "ru_ru")) {
|
||||||
|
Resources.getResourcePack().language(Language.language(Key.key("minecraft", lang), locales));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPlayerContextManager() {
|
||||||
|
MinecraftServer.getConnectionManager().setPlayerProvider(PlayerContextManager::create);
|
||||||
|
|
||||||
|
MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> {
|
||||||
|
var player = (MsbPlayer) event.getPlayer();
|
||||||
|
|
||||||
|
log.info("Connected player '{}' (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress());
|
||||||
|
player.initContexts();
|
||||||
|
}).addListener(PlayerSpawnEvent.class, event -> {
|
||||||
|
var player = (MsbPlayer) event.getPlayer();
|
||||||
|
|
||||||
|
if (event.isFirstSpawn()) player.emitSpawnSignalForContexts();
|
||||||
|
}).addListener(PlayerDisconnectEvent.class, event -> {
|
||||||
|
var player = (MsbPlayer) event.getPlayer();
|
||||||
|
|
||||||
|
log.info("Disconnected player '{}' (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress());
|
||||||
|
player.disposeContexts();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Default contexts
|
||||||
|
PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::new);
|
||||||
|
PlayerContextManager.registerContext(NavigatorContext.class, NavigatorContext::new);
|
||||||
|
PlayerContextManager.registerContext(TalksContext.class, TalksContext::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDefaultSkins() {
|
||||||
|
SkinStorage.loadSkin(SkinStorage.DEFAULT, ResourceFromJar.of("skins/default.msb3skin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDefaultDialogActionsAndConditions() {
|
||||||
|
DialogRegistry.registerActionHandler("close", new CloseDialogActionHandler());
|
||||||
|
DialogRegistry.registerActionHandler("dialog", new DialogDialogActionHandler());
|
||||||
|
|
||||||
|
DialogRegistry.registerConditionHandler("always", new AlwaysDialogConditionHandler());
|
||||||
|
DialogRegistry.registerConditionHandler("never", new NeverDialogConditionHandler());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package ru.dragonestia.msb3.api.boot;
|
||||||
|
|
||||||
|
import team.unnamed.creative.ResourcePack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс инициализатора сервера. Здесь имеются все точки состояний сервера.
|
||||||
|
* Создан для того чтобы было проще прописывать логику инициализации сервера
|
||||||
|
*/
|
||||||
|
public abstract class ServerInitializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запускается при самом старте приложения, когда никакая конфигурация сервера еще не подгружена
|
||||||
|
*/
|
||||||
|
public void onLoad() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запускается после инициализации всех стандартных модулей сервера
|
||||||
|
*/
|
||||||
|
public abstract void onDefaultModulesLoaded();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запускается на стадии подготовки всех ресурсов, которые необходимо скомпилировать и добавить в ресурс-пак.
|
||||||
|
*
|
||||||
|
* <p>Именно на данном этапе необходимо регистрировать глифы.
|
||||||
|
* @param resourcePack Ресурс-пак
|
||||||
|
*/
|
||||||
|
public void onInitializeResources(ResourcePack resourcePack) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запускается после того как все пакеты ресурсов были успешно скомпилированы и готовы к последующей упаковке
|
||||||
|
* и отправке клиенту игрока.
|
||||||
|
*
|
||||||
|
* <p> Сервер в данном состоянии еще не полностью загружен
|
||||||
|
* @param resourcePack Скомпилированный ресурс-пак
|
||||||
|
*/
|
||||||
|
public void onResourcePackCompiled(ResourcePack resourcePack) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сервер полностью запущен и доступен для игры
|
||||||
|
*/
|
||||||
|
public void onServerStarted() {}
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
package ru.dragonestia.msb3.api.command;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.event.ClickEvent;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.command.builder.Command;
|
||||||
|
import net.minestom.server.command.builder.arguments.ArgumentEnum;
|
||||||
|
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||||
|
import net.minestom.server.entity.GameMode;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.utils.location.RelativeVec;
|
||||||
|
import ru.dragonestia.msb3.api.util.Env;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@UtilityClass
|
||||||
|
public class DebugCommands {
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
if (!Env.bool("MSB3_DEBUG").orElse(false)) return;
|
||||||
|
|
||||||
|
registerPosCommand();
|
||||||
|
registerTeleportCommand();
|
||||||
|
registerGameModeCommand();
|
||||||
|
|
||||||
|
log.info("Registered debug commands");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerPosCommand() {
|
||||||
|
var command = new Command("pos");
|
||||||
|
var counter = new AtomicInteger(0);
|
||||||
|
command.setDefaultExecutor((sender, ctx) -> {
|
||||||
|
var player = (Player) sender;
|
||||||
|
var pos = player.getPosition();
|
||||||
|
var decimal = new DecimalFormat("#.00");
|
||||||
|
var result = "new Pos(%s, %s, %s, %sf, %sf)".formatted(
|
||||||
|
decimal.format(pos.x()).replace(",", "."),
|
||||||
|
decimal.format(pos.y()).replace(",", "."),
|
||||||
|
decimal.format(pos.z()).replace(",", "."),
|
||||||
|
decimal.format(pos.yaw()).replace(",", "."),
|
||||||
|
decimal.format(pos.pitch()).replace(",", ".")
|
||||||
|
);
|
||||||
|
|
||||||
|
var color = (counter.getAndIncrement() & 1) == 1 ? NamedTextColor.YELLOW : NamedTextColor.GOLD;
|
||||||
|
var component = Component.text(result, color, TextDecoration.UNDERLINED)
|
||||||
|
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, result));
|
||||||
|
player.sendMessage(Component.text(counter.get() + ") Pos: ").append(component));
|
||||||
|
|
||||||
|
log.info("Pos: {}", result);
|
||||||
|
});
|
||||||
|
MinecraftServer.getCommandManager().register(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerTeleportCommand() {
|
||||||
|
var command = new Command("tp");
|
||||||
|
command.setDefaultExecutor((source, context) -> source.sendMessage(Component.text("Использование: /tp x y z")));
|
||||||
|
var posArg = ArgumentType.RelativeBlockPosition("pos");
|
||||||
|
command.addSyntax((sender, ctx) -> {
|
||||||
|
var player = (Player) sender;
|
||||||
|
RelativeVec relativeVec = ctx.get("pos");
|
||||||
|
var position = player.getPosition().withCoord(relativeVec.fromSender(player));
|
||||||
|
player.teleport(position);
|
||||||
|
player.sendMessage(Component.text("Выбили телепортирован на " + position));
|
||||||
|
}, posArg);
|
||||||
|
MinecraftServer.getCommandManager().register(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerGameModeCommand() {
|
||||||
|
var command = new Command("gm", "gamemode");
|
||||||
|
|
||||||
|
ArgumentEnum<GameMode> gamemode = ArgumentType.Enum("gamemode", GameMode.class).setFormat(ArgumentEnum.Format.LOWER_CASED);
|
||||||
|
gamemode.setCallback((sender, exception) -> {
|
||||||
|
sender.sendMessage(Component.text("Invalid gamemode ", NamedTextColor.RED)
|
||||||
|
.append(Component.text(exception.getInput(), NamedTextColor.WHITE))
|
||||||
|
.append(Component.text("!")));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
command.setDefaultExecutor((sender, context) -> {
|
||||||
|
var commandName = context.getCommandName();
|
||||||
|
sender.sendMessage(Component.text("Использование: /" + commandName + " <gamemode>", NamedTextColor.RED));
|
||||||
|
});
|
||||||
|
|
||||||
|
command.addSyntax((sender, context) -> {
|
||||||
|
if (!(sender instanceof Player p)) {
|
||||||
|
sender.sendMessage(Component.text("Please run this command in-game.", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode = context.get(gamemode);
|
||||||
|
|
||||||
|
p.setGameMode(mode);
|
||||||
|
String gamemodeString = "gameMode." + mode.name().toLowerCase(Locale.ROOT);
|
||||||
|
Component gamemodeComponent = Component.translatable(gamemodeString);
|
||||||
|
sender.sendMessage(Component.translatable("commands.gamemode.success.self", gamemodeComponent));
|
||||||
|
}, gamemode);
|
||||||
|
MinecraftServer.getCommandManager().register(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
api/src/main/java/ru/dragonestia/msb3/api/dialog/Dialog.java
Normal file
77
api/src/main/java/ru/dragonestia/msb3/api/dialog/Dialog.java
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
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.dialog.interlocutor.Interlocutor;
|
||||||
|
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.List;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Dialog {
|
||||||
|
|
||||||
|
private Key id;
|
||||||
|
private String text;
|
||||||
|
private boolean rememberId;
|
||||||
|
private String themeId;
|
||||||
|
private List<DialogButton> buttons;
|
||||||
|
|
||||||
|
public void switchDialog(Player player, DialogueRenderer renderer, Interlocutor interlocutor) {
|
||||||
|
var ctx = PlayerContext.of(player, TalksContext.class);
|
||||||
|
if (rememberId) {
|
||||||
|
ctx.getOpenedDialogues().add(id.asString());
|
||||||
|
DebugMessage.send(player, "Идентификатор диалога %s был сохранен в список просмотренных диалогов".formatted(id.asString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogueTheme theme = DialogueTheme.builder().build();
|
||||||
|
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.toBuilder()
|
||||||
|
.setAvatar(interlocutor.getDialogAvatar())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
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(), interlocutor, this, button, buttonCtx.buttonNumber(), buttonCtx.renderer());
|
||||||
|
button.onClick(click);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
renderer.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void open(Player player, Interlocutor interlocutor) {
|
||||||
|
var renderer = DialogueRenderer.getRenderer(player).orElseGet(() -> new DialogueRenderer(player, DialogueTheme.builder().build()));
|
||||||
|
switchDialog(player, renderer, interlocutor);
|
||||||
|
renderer.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
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) {
|
||||||
|
if (PlayerContext.of(player, TalksContext.class).isIgnoreDialogConditions()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
DebugMessage.send(player, "Выполнение действия диалога actionId=%s params=%s".formatted(actionId, params));
|
||||||
|
action.get().handle(click, 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,15 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.interlocutor.Interlocutor;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
|
||||||
|
public record DialogButtonClick(
|
||||||
|
Player player,
|
||||||
|
Interlocutor interlocutor,
|
||||||
|
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, button, 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,60 @@
|
|||||||
|
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 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 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 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,32 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.action;
|
||||||
|
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.DialogRegistry;
|
||||||
|
import ru.dragonestia.msb3.api.util.DebugMessage;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
public class DialogDialogActionHandler implements DialogActionHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(DialogButtonClick click, Map<String, String> params) {
|
||||||
|
var player = click.player();
|
||||||
|
var dialogId = params.get("dialogId");
|
||||||
|
if (dialogId == null) {
|
||||||
|
DebugMessage.sendError(player, "Отсутствует обязательный параметр dialogId для команды dialog");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dialog = DialogRegistry.findDialog(Key.key(dialogId));
|
||||||
|
if (dialog.isEmpty()) {
|
||||||
|
log.error("Dialog {} not found", dialogId);
|
||||||
|
DebugMessage.sendError(player, "Диалог с идентификатором %s не найден".formatted(dialogId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.get().switchDialog(player, click.renderer(), click.interlocutor());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
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.dialog.DialogButton;
|
||||||
|
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, DialogButton button, DialogueRenderer renderer, Map<String, String> params) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
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.dialog.DialogButton;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface DialogConditionHandler {
|
||||||
|
|
||||||
|
boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer, Map<String, String> params);
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
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.dialog.DialogButton;
|
||||||
|
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, DialogButton button, 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,15 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.interlocutor;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.api.resource.DialogueResources;
|
||||||
|
import ru.dragonestia.msb3.api.resource.MonologueResources;
|
||||||
|
|
||||||
|
public class AnonymousInterlocutor extends SimpleInterlocutor {
|
||||||
|
|
||||||
|
public AnonymousInterlocutor(String name) {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
DialogueResources.getAvatar(DialogueResources.DEFAULT).orElseThrow(),
|
||||||
|
MonologueResources.getAvatar(MonologueResources.DEFAULT).orElseThrow()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.interlocutor;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.dialog.Dialog;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
|
||||||
|
import ru.dragonestia.msb3.api.ui.monologue.Monologue;
|
||||||
|
import ru.dragonestia.msb3.api.ui.monologue.MonologueTheme;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
|
||||||
|
public interface Interlocutor {
|
||||||
|
|
||||||
|
String getInterlocutorName();
|
||||||
|
|
||||||
|
GlyphImage getDialogAvatar();
|
||||||
|
|
||||||
|
GlyphImage getMonologAvatar();
|
||||||
|
|
||||||
|
default MonologueTheme getMonologueTheme() {
|
||||||
|
return MonologueTheme.builder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMonolog(Player receiver, String message) {
|
||||||
|
sendMonolog(receiver, message, getMonologueTheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMonolog(Player receiver, String message, MonologueTheme theme) {
|
||||||
|
Monologue.create(receiver, getInterlocutorName(), message).show(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendDialog(Player receiver, Dialog dialog) {
|
||||||
|
sendDialog(receiver, dialog, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendDialog(Player receiver, Dialog dialog, boolean inDialogueWindowNow) {
|
||||||
|
if (inDialogueWindowNow) {
|
||||||
|
var renderer = DialogueRenderer.getRenderer(receiver).orElseThrow();
|
||||||
|
dialog.switchDialog(receiver, renderer, this);
|
||||||
|
renderer.rerender();
|
||||||
|
} else {
|
||||||
|
dialog.open(receiver, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.interlocutor;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SimpleInterlocutor implements Interlocutor {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private GlyphImage dialogAvatar;
|
||||||
|
private GlyphImage monologAvatar;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getInterlocutorName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GlyphImage getDialogAvatar() {
|
||||||
|
return dialogAvatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GlyphImage getMonologAvatar() {
|
||||||
|
return monologAvatar;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
package ru.dragonestia.msb3.api.dialog.provider;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
|
||||||
|
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
|
||||||
|
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||||
|
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||||
|
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 team.unnamed.creative.base.Writable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@UtilityClass
|
||||||
|
public class DialogFileProvider {
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public void readFromHocon(Writable writable) {
|
||||||
|
var root = HoconConfigurationLoader.builder()
|
||||||
|
.buildAndLoadString(writable.toUTF8String());
|
||||||
|
loadDialogs(Objects.requireNonNull(root.get(DialogsList.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public void readFromJson(Writable writable) {
|
||||||
|
var root = GsonConfigurationLoader.builder()
|
||||||
|
.buildAndLoadString(writable.toUTF8String());
|
||||||
|
loadDialogs(Objects.requireNonNull(root.get(DialogsList.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public void readFromYaml(Writable writable) {
|
||||||
|
var root = YamlConfigurationLoader.builder()
|
||||||
|
.buildAndLoadString(writable.toUTF8String());
|
||||||
|
loadDialogs(Objects.requireNonNull(root.get(DialogsList.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadDialogs(DialogsList source) {
|
||||||
|
for (var entry: source.dialogs.entrySet()) {
|
||||||
|
var dialogId = entry.getKey();
|
||||||
|
var dialogEntry = entry.getValue();
|
||||||
|
var dialog = new Dialog();
|
||||||
|
|
||||||
|
dialog.setId(Key.key(dialogId));
|
||||||
|
dialog.setText(Objects.requireNonNull(dialogEntry.text, "Text is null inside dialog " + dialogId));
|
||||||
|
dialog.setRememberId(dialogEntry.remember);
|
||||||
|
dialog.setThemeId(dialogEntry.theme);
|
||||||
|
|
||||||
|
var buttons = new ArrayList<DialogButton>();
|
||||||
|
for (var buttonEntry: dialogEntry.buttons) {
|
||||||
|
var button = new DialogButton();
|
||||||
|
|
||||||
|
if (buttonEntry.id != null) button.setId(Key.key(buttonEntry.id));
|
||||||
|
button.setText(Objects.requireNonNull(buttonEntry.text));
|
||||||
|
button.setActionId(Objects.requireNonNull(buttonEntry.action));
|
||||||
|
button.setParams(Objects.requireNonNull(buttonEntry.params));
|
||||||
|
|
||||||
|
var conditions = new ArrayList<DialogCondition>();
|
||||||
|
for (var conditionEntry: buttonEntry.conditions) {
|
||||||
|
var condition = new DialogCondition();
|
||||||
|
|
||||||
|
condition.setConditionId(Objects.requireNonNull(conditionEntry.id));
|
||||||
|
condition.setParams(Objects.requireNonNull(conditionEntry.params));
|
||||||
|
}
|
||||||
|
button.setConditions(conditions);
|
||||||
|
|
||||||
|
buttons.add(button);
|
||||||
|
}
|
||||||
|
dialog.setButtons(buttons);
|
||||||
|
|
||||||
|
DialogRegistry.storeDialog(dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ConfigSerializable
|
||||||
|
public static class DialogsList {
|
||||||
|
|
||||||
|
private Map<String, DialogEntry> dialogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@ConfigSerializable
|
||||||
|
public static class DialogEntry {
|
||||||
|
private String text;
|
||||||
|
private boolean remember = false;
|
||||||
|
private String theme = null;
|
||||||
|
private List<ButtonEntry> buttons;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{text=" + text + ", remember=" + remember + ", theme=" + theme + ", buttons=" + buttons + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@ConfigSerializable
|
||||||
|
public static class ButtonEntry {
|
||||||
|
private String id = null;
|
||||||
|
private String text;
|
||||||
|
private String action;
|
||||||
|
private Map<String, String> params = new HashMap<>();
|
||||||
|
private List<ConditionEntry> conditions = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{id=" + id + ", text=" + text + ", action=" + action + ", params=" + params + ", conditions=" + conditions + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@ConfigSerializable
|
||||||
|
public static class ConditionEntry {
|
||||||
|
private String id;
|
||||||
|
private Map<String, String> params = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{id=" + id + ", params=" + params + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
api/src/main/java/ru/dragonestia/msb3/api/entity/Human.java
Normal file
153
api/src/main/java/ru/dragonestia/msb3/api/entity/Human.java
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package ru.dragonestia.msb3.api.entity;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.coordinate.Point;
|
||||||
|
import net.minestom.server.coordinate.Pos;
|
||||||
|
import net.minestom.server.coordinate.Vec;
|
||||||
|
import net.minestom.server.entity.*;
|
||||||
|
import net.minestom.server.entity.attribute.Attribute;
|
||||||
|
import net.minestom.server.entity.metadata.display.AbstractDisplayMeta;
|
||||||
|
import net.minestom.server.entity.metadata.display.TextDisplayMeta;
|
||||||
|
import net.minestom.server.instance.Instance;
|
||||||
|
import net.minestom.server.network.packet.server.play.PlayerInfoRemovePacket;
|
||||||
|
import net.minestom.server.network.packet.server.play.PlayerInfoUpdatePacket;
|
||||||
|
import net.minestom.server.network.packet.server.play.TeamsPacket;
|
||||||
|
import net.minestom.server.scoreboard.Team;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class Human extends EntityCreature {
|
||||||
|
|
||||||
|
private static final AtomicInteger freeId = new AtomicInteger(0);
|
||||||
|
private static final Map<String, Team> npcTeams = new ConcurrentHashMap<>();
|
||||||
|
private static final Point displayNamePositionOffset = new Vec(0, 2, 0);
|
||||||
|
|
||||||
|
@Getter private Component name;
|
||||||
|
private final String username;
|
||||||
|
@Getter private final PlayerSkin skin;
|
||||||
|
private final NamedTextColor teamColor;
|
||||||
|
private final PlayerInfoUpdatePacket createPacket;
|
||||||
|
private final PlayerInfoRemovePacket removePacket;
|
||||||
|
private final Team team;
|
||||||
|
private Entity displayNameEntity;
|
||||||
|
private Predicate<Player> lastViewableRule;
|
||||||
|
|
||||||
|
public Human(String name, PlayerSkin skin) {
|
||||||
|
this(Component.text(name, NamedTextColor.YELLOW), skin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Human(Component name, PlayerSkin skin) {
|
||||||
|
this(name, skin, NamedTextColor.YELLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Human(Component name, PlayerSkin skin, NamedTextColor teamColor) {
|
||||||
|
super(EntityType.PLAYER);
|
||||||
|
this.name = name;
|
||||||
|
this.username = "Human_" + freeId.incrementAndGet();
|
||||||
|
this.skin = skin;
|
||||||
|
this.teamColor = teamColor;
|
||||||
|
this.createPacket = getCreatePacket();
|
||||||
|
this.removePacket = new PlayerInfoRemovePacket(getUuid());
|
||||||
|
this.team = pickTeam();
|
||||||
|
|
||||||
|
getEntityMeta().setCustomNameVisible(false);
|
||||||
|
setBoundingBox(0.6, 1.8, 0.6);
|
||||||
|
getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.2F);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerInfoUpdatePacket getCreatePacket() {
|
||||||
|
var properties = List.of(new PlayerInfoUpdatePacket.Property("textures", skin.textures(), skin.signature()));
|
||||||
|
return new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.ADD_PLAYER,
|
||||||
|
new PlayerInfoUpdatePacket.Entry(getUuid(), username, properties, false, 0, GameMode.CREATIVE, name, null, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> setInstance(@NotNull Instance instance, @NotNull Pos spawnPosition) {
|
||||||
|
return super.setInstance(instance, spawnPosition)
|
||||||
|
.thenRun(this::getDisplayNameEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull CompletableFuture<Void> teleport(@NotNull Pos position, @NotNull Vec velocity, long @Nullable [] chunks, int flags, boolean shouldConfirm) {
|
||||||
|
return super.teleport(position, velocity, chunks, flags, shouldConfirm)
|
||||||
|
.thenRun(() -> getDisplayNameEntity().teleport(position.add(displayNamePositionOffset), chunks, flags, shouldConfirm));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshPosition(@NotNull Pos newPosition, boolean ignoreView, boolean sendPackets) {
|
||||||
|
super.refreshPosition(newPosition, ignoreView, sendPackets);
|
||||||
|
getDisplayNameEntity().refreshPosition(newPosition.add(displayNamePositionOffset), ignoreView, sendPackets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void remove(boolean permanent) {
|
||||||
|
super.remove(permanent);
|
||||||
|
if (displayNameEntity != null) displayNameEntity.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateNewViewer(@NotNull Player player) {
|
||||||
|
player.sendPacket(createPacket);
|
||||||
|
super.updateNewViewer(player);
|
||||||
|
player.sendPacket(new TeamsPacket(team.getTeamName(), new TeamsPacket.AddEntitiesToTeamAction(List.of(username))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateOldViewer(@NotNull Player player) {
|
||||||
|
player.sendPacket(removePacket);
|
||||||
|
super.updateOldViewer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateViewableRule() {
|
||||||
|
super.updateViewableRule();
|
||||||
|
if (displayNameEntity != null) displayNameEntity.updateViewableRule();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateViewableRule(@Nullable Predicate<Player> predicate) {
|
||||||
|
super.updateViewableRule(lastViewableRule = predicate);
|
||||||
|
if (displayNameEntity != null) displayNameEntity.updateViewableRule(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Team pickTeam() {
|
||||||
|
return npcTeams.computeIfAbsent(teamColor.asHexString(), $ -> MinecraftServer.getTeamManager()
|
||||||
|
.createBuilder("NPC_r%s_g%s_b%s".formatted(teamColor.red(), teamColor.green(), teamColor.blue()))
|
||||||
|
.collisionRule(TeamsPacket.CollisionRule.ALWAYS)
|
||||||
|
.teamColor(teamColor)
|
||||||
|
.nameTagVisibility(TeamsPacket.NameTagVisibility.NEVER)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized Entity getDisplayNameEntity() {
|
||||||
|
if (displayNameEntity == null) {
|
||||||
|
displayNameEntity = new Entity(EntityType.TEXT_DISPLAY);
|
||||||
|
var meta = (TextDisplayMeta) displayNameEntity.getEntityMeta();
|
||||||
|
meta.setText(name);
|
||||||
|
meta.setHasNoGravity(true);
|
||||||
|
meta.setBillboardRenderConstraints(AbstractDisplayMeta.BillboardConstraints.VERTICAL);
|
||||||
|
if (lastViewableRule != null) displayNameEntity.updateViewableRule(lastViewableRule);
|
||||||
|
displayNameEntity.setInstance(instance, getPosition().add(displayNamePositionOffset));
|
||||||
|
}
|
||||||
|
return displayNameEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(Component name) {
|
||||||
|
this.name = name;
|
||||||
|
if (displayNameEntity != null) {
|
||||||
|
var meta = (TextDisplayMeta) displayNameEntity.getEntityMeta();
|
||||||
|
meta.setText(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
api/src/main/java/ru/dragonestia/msb3/api/entity/NPC.java
Normal file
13
api/src/main/java/ru/dragonestia/msb3/api/entity/NPC.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ru.dragonestia.msb3.api.entity;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.event.NPCClickEvent;
|
||||||
|
|
||||||
|
public interface NPC {
|
||||||
|
|
||||||
|
default boolean supportedClickType(NPCClickEvent.ClickType clickType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onClickByPlayer(Player player, NPCClickEvent.ClickType clickType);
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package ru.dragonestia.msb3.api.entity;
|
||||||
|
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.entity.ItemEntity;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.event.item.PickupItemEvent;
|
||||||
|
import net.minestom.server.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
|
||||||
|
public class PickableItem extends ItemEntity {
|
||||||
|
|
||||||
|
public PickableItem(@NotNull ItemStack itemStack) {
|
||||||
|
super(itemStack);
|
||||||
|
|
||||||
|
setPickable(true);
|
||||||
|
setMergeable(false);
|
||||||
|
setPickupDelay(500, ChronoUnit.MILLIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerEvent() {
|
||||||
|
MinecraftServer.getGlobalEventHandler().addListener(PickupItemEvent.class, event -> {
|
||||||
|
if (!(event.getItemEntity() instanceof PickableItem)) return;
|
||||||
|
if (!(event.getEntity() instanceof Player player)) return;
|
||||||
|
|
||||||
|
var itemEntity = event.getItemEntity();
|
||||||
|
var inv = player.getInventory();
|
||||||
|
var item = itemEntity.getItemStack();
|
||||||
|
|
||||||
|
if (!inv.addItemStack(item)) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package ru.dragonestia.msb3.api.entity;
|
||||||
|
|
||||||
|
import net.minestom.server.coordinate.Pos;
|
||||||
|
import net.minestom.server.entity.Entity;
|
||||||
|
import net.minestom.server.entity.EntityType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class StaticEntity extends Entity {
|
||||||
|
|
||||||
|
public StaticEntity(@NotNull EntityType entityType) {
|
||||||
|
super(entityType);
|
||||||
|
|
||||||
|
setNoGravity(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Spot(Pos pos, Supplier<StaticEntity> supplier) {}
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
package ru.dragonestia.msb3.api.entity.goal;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.minestom.server.entity.EntityCreature;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.entity.ai.GoalSelector;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class LookCloseGoal extends GoalSelector {
|
||||||
|
|
||||||
|
private final static int MAX_DIST = 7 * 7;
|
||||||
|
|
||||||
|
@Setter @Getter private volatile boolean enabled = false;
|
||||||
|
private boolean lastSeePlayer = false;
|
||||||
|
@Getter private float defaultYaw;
|
||||||
|
@Getter private float defaultPitch;
|
||||||
|
|
||||||
|
public LookCloseGoal(@NotNull EntityCreature entityCreature) {
|
||||||
|
super(entityCreature);
|
||||||
|
|
||||||
|
defaultYaw = entityCreature.getPosition().yaw();
|
||||||
|
defaultPitch = entityCreature.getPosition().pitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldStart() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(long l) {
|
||||||
|
if ((l & 0b11) == 0) return;
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
if (lastSeePlayer) {
|
||||||
|
entityCreature.setView(defaultYaw, defaultPitch);
|
||||||
|
lastSeePlayer = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player closestPlayer = null;
|
||||||
|
double closestDistance = Double.MAX_VALUE;
|
||||||
|
|
||||||
|
for (var player: entityCreature.getViewers()) {
|
||||||
|
var distance = player.getPosition().distanceSquared(entityCreature.getPosition());
|
||||||
|
if (distance > MAX_DIST) continue;
|
||||||
|
|
||||||
|
if (distance < closestDistance) {
|
||||||
|
closestDistance = distance;
|
||||||
|
closestPlayer = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closestPlayer != null) {
|
||||||
|
entityCreature.lookAt(closestPlayer);
|
||||||
|
lastSeePlayer = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSeePlayer) {
|
||||||
|
entityCreature.setView(defaultYaw, defaultPitch);
|
||||||
|
lastSeePlayer = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnd() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end() {}
|
||||||
|
|
||||||
|
public void setDefaultRotation(float yaw, float pitch) {
|
||||||
|
defaultYaw = yaw;
|
||||||
|
defaultPitch = pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package ru.dragonestia.msb3.api.event;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.entity.Entity;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.entity.PlayerHand;
|
||||||
|
import net.minestom.server.event.entity.EntityAttackEvent;
|
||||||
|
import net.minestom.server.event.player.PlayerEntityInteractEvent;
|
||||||
|
import net.minestom.server.event.trait.EntityEvent;
|
||||||
|
import net.minestom.server.event.trait.InstanceEvent;
|
||||||
|
import net.minestom.server.instance.Instance;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import ru.dragonestia.msb3.api.entity.NPC;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NPCClickEvent implements EntityEvent, InstanceEvent {
|
||||||
|
|
||||||
|
@Getter private final Player player;
|
||||||
|
private final NPC npcEntity;
|
||||||
|
@Getter private final ClickType clickType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Entity getEntity() {
|
||||||
|
return (Entity) npcEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Instance getInstance() {
|
||||||
|
return Objects.requireNonNull(player.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ClickType {
|
||||||
|
ATTACK,
|
||||||
|
INTERACT
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
MinecraftServer.getGlobalEventHandler().addListener(PlayerEntityInteractEvent.class, event -> {
|
||||||
|
if (event.getHand() != PlayerHand.MAIN) return;
|
||||||
|
if (event.getEntity() instanceof NPC npc) {
|
||||||
|
if (!npc.supportedClickType(ClickType.INTERACT)) return;
|
||||||
|
MinecraftServer.getGlobalEventHandler().call(new NPCClickEvent(event.getPlayer(), npc, ClickType.INTERACT));
|
||||||
|
}
|
||||||
|
}).addListener(EntityAttackEvent.class, event -> {
|
||||||
|
if (event.getTarget() instanceof NPC npc && event.getEntity() instanceof Player player) {
|
||||||
|
if (!npc.supportedClickType(ClickType.ATTACK)) return;
|
||||||
|
MinecraftServer.getGlobalEventHandler().call(new NPCClickEvent(player, npc, ClickType.ATTACK));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph;
|
|
||||||
|
|
||||||
public final class GlyphConstants {
|
|
||||||
|
|
||||||
public static final int CHEST_GUI_WIDTH = 176;
|
|
||||||
public static final int DEFAULT_CHAT_WIDTH = 320;
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.compile;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public interface ArbitraryCharacterFactory {
|
|
||||||
|
|
||||||
@NotNull Character nextCharacter() throws IllegalStateException;
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.compile;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class DefaultArbitraryCharacterFactory implements ArbitraryCharacterFactory {
|
|
||||||
|
|
||||||
private final static Set<Character> reservedCharacters = new HashSet<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
for (char c = 'a'; c <= 'z'; c++) reservedCharacters.add(c);
|
|
||||||
for (char c = 'A'; c <= 'Z'; c++) reservedCharacters.add(c);
|
|
||||||
for (char c = 'а'; c <= 'я'; c++) reservedCharacters.add(c);
|
|
||||||
for (char c = 'А'; c <= 'Я'; c++) reservedCharacters.add(c);
|
|
||||||
for (char c = '0'; c <= '9'; c++) reservedCharacters.add(c);
|
|
||||||
|
|
||||||
reservedCharacters.addAll(Arrays.asList(
|
|
||||||
'!', '?', ':', '$',
|
|
||||||
';', '#', '@', '%',
|
|
||||||
'^', '&', '*', '(',
|
|
||||||
')', '_', '-', '+',
|
|
||||||
'/', '\\', '№', '"',
|
|
||||||
'\'', '{', '}', '[',
|
|
||||||
']', '~', '`', '<', '>',
|
|
||||||
',', '.', '|', '\n', '\r',
|
|
||||||
'\b', '\f', '\t', ' ',
|
|
||||||
'ё', 'Ё', '=')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private char currentChar = '\uA201';
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Character nextCharacter() throws IllegalStateException {
|
|
||||||
if (currentChar == Character.MAX_VALUE) {
|
|
||||||
throw new IllegalStateException("Characters range exceeded");
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
currentChar++;
|
|
||||||
} while (!isCharacterAllowed(currentChar));
|
|
||||||
return currentChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isCharacterAllowed(char c) {
|
|
||||||
return !reservedCharacters.contains(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.compile;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import team.unnamed.creative.font.Font;
|
|
||||||
import team.unnamed.creative.font.FontProvider;
|
|
||||||
import team.unnamed.creative.part.ResourcePackPart;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class DefaultGlyphCompiler implements GlyphCompiler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull ResourcePackPart> compile(@NotNull Collection<@NotNull ResourceProducer> producers) {
|
|
||||||
Set<ResourcePackPart> fileResources = new HashSet<>();
|
|
||||||
|
|
||||||
Set<Key> fontKeys = producers
|
|
||||||
.stream()
|
|
||||||
.map(ResourceProducer::fontKey)
|
|
||||||
.collect(Collectors.toUnmodifiableSet());
|
|
||||||
|
|
||||||
for (Key key : fontKeys) {
|
|
||||||
List<FontProvider> fontProviders = new ArrayList<>();
|
|
||||||
ArbitraryCharacterFactory characterFactory = new DefaultArbitraryCharacterFactory();
|
|
||||||
for (ResourceProducer producer : producers) {
|
|
||||||
if (producer.fontKey().equals(key)) {
|
|
||||||
producer.produceResources(characterFactory);
|
|
||||||
// Add font providers for current font key
|
|
||||||
fontProviders.addAll(producer.fontProviders());
|
|
||||||
// Add textures to common set with resources
|
|
||||||
fileResources.addAll(producer.textures());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fileResources.add(Font.font(key, fontProviders));
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
<T> Iterable<T> toIterable(final Stream<T> stream) {
|
|
||||||
return stream::iterator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.compile;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import team.unnamed.creative.part.ResourcePackPart;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public interface GlyphCompiler {
|
|
||||||
|
|
||||||
static GlyphCompiler instance() {
|
|
||||||
return new DefaultGlyphCompiler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull Collection<@NotNull ResourcePackPart> compile(@NotNull Collection<@NotNull ResourceProducer> resourceProducerCollection);
|
|
||||||
|
|
||||||
default @NotNull Collection<@NotNull ResourcePackPart> compile(@NotNull ResourceProducer... resourceProducer) {
|
|
||||||
return compile(Arrays.asList(resourceProducer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.compile;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import team.unnamed.creative.font.FontProvider;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public interface ResourceProducer {
|
|
||||||
|
|
||||||
@NotNull Key fontKey();
|
|
||||||
|
|
||||||
boolean produced();
|
|
||||||
|
|
||||||
void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException;
|
|
||||||
|
|
||||||
@NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException;
|
|
||||||
|
|
||||||
default @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.font;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.ServerBootstrap;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter.LanguageGlyphCollection;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.mojang.MojangSpacesGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
|
||||||
import team.unnamed.creative.base.Writable;
|
|
||||||
import team.unnamed.creative.part.ResourcePackPart;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class GlyphFont {
|
|
||||||
|
|
||||||
public static final SpacesGlyphResourceProducer SPACES = MojangSpacesGlyph.create();
|
|
||||||
private static final Key MINECRAFT_FONT_KEY = Key.key(Glyph.DEFAULT_NAMESPACE, "minecraft_font");
|
|
||||||
private static final Writable MINECRAFT_FONT_IMAGE_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/defaults/minecraft_font.png");
|
|
||||||
public static final LanguageGlyphCollection LANGUAGE_GLYPH = minecraftFontGlyphCollection(
|
|
||||||
MINECRAFT_FONT_KEY,
|
|
||||||
Key.key(MINECRAFT_FONT_KEY.asString().concat(".png")),
|
|
||||||
List.of(new TextureProperties(12,-6),
|
|
||||||
new TextureProperties(8,-24),
|
|
||||||
new TextureProperties(8,-36)));
|
|
||||||
|
|
||||||
private GlyphFont() {}
|
|
||||||
|
|
||||||
public static Collection<ResourcePackPart> compile() {
|
|
||||||
return GlyphCompiler.instance().compile(SPACES, LANGUAGE_GLYPH);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @NotNull LanguageGlyphCollection minecraftFontGlyphCollection(@NotNull Key fontKey, @NotNull Key textureKey, @NotNull List<@NotNull TextureProperties> propertiesList) {
|
|
||||||
return LanguageGlyphCollection.of(fontKey,
|
|
||||||
Texture.texture(textureKey, MINECRAFT_FONT_IMAGE_WRITABLE),
|
|
||||||
propertiesList,
|
|
||||||
List.of(" АБВГДЕЖЗИК",
|
|
||||||
"ЛМНОПРСТУФХЦЧШЩЪ",
|
|
||||||
"ЫЬЭЮЯабвгдежзикл",
|
|
||||||
"мнопрстуфхцчшщъы",
|
|
||||||
"ьэюяйЙёЁ ",
|
|
||||||
"₽!\"#$%&'()*+,-./",
|
|
||||||
"0123456789: <=>?",
|
|
||||||
"@ABCDEFGHIJKLMNO",
|
|
||||||
"PQRSTUVWXYZ[\\]^_",
|
|
||||||
"`abcdefghijklmno",
|
|
||||||
"pqrstuvwxyz{|} ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph;
|
|
||||||
|
|
||||||
public interface AppendableGlyph extends Glyph {}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph;
|
|
||||||
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class EmptyGlyph implements AppendableGlyph {
|
|
||||||
|
|
||||||
public static final EmptyGlyph INSTANCE = new EmptyGlyph();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Component toAdventure() throws ResourceNotProducedException {
|
|
||||||
return Component.text("");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int width() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
|
|
||||||
public interface Glyph {
|
|
||||||
|
|
||||||
String DEFAULT_NAMESPACE = "msb3";
|
|
||||||
|
|
||||||
Key DEFAULT_FONT_KEY = Key.key(DEFAULT_NAMESPACE, "default");
|
|
||||||
|
|
||||||
Key DEFAULT_SPACES_FONT_KEY = Key.key(DEFAULT_NAMESPACE, "spaces");
|
|
||||||
|
|
||||||
int SEPARATOR_WIDTH = 1;
|
|
||||||
|
|
||||||
@NotNull Component toAdventure() throws ResourceNotProducedException;
|
|
||||||
|
|
||||||
int width();
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph;
|
|
||||||
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface GlyphComponentBuilder {
|
|
||||||
|
|
||||||
static @NotNull GlyphComponentBuilder universal(SpacesGlyphResourceProducer spacesProducer) {
|
|
||||||
return new GlyphComponentBuilderImpl(spacesProducer, 0, Component.text(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
static @NotNull GlyphComponentBuilder gui(SpacesGlyphResourceProducer spacesProducer) {
|
|
||||||
return new GlyphComponentBuilderImpl(spacesProducer, -8, Component.text("", NamedTextColor.WHITE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull AppendableGlyph glyph);
|
|
||||||
|
|
||||||
default @NotNull GlyphComponentBuilder append(PositionType positionType, @NotNull AppendableGlyph glyph) {
|
|
||||||
return append(positionType, 0, glyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull List<? extends @NotNull AppendableGlyph> glyphList);
|
|
||||||
|
|
||||||
default @NotNull GlyphComponentBuilder append(PositionType positionType, @NotNull List<? extends @NotNull AppendableGlyph> glyphList) {
|
|
||||||
return append(positionType, 0, glyphList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append with default position type
|
|
||||||
|
|
||||||
default @NotNull GlyphComponentBuilder append(int position, @NotNull AppendableGlyph glyph) {
|
|
||||||
return append(PositionType.ABSOLUTE, position, glyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @NotNull GlyphComponentBuilder append(@NotNull AppendableGlyph glyph) {
|
|
||||||
return append(PositionType.ABSOLUTE, glyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @NotNull GlyphComponentBuilder append(int position, @NotNull List<? extends @NotNull AppendableGlyph> glyphList) {
|
|
||||||
return append(PositionType.ABSOLUTE, position, glyphList);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @NotNull GlyphComponentBuilder append(@NotNull List<? extends @NotNull AppendableGlyph> glyphList) {
|
|
||||||
return append(PositionType.ABSOLUTE, glyphList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull Component build(boolean keepInitialPosition);
|
|
||||||
|
|
||||||
default @NotNull Component build() {
|
|
||||||
return build(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PositionType {
|
|
||||||
ABSOLUTE,
|
|
||||||
RELATIVE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph;
|
|
||||||
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public class GlyphComponentBuilderImpl implements GlyphComponentBuilder {
|
|
||||||
|
|
||||||
private final SpacesGlyphResourceProducer spacesProducer;
|
|
||||||
private final int initialPosition;
|
|
||||||
private final Component baseComponent;
|
|
||||||
|
|
||||||
private final List<Glyph> glyphs = new ArrayList<>();
|
|
||||||
|
|
||||||
private int previousElementsWidth;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull AppendableGlyph glyph) {
|
|
||||||
if (positionType == PositionType.ABSOLUTE && previousElementsWidth != 0) {
|
|
||||||
glyphs.add(spacesProducer.translate((-1) * previousElementsWidth));
|
|
||||||
previousElementsWidth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position != 0) {
|
|
||||||
glyphs.add(spacesProducer.translate(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
glyphs.add(glyph);
|
|
||||||
this.previousElementsWidth += position + glyph.width();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull GlyphComponentBuilder append(PositionType positionType, int position, @NotNull List<? extends @NotNull AppendableGlyph> glyphList) {
|
|
||||||
if (positionType == PositionType.ABSOLUTE && previousElementsWidth != 0) {
|
|
||||||
glyphs.add(spacesProducer.translate((-1) * previousElementsWidth));
|
|
||||||
previousElementsWidth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position != 0) {
|
|
||||||
glyphs.add(spacesProducer.translate(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = 0;
|
|
||||||
for (AppendableGlyph glyph : glyphList) {
|
|
||||||
glyphs.add(glyph);
|
|
||||||
width += glyph.width();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.previousElementsWidth += position + width;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Component build(boolean keepInitialPosition) {
|
|
||||||
if (keepInitialPosition) {
|
|
||||||
previousElementsWidth += initialPosition;
|
|
||||||
|
|
||||||
// Component should have zero width finally
|
|
||||||
if (previousElementsWidth != 0) {
|
|
||||||
glyphs.add(spacesProducer.translate((-1) * previousElementsWidth));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var component = baseComponent;
|
|
||||||
|
|
||||||
if (initialPosition != 0) {
|
|
||||||
component = component.append(spacesProducer.translate(initialPosition).toAdventure());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Glyph glyph : glyphs) {
|
|
||||||
component = component.append(glyph.toAdventure());
|
|
||||||
}
|
|
||||||
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.exception;
|
|
||||||
|
|
||||||
public class ResourceAlreadyProducedException extends RuntimeException {}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.exception;
|
|
||||||
|
|
||||||
public class ResourceNotProducedException extends RuntimeException {}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
public interface ImageGlyph extends AppendableGlyph, ResourceProducer {
|
|
||||||
|
|
||||||
static @NotNull ImageGlyph of(@NotNull Key key,
|
|
||||||
@NotNull Texture texture,
|
|
||||||
@NotNull TextureProperties properties) {
|
|
||||||
return new ImageGlyphImpl(key, texture, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
static @NotNull ImageGlyph of(@NotNull Texture texture,
|
|
||||||
@NotNull TextureProperties properties) {
|
|
||||||
return of(Glyph.DEFAULT_FONT_KEY, texture, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull Character character() throws ResourceNotProducedException;
|
|
||||||
|
|
||||||
@NotNull Texture texture();
|
|
||||||
|
|
||||||
default @NotNull Component toAdventure() throws ResourceNotProducedException {
|
|
||||||
return Component.text(character()).font(fontKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.util.ImageUtil;
|
|
||||||
import team.unnamed.creative.font.FontProvider;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public class ImageGlyphImpl implements ImageGlyph {
|
|
||||||
|
|
||||||
private final Key fontKey;
|
|
||||||
private final Texture texture;
|
|
||||||
private final TextureProperties properties;
|
|
||||||
|
|
||||||
private Character character;
|
|
||||||
private Set<FontProvider> fontProviders;
|
|
||||||
|
|
||||||
private int width = -1;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Key fontKey() {
|
|
||||||
return fontKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean produced() {
|
|
||||||
return fontProviders != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException {
|
|
||||||
if (fontProviders != null) {
|
|
||||||
throw new ResourceAlreadyProducedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var fontProviderBuilder = FontProvider.bitMap();
|
|
||||||
|
|
||||||
this.character = characterFactory.nextCharacter();
|
|
||||||
|
|
||||||
fontProviderBuilder.characters(String.valueOf(character));
|
|
||||||
fontProviderBuilder.file(texture.key());
|
|
||||||
fontProviderBuilder.ascent(properties.ascent());
|
|
||||||
fontProviderBuilder.height(properties.height());
|
|
||||||
|
|
||||||
this.fontProviders = Collections.singleton(fontProviderBuilder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException {
|
|
||||||
if (fontProviders == null) {
|
|
||||||
throw new ResourceNotProducedException();
|
|
||||||
}
|
|
||||||
return fontProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException {
|
|
||||||
return Collections.singleton(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int width() {
|
|
||||||
if (width == -1) {
|
|
||||||
try {
|
|
||||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(texture.data().toByteArray()));
|
|
||||||
int fileHeight = image.getHeight();
|
|
||||||
width = (int) Math.ceil(
|
|
||||||
((double) properties.height() / (double) fileHeight)
|
|
||||||
* ImageUtil.calculateWidth(image)) + Glyph.SEPARATOR_WIDTH;
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Character character() throws ResourceNotProducedException {
|
|
||||||
if (fontProviders == null) {
|
|
||||||
throw new ResourceNotProducedException();
|
|
||||||
}
|
|
||||||
return character;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Texture texture() {
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
|
|
||||||
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public interface ColorableGlyph {
|
|
||||||
|
|
||||||
@Nullable TextColor color();
|
|
||||||
|
|
||||||
void updateColor(@Nullable TextColor color);
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface LanguageGlyphCollection extends ResourceProducer {
|
|
||||||
|
|
||||||
static @NotNull LanguageGlyphCollection of(@NotNull Key fontKey,
|
|
||||||
@NotNull Texture texture,
|
|
||||||
@NotNull List<@NotNull TextureProperties> propertiesList,
|
|
||||||
@NotNull List<@NotNull String> charactersMapping) {
|
|
||||||
return new LanguageGlyphCollectionImpl(fontKey, texture, propertiesList, charactersMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
static @NotNull LanguageGlyphCollection of(@NotNull Texture texture,
|
|
||||||
@NotNull List<@NotNull TextureProperties> propertiesList,
|
|
||||||
@NotNull List<@NotNull String> charactersMapping) {
|
|
||||||
return of(Glyph.DEFAULT_FONT_KEY, texture, propertiesList, charactersMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull AppendableGlyph translate(int height, int ascent, @NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException;
|
|
||||||
|
|
||||||
@NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text, @Nullable TextColor color) throws IllegalArgumentException;
|
|
||||||
|
|
||||||
default @NotNull AppendableGlyph translate(int height, int ascent, @NotNull Character character) throws IllegalArgumentException {
|
|
||||||
return translate(height, ascent, character, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text) throws IllegalArgumentException {
|
|
||||||
return translate(height, ascent, text, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
|
||||||
import team.unnamed.creative.font.FontProvider;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class LanguageGlyphCollectionImpl implements LanguageGlyphCollection {
|
|
||||||
|
|
||||||
private final Key fontKey;
|
|
||||||
private final Texture texture;
|
|
||||||
|
|
||||||
private final Map<TextureProperties, MulticharacterImageGlyphCollection> propertiesToMulticharacterMap = new HashMap<>();
|
|
||||||
|
|
||||||
private Set<FontProvider> fontProviders;
|
|
||||||
|
|
||||||
LanguageGlyphCollectionImpl(
|
|
||||||
Key fontKey,
|
|
||||||
Texture texture,
|
|
||||||
List<TextureProperties> propertiesList,
|
|
||||||
List<String> charactersMapping
|
|
||||||
) {
|
|
||||||
this.fontKey = fontKey;
|
|
||||||
this.texture = texture;
|
|
||||||
|
|
||||||
for (TextureProperties properties : propertiesList) {
|
|
||||||
propertiesToMulticharacterMap.put(properties, MulticharacterImageGlyphCollection.of(fontKey, texture, properties, charactersMapping));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Key fontKey() {
|
|
||||||
return fontKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean produced() {
|
|
||||||
return fontProviders != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException {
|
|
||||||
if (fontProviders != null) {
|
|
||||||
throw new ResourceAlreadyProducedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
fontProviders = new HashSet<>();
|
|
||||||
|
|
||||||
for (var entry : propertiesToMulticharacterMap.entrySet()) {
|
|
||||||
entry.getValue().produceResources(characterFactory);
|
|
||||||
fontProviders.addAll(entry.getValue().fontProviders());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException {
|
|
||||||
return fontProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException {
|
|
||||||
return Collections.singleton(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull AppendableGlyph translate(int height, int ascent, @NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException {
|
|
||||||
TextureProperties properties = new TextureProperties(height, ascent);
|
|
||||||
if (!propertiesToMulticharacterMap.containsKey(properties)) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
return propertiesToMulticharacterMap.get(properties).translate(character, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull List<@NotNull AppendableGlyph> translate(int height, int ascent, @NotNull String text, @Nullable TextColor color) throws IllegalArgumentException {
|
|
||||||
TextureProperties properties = new TextureProperties(height, ascent);
|
|
||||||
if (!propertiesToMulticharacterMap.containsKey(properties)) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
return propertiesToMulticharacterMap.get(properties).translate(text, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyph;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface MulticharacterImageGlyphCollection extends ResourceProducer {
|
|
||||||
|
|
||||||
static @NotNull MulticharacterImageGlyphCollection of(@NotNull Key fontKey,
|
|
||||||
@NotNull Texture texture,
|
|
||||||
@NotNull TextureProperties properties,
|
|
||||||
@NotNull List<@NotNull String> charactersMapping) {
|
|
||||||
return new MulticharacterImageGlyphCollectionImpl(fontKey, texture, properties, charactersMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
static @NotNull MulticharacterImageGlyphCollection of(@NotNull Texture texture,
|
|
||||||
@NotNull TextureProperties properties,
|
|
||||||
@NotNull List<@NotNull String> charactersMapping) {
|
|
||||||
return of(Glyph.DEFAULT_FONT_KEY, texture, properties, charactersMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull AppendableGlyph translate(@NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException;
|
|
||||||
|
|
||||||
default @NotNull List<@NotNull AppendableGlyph> translate(@NotNull String text, @Nullable TextColor color) throws IllegalArgumentException {
|
|
||||||
List<AppendableGlyph> glyphs = new ArrayList<>();
|
|
||||||
for (char character : text.toCharArray()) {
|
|
||||||
if (character == ' ') {
|
|
||||||
glyphs.add(SpacesGlyph.DEFAULT_SPACE_GLYPH);
|
|
||||||
} else {
|
|
||||||
glyphs.add(translate(character, color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return glyphs;
|
|
||||||
}
|
|
||||||
|
|
||||||
default @NotNull AppendableGlyph translate(@NotNull Character character) throws IllegalArgumentException {
|
|
||||||
return translate(character, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @NotNull List<@NotNull AppendableGlyph> translate(@NotNull String text) throws IllegalArgumentException {
|
|
||||||
return translate(text, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,131 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.util.ImageUtil;
|
|
||||||
import team.unnamed.creative.font.FontProvider;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public class MulticharacterImageGlyphCollectionImpl implements MulticharacterImageGlyphCollection {
|
|
||||||
|
|
||||||
private final Key fontKey;
|
|
||||||
private final Texture texture;
|
|
||||||
private final TextureProperties properties;
|
|
||||||
private final List<String> charactersMapping;
|
|
||||||
|
|
||||||
private final Map<Character, Character> originToArbitraryCharacterMap = new HashMap<>();
|
|
||||||
|
|
||||||
private Set<FontProvider> fontProviders;
|
|
||||||
|
|
||||||
private BufferedImage image;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Key fontKey() {
|
|
||||||
return fontKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean produced() {
|
|
||||||
return fontProviders != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void produceResources(ArbitraryCharacterFactory characterFactory) throws ResourceAlreadyProducedException {
|
|
||||||
if (fontProviders != null) {
|
|
||||||
throw new ResourceAlreadyProducedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var fontProviderBuilder = FontProvider.bitMap();
|
|
||||||
fontProviderBuilder.file(texture.key());
|
|
||||||
fontProviderBuilder.ascent(properties.ascent());
|
|
||||||
fontProviderBuilder.height(properties.height());
|
|
||||||
|
|
||||||
List<String> mappingLines = new ArrayList<>();
|
|
||||||
|
|
||||||
for (String mappingLine : charactersMapping) {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (char character : mappingLine.toCharArray()) {
|
|
||||||
char arbitraryCharacter = characterFactory.nextCharacter();
|
|
||||||
originToArbitraryCharacterMap.put(character, arbitraryCharacter);
|
|
||||||
builder.append(arbitraryCharacter);
|
|
||||||
}
|
|
||||||
mappingLines.add(builder.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
fontProviderBuilder.characters(mappingLines);
|
|
||||||
|
|
||||||
this.fontProviders = Collections.singleton(fontProviderBuilder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException {
|
|
||||||
return fontProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException {
|
|
||||||
return Collections.singleton(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull PreparedImageGlyph translate(@NotNull Character character, @Nullable TextColor color) throws IllegalArgumentException {
|
|
||||||
if (!originToArbitraryCharacterMap.containsKey(character)) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = 0;
|
|
||||||
for (int lineIndex = 0; lineIndex < charactersMapping.size(); lineIndex++) {
|
|
||||||
String line = charactersMapping.get(lineIndex);
|
|
||||||
for (int characterIndex = 0; characterIndex < line.toCharArray().length; characterIndex++) {
|
|
||||||
if (line.charAt(characterIndex) == character) {
|
|
||||||
if (image == null) {
|
|
||||||
cacheImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (image == null) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
int filePartWidth = image.getWidth() / charactersMapping.get(0).length();
|
|
||||||
int filePartHeight = image.getHeight() / charactersMapping.size();
|
|
||||||
|
|
||||||
width = (int) Math.ceil(
|
|
||||||
((double) properties.height() / (double) filePartHeight)
|
|
||||||
* ImageUtil.calculateWidth(
|
|
||||||
image, filePartWidth * characterIndex, filePartHeight * lineIndex,
|
|
||||||
filePartWidth * (characterIndex + 1), filePartHeight * (lineIndex + 1)
|
|
||||||
)) + Glyph.SEPARATOR_WIDTH;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PreparedImageGlyph(fontKey, originToArbitraryCharacterMap.get(character), width, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cacheImage() {
|
|
||||||
try {
|
|
||||||
image = ImageIO.read(new ByteArrayInputStream(texture.data().toByteArray()));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
|
|
||||||
|
|
||||||
@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public class PreparedImageGlyph implements AppendableGlyph, ColorableGlyph {
|
|
||||||
|
|
||||||
private final Key key;
|
|
||||||
private final Character character;
|
|
||||||
private final int width;
|
|
||||||
private @Nullable TextColor color;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Component toAdventure() {
|
|
||||||
return Component.text(character)
|
|
||||||
.font(key)
|
|
||||||
.color(color == null ? NamedTextColor.BLACK : color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int width() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable TextColor color() {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateColor(@Nullable TextColor color) {
|
|
||||||
this.color = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.space;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.EmptyGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.util.ArrayUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public abstract class AbstractSpacesGlyphResourceProducer implements SpacesGlyphResourceProducer {
|
|
||||||
|
|
||||||
private final Key fontKey;
|
|
||||||
|
|
||||||
protected Map<Integer, Character> mapping;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Key fontKey() {
|
|
||||||
return fontKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean produced() {
|
|
||||||
return mapping != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Glyph translate(int length) throws ResourceNotProducedException {
|
|
||||||
if (mapping == null) {
|
|
||||||
throw new ResourceNotProducedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length == 0) {
|
|
||||||
return EmptyGlyph.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sign = length > 0 ? 1 : -1;
|
|
||||||
String binaryString = Integer.toBinaryString(Math.abs(length));
|
|
||||||
|
|
||||||
List<Character> characters = new ArrayList<>();
|
|
||||||
|
|
||||||
int currentRankLength = 1;
|
|
||||||
for (int index = 0; index < binaryString.length(); index++) {
|
|
||||||
char digit = binaryString.charAt(binaryString.length() - index - 1);
|
|
||||||
if (digit == '1') {
|
|
||||||
int partLength = currentRankLength * sign;
|
|
||||||
if (!mapping.containsKey(partLength)) {
|
|
||||||
throw new IllegalArgumentException("Too much length");
|
|
||||||
}
|
|
||||||
characters.add(mapping.get(partLength));
|
|
||||||
}
|
|
||||||
currentRankLength *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SpacesGlyph(fontKey(), ArrayUtil.toCharArray(characters), length);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.space;
|
|
||||||
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.AppendableGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import team.unnamed.creative.font.Font;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public class DefaultSpaceGlyph implements AppendableGlyph {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Component toAdventure() throws ResourceNotProducedException {
|
|
||||||
return Component.text(" ").font(Font.MINECRAFT_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int width() {
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.space;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import team.unnamed.creative.base.Writable;
|
|
||||||
import team.unnamed.creative.font.BitMapFontProvider;
|
|
||||||
import team.unnamed.creative.font.FontProvider;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class DefaultSpacesGlyphResourceProducer extends AbstractSpacesGlyphResourceProducer {
|
|
||||||
|
|
||||||
private final Key textureKey;
|
|
||||||
private final Writable writable;
|
|
||||||
|
|
||||||
private Set<Texture> textures;
|
|
||||||
private Set<FontProvider> fontProviders;
|
|
||||||
|
|
||||||
public DefaultSpacesGlyphResourceProducer(Key fontKey, Key textureKey, Writable writable) {
|
|
||||||
super(fontKey);
|
|
||||||
this.textureKey = textureKey;
|
|
||||||
this.writable = writable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean produced() {
|
|
||||||
return textures != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void produceResources(ArbitraryCharacterFactory characterFactory) {
|
|
||||||
if (textures != null) {
|
|
||||||
throw new ResourceAlreadyProducedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mapping = new HashMap<>();
|
|
||||||
|
|
||||||
Set<FontProvider> fontProviders = new HashSet<>();
|
|
||||||
|
|
||||||
for (int length = 1; length <= 2048; length *= 2) {
|
|
||||||
fontProviders.add(prepareBuilder(characterFactory, length).build());
|
|
||||||
fontProviders.add(prepareBuilder(characterFactory, length * (-1)).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.textures = Collections.singleton(Texture.texture(textureKey, writable));
|
|
||||||
this.fontProviders = fontProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException {
|
|
||||||
if (fontProviders == null) {
|
|
||||||
throw new ResourceNotProducedException();
|
|
||||||
}
|
|
||||||
return fontProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull Texture> textures() throws ResourceNotProducedException {
|
|
||||||
if (textures == null) {
|
|
||||||
throw new ResourceNotProducedException();
|
|
||||||
}
|
|
||||||
return textures;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private BitMapFontProvider.Builder prepareBuilder(ArbitraryCharacterFactory characterFactory, int length) {
|
|
||||||
var fontProviderBuilder = FontProvider.bitMap();
|
|
||||||
|
|
||||||
char character = characterFactory.nextCharacter();
|
|
||||||
|
|
||||||
fontProviderBuilder.characters(String.valueOf(character));
|
|
||||||
fontProviderBuilder.file(textureKey);
|
|
||||||
|
|
||||||
if (length > 0) {
|
|
||||||
fontProviderBuilder.height(length - 1);
|
|
||||||
fontProviderBuilder.ascent(0);
|
|
||||||
} else {
|
|
||||||
fontProviderBuilder.height(length - 2);
|
|
||||||
fontProviderBuilder.ascent(-32768);
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping.put(length, character);
|
|
||||||
|
|
||||||
return fontProviderBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.space;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import team.unnamed.creative.base.Writable;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
|
||||||
public class SpacesGlyph implements Glyph {
|
|
||||||
|
|
||||||
private static final Key DEFAULT_SPACE_TEXTURE_KEY = Key.key(Glyph.DEFAULT_NAMESPACE, "space");
|
|
||||||
public static final @NotNull DefaultSpaceGlyph DEFAULT_SPACE_GLYPH = new DefaultSpaceGlyph();
|
|
||||||
private final Key key;
|
|
||||||
private final char[] characters;
|
|
||||||
private final int length;
|
|
||||||
|
|
||||||
public static @NotNull SpacesGlyphResourceProducer create(@NotNull Key fontKey,
|
|
||||||
@NotNull Key textureKey,
|
|
||||||
@NotNull Writable spacesWritable) {
|
|
||||||
return new DefaultSpacesGlyphResourceProducer(fontKey, textureKey, spacesWritable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SpacesGlyphResourceProducer create(@NotNull Writable spacesWritable) {
|
|
||||||
return create(Glyph.DEFAULT_SPACES_FONT_KEY, DEFAULT_SPACE_TEXTURE_KEY, spacesWritable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Component toAdventure() {
|
|
||||||
return Component.text(new String(characters)).font(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int width() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.space;
|
|
||||||
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
|
|
||||||
public interface SpacesGlyphResourceProducer extends ResourceProducer {
|
|
||||||
|
|
||||||
Glyph translate(int length) throws ResourceNotProducedException;
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.space.mojang;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer;
|
|
||||||
|
|
||||||
public interface MojangSpacesGlyph extends Glyph, ResourceProducer {
|
|
||||||
|
|
||||||
static @NotNull SpacesGlyphResourceProducer create(@NotNull Key key) {
|
|
||||||
return new MojangSpacesGlyphResourceProducer(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SpacesGlyphResourceProducer create() {
|
|
||||||
return create(Glyph.DEFAULT_SPACES_FONT_KEY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.space.mojang;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ArbitraryCharacterFactory;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceAlreadyProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.exception.ResourceNotProducedException;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.AbstractSpacesGlyphResourceProducer;
|
|
||||||
import team.unnamed.creative.font.FontProvider;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class MojangSpacesGlyphResourceProducer extends AbstractSpacesGlyphResourceProducer {
|
|
||||||
|
|
||||||
private Set<FontProvider> fontProviders;
|
|
||||||
|
|
||||||
public MojangSpacesGlyphResourceProducer(Key key) {
|
|
||||||
super(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean produced() {
|
|
||||||
return fontProviders != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void produceResources(ArbitraryCharacterFactory characterFactory) {
|
|
||||||
if (fontProviders != null) {
|
|
||||||
throw new ResourceAlreadyProducedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping = new HashMap<>();
|
|
||||||
|
|
||||||
var fontProviderBuilder = FontProvider.space();
|
|
||||||
|
|
||||||
for (int length = 1; length <= 2048; length *= 2) {
|
|
||||||
fontProviderBuilder.advance(retrieveCharacter(characterFactory, length), length);
|
|
||||||
fontProviderBuilder.advance(retrieveCharacter(characterFactory, length * (-1)), length * (-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fontProviders = Collections.singleton(fontProviderBuilder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull FontProvider> fontProviders() throws ResourceNotProducedException {
|
|
||||||
if (fontProviders == null) {
|
|
||||||
throw new ResourceNotProducedException();
|
|
||||||
}
|
|
||||||
return fontProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Character retrieveCharacter(ArbitraryCharacterFactory characterFactory, int length) {
|
|
||||||
char character = characterFactory.nextCharacter();
|
|
||||||
mapping.put(length, character);
|
|
||||||
return character;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.pack;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import team.unnamed.creative.part.ResourcePackPart;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
|
||||||
public class DefaultGlyphResourcePack implements GlyphResourcePack {
|
|
||||||
|
|
||||||
private final Map<String, ResourceProducer> raw = new HashMap<>();
|
|
||||||
private final Map<String, ResourceProducer> compiled = new HashMap<>();
|
|
||||||
|
|
||||||
private final Set<ResourcePackPart> resources = new HashSet<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<@NotNull ResourcePackPart> all() {
|
|
||||||
if (!raw.isEmpty()) {
|
|
||||||
compileAll();
|
|
||||||
}
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compileAll() {
|
|
||||||
var resources = GlyphCompiler.instance().compile(raw.values());
|
|
||||||
this.resources.addAll(resources);
|
|
||||||
compiled.putAll(raw);
|
|
||||||
raw.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull <T extends ResourceProducer> GlyphResourcePack with(@NotNull ResourceIdentifier<@NotNull T> id, @NotNull T producer) {
|
|
||||||
if (raw.containsKey(id.key()) || compiled.containsKey(id.key())) {
|
|
||||||
throw new IllegalArgumentException("Producer with this identifier already registered");
|
|
||||||
}
|
|
||||||
raw.put(id.key(), producer);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull GlyphResourcePack with(@NotNull ResourcePackPart resource) {
|
|
||||||
resources.add(resource);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends ResourceProducer> @NotNull T get(@NotNull ResourceIdentifier<@NotNull T> id) throws IllegalArgumentException {
|
|
||||||
if (!compiled.containsKey(id.key())) {
|
|
||||||
throw new IllegalArgumentException("Producer with that identifier is not compiled. Provided key: " + id.key());
|
|
||||||
}
|
|
||||||
ResourceProducer producer = compiled.get(id.key());
|
|
||||||
if (!id.getType().isAssignableFrom(producer.getClass())) {
|
|
||||||
throw new IllegalArgumentException("Wrong producer type");
|
|
||||||
}
|
|
||||||
return (T) producer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.pack;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.mojang.MojangSpacesGlyph;
|
|
||||||
import team.unnamed.creative.ResourcePack;
|
|
||||||
import team.unnamed.creative.part.ResourcePackPart;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public interface GlyphResourcePack {
|
|
||||||
|
|
||||||
@NotNull Collection<@NotNull ResourcePackPart> all();
|
|
||||||
|
|
||||||
void compileAll();
|
|
||||||
|
|
||||||
@Contract("_, _ -> this")
|
|
||||||
<T extends ResourceProducer> @NotNull GlyphResourcePack with(@NotNull ResourceIdentifier<@NotNull T> id, @NotNull T producer);
|
|
||||||
|
|
||||||
@Contract("_ -> this")
|
|
||||||
@NotNull GlyphResourcePack with(@NotNull ResourcePackPart resource);
|
|
||||||
|
|
||||||
@Contract("_ -> this")
|
|
||||||
default @NotNull GlyphResourcePack with(@NotNull ResourcePackPart... resources) {
|
|
||||||
for (ResourcePackPart resource : resources) {
|
|
||||||
with(resource);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_ -> this")
|
|
||||||
default @NotNull GlyphResourcePack with(@NotNull Collection<@NotNull ResourcePackPart> resources) {
|
|
||||||
resources.forEach(this::with);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("-> this")
|
|
||||||
default @NotNull GlyphResourcePack withMojangSpaces() {
|
|
||||||
with(ResourceIdentifier.SPACES, MojangSpacesGlyph.create());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
<T extends ResourceProducer> @NotNull T get(@NotNull ResourceIdentifier<@NotNull T> id) throws IllegalArgumentException;
|
|
||||||
|
|
||||||
default @NotNull SpacesGlyphResourceProducer spaces() {
|
|
||||||
return get(ResourceIdentifier.SPACES);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void writeAll(@NotNull ResourcePack resourcePack) {
|
|
||||||
all().forEach(resourcePack::part);
|
|
||||||
}
|
|
||||||
|
|
||||||
static @NotNull GlyphResourcePack create() {
|
|
||||||
return new DefaultGlyphResourcePack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.pack;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
|
|
||||||
|
|
||||||
public interface ImageResourceIdentifier extends ResourceIdentifier<@NotNull ImageGlyph> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default @NotNull Class<@NotNull ImageGlyph> getType() {
|
|
||||||
return ImageGlyph.class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.pack;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.multicharacter.LanguageGlyphCollection;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.space.SpacesGlyphResourceProducer;
|
|
||||||
|
|
||||||
public interface ResourceIdentifier<T extends @NotNull ResourceProducer> {
|
|
||||||
|
|
||||||
@NotNull StringIdentifier<@NotNull SpacesGlyphResourceProducer> SPACES = StringIdentifier.of("spaces", SpacesGlyphResourceProducer.class);
|
|
||||||
|
|
||||||
@NotNull StringIdentifier<@NotNull LanguageGlyphCollection> MINECRAFT_FONT = StringIdentifier.of("minecraft_font", LanguageGlyphCollection.class);
|
|
||||||
|
|
||||||
String STRING_IDENTIFIER_NAMESPACE = "glyphs";
|
|
||||||
|
|
||||||
@NotNull String key();
|
|
||||||
|
|
||||||
@NotNull Class<T> getType();
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.pack;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.ResourceProducer;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
|
|
||||||
|
|
||||||
public class StringIdentifier<T extends @NotNull ResourceProducer> implements ResourceIdentifier<T> {
|
|
||||||
|
|
||||||
private final @NotNull String id;
|
|
||||||
|
|
||||||
private final @NotNull Class<T> type;
|
|
||||||
|
|
||||||
protected StringIdentifier(@NotNull String id, @NotNull Class<T> type) {
|
|
||||||
this.id = id;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Class<T> getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String key() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends @NotNull ResourceProducer> @NotNull StringIdentifier<@NotNull T> of(
|
|
||||||
@NotNull String id,
|
|
||||||
@NotNull Class<T> type) {
|
|
||||||
return new StringIdentifier<>(id, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull StringIdentifier<@NotNull ImageGlyph> image(@NotNull String id) {
|
|
||||||
return new StringIdentifier<>(id, ImageGlyph.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.util;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class ArrayUtil {
|
|
||||||
|
|
||||||
public char[] toCharArray(@NotNull List<Character> list) {
|
|
||||||
char[] arr = new char[list.size()];
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
|
||||||
arr[i] = list.get(i);
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.util;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class ImageUtil {
|
|
||||||
|
|
||||||
public int calculateWidth(BufferedImage image, int xFrom, int yFrom, int xTo, int yTo) {
|
|
||||||
int width;
|
|
||||||
for (width = xTo - 1; width > xFrom; width--) {
|
|
||||||
for (int height = yFrom; height < yTo; height++) {
|
|
||||||
if (new Color(image.getRGB(width, height), true)
|
|
||||||
.getAlpha() == 255) {
|
|
||||||
return width - xFrom + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return width - xFrom + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int calculateWidth(BufferedImage image) {
|
|
||||||
return calculateWidth(image, 0, 0, image.getWidth(), image.getHeight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,8 +3,7 @@ package ru.dragonestia.msb3.api.item;
|
|||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.item.Material;
|
import net.minestom.server.item.Material;
|
||||||
import ru.dragonestia.msb3.api.ServerBootstrap;
|
import ru.dragonestia.msb3.api.boot.ServerBootstrap;
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
||||||
import team.unnamed.creative.base.Writable;
|
import team.unnamed.creative.base.Writable;
|
||||||
import team.unnamed.creative.model.*;
|
import team.unnamed.creative.model.*;
|
||||||
@ -24,7 +23,7 @@ public class BlankSlotItem {
|
|||||||
public synchronized static ItemStack getItem() {
|
public synchronized static ItemStack getItem() {
|
||||||
if (stack == null) {
|
if (stack == null) {
|
||||||
stack = ItemStack.builder(Material.PAPER)
|
stack = ItemStack.builder(Material.PAPER)
|
||||||
.customModelData(1)
|
.itemModel("minecraft:air")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ public class BlankSlotItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Collection<ResourcePackPart> compile() {
|
public static Collection<ResourcePackPart> compile() {
|
||||||
var modelKey = Key.key(Glyph.DEFAULT_NAMESPACE, "blank_slot");
|
var modelKey = Key.key("msb3", "blank_slot");
|
||||||
var itemKey = Key.key("item/paper");
|
var itemKey = Key.key("item/paper");
|
||||||
|
|
||||||
Model blankSlotModel = Model.model()
|
Model blankSlotModel = Model.model()
|
||||||
@ -52,7 +51,7 @@ public class BlankSlotItem {
|
|||||||
.overrides(ItemOverride.of(modelKey, ItemPredicate.customModelData(1)))
|
.overrides(ItemOverride.of(modelKey, ItemPredicate.customModelData(1)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Texture texture = Texture.texture(Key.key(Glyph.DEFAULT_NAMESPACE, "blank_slot.png"), BLANK_SLOT_IMAGE_WRITABLE);
|
Texture texture = Texture.texture(Key.key("msb3", "blank_slot.png"), BLANK_SLOT_IMAGE_WRITABLE);
|
||||||
return Arrays.asList(blankSlotModel, paperItemModel, texture);
|
return Arrays.asList(blankSlotModel, paperItemModel, texture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
package ru.dragonestia.msb3.api.item;
|
package ru.dragonestia.msb3.api.item;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import ru.dragonestia.msb3.api.ServerBootstrap;
|
import ru.dragonestia.msb3.api.boot.ServerBootstrap;
|
||||||
import ru.dragonestia.msb3.api.item.prefab.ItemPrefab;
|
import ru.dragonestia.msb3.api.item.prefab.ItemPrefab;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -9,12 +10,11 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
|
@UtilityClass
|
||||||
public final class ItemPrefabManager {
|
public final class ItemPrefabManager {
|
||||||
|
|
||||||
private final Map<String, ItemPrefab> prefabs = new HashMap<>();
|
private final Map<String, ItemPrefab> prefabs = new HashMap<>();
|
||||||
|
|
||||||
public ItemPrefabManager() {}
|
|
||||||
|
|
||||||
public synchronized void register(ItemPrefab prefab) {
|
public synchronized void register(ItemPrefab prefab) {
|
||||||
if (ServerBootstrap.isStarted()) {
|
if (ServerBootstrap.isStarted()) {
|
||||||
throw new IllegalStateException("Server is already started");
|
throw new IllegalStateException("Server is already started");
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package ru.dragonestia.msb3.api.item.prefab;
|
|||||||
import net.kyori.adventure.nbt.BinaryTag;
|
import net.kyori.adventure.nbt.BinaryTag;
|
||||||
import net.minestom.server.item.ItemStack;
|
import net.minestom.server.item.ItemStack;
|
||||||
import net.minestom.server.tag.Tag;
|
import net.minestom.server.tag.Tag;
|
||||||
import ru.dragonestia.msb3.api.ServerBootstrap;
|
import ru.dragonestia.msb3.api.item.ItemPrefabManager;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -22,12 +22,12 @@ public class ItemPrefab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<ItemPrefab> of(String identifier) {
|
public static Optional<ItemPrefab> of(String identifier) {
|
||||||
return ServerBootstrap.getInstance().getItemPrefabManager().getPrefab(identifier);
|
return ItemPrefabManager.getPrefab(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<ItemPrefab> of(ItemStack item) {
|
public static Optional<ItemPrefab> of(ItemStack item) {
|
||||||
if (item == null || item.hasTag(TAG_IDENTIFIER)) return Optional.empty();
|
if (item == null || item.hasTag(TAG_IDENTIFIER)) return Optional.empty();
|
||||||
return ServerBootstrap.getInstance().getItemPrefabManager().getPrefab(item.getTag(TAG_IDENTIFIER));
|
return ItemPrefabManager.getPrefab(item.getTag(TAG_IDENTIFIER));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String getIdentifier() {
|
public final String getIdentifier() {
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
package ru.dragonestia.msb3.api.module;
|
package ru.dragonestia.msb3.api.module;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.coordinate.Pos;
|
import net.minestom.server.coordinate.Pos;
|
||||||
import net.minestom.server.entity.GameMode;
|
import net.minestom.server.entity.GameMode;
|
||||||
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
|
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
|
||||||
import net.minestom.server.instance.Chunk;
|
import net.minestom.server.instance.*;
|
||||||
import net.minestom.server.instance.DynamicChunk;
|
|
||||||
import net.minestom.server.instance.IChunkLoader;
|
|
||||||
import net.minestom.server.instance.Instance;
|
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.world.DimensionType;
|
import net.minestom.server.world.DimensionType;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import ru.dragonestia.msb3.api.world.World;
|
||||||
import ru.dragonestia.msb3.api.world.WorldFactory;
|
import ru.dragonestia.msb3.api.world.WorldFactory;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@ -19,21 +18,19 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
public class FlatWorldModule {
|
public class FlatWorldModule {
|
||||||
|
|
||||||
private static boolean used = false;
|
private static boolean used = false;
|
||||||
|
@Getter private static World world;
|
||||||
|
|
||||||
private FlatWorldModule() {}
|
private FlatWorldModule() {}
|
||||||
|
|
||||||
public static synchronized void init(GameMode gameMode) {
|
public static void init(GameMode gameMode) {
|
||||||
if (used) return;
|
|
||||||
used = true;
|
|
||||||
|
|
||||||
var dimension = MinecraftServer.getDimensionTypeRegistry().register("msb3:full_bright", DimensionType.builder()
|
var dimension = MinecraftServer.getDimensionTypeRegistry().register("msb3:full_bright", DimensionType.builder()
|
||||||
.ambientLight(2f)
|
.ambientLight(2f)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
var factory = WorldFactory.custom(dimension, new IChunkLoader() {
|
var factory = WorldFactory.custom(dimension, new IChunkLoader() {
|
||||||
@Override
|
@Override
|
||||||
public @NotNull CompletableFuture<@Nullable Chunk> loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
|
public @NotNull Chunk loadChunk(@NotNull Instance instance, int chunkX, int chunkZ) {
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
|
||||||
var chunk = new DynamicChunk(instance, chunkX, chunkZ);
|
var chunk = new DynamicChunk(instance, chunkX, chunkZ);
|
||||||
for (int x = 0; x < 16; x++) {
|
for (int x = 0; x < 16; x++) {
|
||||||
for (int z = 0; z < 16; z++) {
|
for (int z = 0; z < 16; z++) {
|
||||||
@ -49,21 +46,25 @@ public class FlatWorldModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chunk;
|
return chunk;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull CompletableFuture<Void> saveChunk(@NotNull Chunk chunk) {
|
public void saveChunk(@NotNull Chunk chunk) {}
|
||||||
return new CompletableFuture<>();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var world = factory.createWorldSync();
|
init(gameMode, factory.createWorldSync(), new Pos(0, 11, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void init(GameMode gameMode, World world, Pos spawnPos) {
|
||||||
|
if (used) return;
|
||||||
|
used = true;
|
||||||
|
|
||||||
|
FlatWorldModule.world = world;
|
||||||
|
|
||||||
MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> {
|
MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> {
|
||||||
var player = event.getPlayer();
|
var player = event.getPlayer();
|
||||||
|
|
||||||
player.setRespawnPoint(new Pos(0, 11, 0));
|
player.setRespawnPoint(spawnPos);
|
||||||
player.setGameMode(gameMode);
|
player.setGameMode(gameMode);
|
||||||
|
|
||||||
event.setSpawningInstance(world.getInstance());
|
event.setSpawningInstance(world.getInstance());
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
package ru.dragonestia.msb3.api.module;
|
package ru.dragonestia.msb3.api.module;
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
import net.kyori.adventure.resource.ResourcePackCallback;
|
import net.kyori.adventure.resource.ResourcePackCallback;
|
||||||
import net.kyori.adventure.resource.ResourcePackInfo;
|
import net.kyori.adventure.resource.ResourcePackInfo;
|
||||||
import net.kyori.adventure.resource.ResourcePackRequest;
|
import net.kyori.adventure.resource.ResourcePackRequest;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.event.player.PlayerSpawnEvent;
|
import net.minestom.server.event.player.PlayerSpawnEvent;
|
||||||
import ru.dragonestia.msb3.api.ServerBootstrap;
|
|
||||||
import team.unnamed.creative.BuiltResourcePack;
|
import team.unnamed.creative.BuiltResourcePack;
|
||||||
import team.unnamed.creative.base.Writable;
|
import team.unnamed.creative.base.Writable;
|
||||||
import team.unnamed.creative.server.ResourcePackServer;
|
import team.unnamed.creative.server.ResourcePackServer;
|
||||||
@ -22,19 +22,16 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
public class ResourcePackRepositoryModule {
|
public class ResourcePackRepositoryModule {
|
||||||
|
|
||||||
private static boolean used = false;
|
private boolean used = false;
|
||||||
|
|
||||||
private ResourcePackRepositoryModule() {}
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static synchronized void init(ServerBootstrap bootstrap, String address, int port) {
|
public synchronized void init(String address, int port) {
|
||||||
if (used) return;
|
if (used) return;
|
||||||
used = true;
|
used = true;
|
||||||
|
|
||||||
bootstrap.getResourcePackManager().compile();
|
|
||||||
|
|
||||||
var file = new File("./resource-pack.zip");
|
var file = new File("./resource-pack.zip");
|
||||||
var hash = calculateHash(file);
|
var hash = calculateHash(file);
|
||||||
var uuid = UUID.randomUUID();
|
var uuid = UUID.randomUUID();
|
||||||
@ -46,6 +43,8 @@ public class ResourcePackRepositoryModule {
|
|||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
MinecraftServer.getGlobalEventHandler().addListener(PlayerSpawnEvent.class, event -> {
|
MinecraftServer.getGlobalEventHandler().addListener(PlayerSpawnEvent.class, event -> {
|
||||||
|
if (!event.isFirstSpawn()) return;
|
||||||
|
|
||||||
var player = event.getPlayer();
|
var player = event.getPlayer();
|
||||||
|
|
||||||
player.sendResourcePacks(ResourcePackRequest.resourcePackRequest()
|
player.sendResourcePacks(ResourcePackRequest.resourcePackRequest()
|
||||||
@ -59,7 +58,7 @@ public class ResourcePackRepositoryModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private static String calculateHash(File file) {
|
private String calculateHash(File file) {
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
|
|
||||||
try (InputStream fis = new FileInputStream(file)) {
|
try (InputStream fis = new FileInputStream(file)) {
|
||||||
@ -68,7 +67,7 @@ public class ResourcePackRepositoryModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String byteArray2Hex(final byte[] hash) {
|
private String byteArray2Hex(final byte[] hash) {
|
||||||
Formatter formatter = new Formatter();
|
Formatter formatter = new Formatter();
|
||||||
for (byte b: hash) formatter.format("%02x", b);
|
for (byte b: hash) formatter.format("%02x", b);
|
||||||
return formatter.toString();
|
return formatter.toString();
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
package ru.dragonestia.msb3.api.player;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.network.player.GameProfile;
|
||||||
|
import net.minestom.server.network.player.PlayerConnection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import ru.dragonestia.msb3.api.util.UncheckedRunnable;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MsbPlayer extends Player {
|
||||||
|
|
||||||
|
private final Map<String, PlayerContext> contexts = new LinkedHashMap<>();
|
||||||
|
@Getter private final Instant startSessionTime = Instant.now();
|
||||||
|
|
||||||
|
public MsbPlayer(@NotNull PlayerConnection playerConnection, @NotNull GameProfile gameProfile) {
|
||||||
|
super(playerConnection, gameProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void putContext(PlayerContext ctx) {
|
||||||
|
contexts.put(ctx.getClass().getName(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends PlayerContext> T getContext(Class<T> clazz) {
|
||||||
|
return (T) contexts.get(clazz.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initContexts() {
|
||||||
|
for (var entry: contexts.entrySet()) {
|
||||||
|
var ctxKey = entry.getKey();
|
||||||
|
var ctx = entry.getValue();
|
||||||
|
|
||||||
|
UncheckedRunnable.runIgnoreException(ctx::init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void emitSpawnSignalForContexts() {
|
||||||
|
for (var entry: contexts.entrySet()) {
|
||||||
|
var ctxKey = entry.getKey();
|
||||||
|
var ctx = entry.getValue();
|
||||||
|
|
||||||
|
UncheckedRunnable.runIgnoreException(ctx::firstSpawn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disposeContexts() {
|
||||||
|
for (var entry: contexts.entrySet()) {
|
||||||
|
var ctxKey = entry.getKey();
|
||||||
|
var ctx = entry.getValue();
|
||||||
|
|
||||||
|
UncheckedRunnable.runIgnoreException(ctx::dispose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package ru.dragonestia.msb3.api.player;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class PlayerContext {
|
||||||
|
|
||||||
|
private final MsbPlayer player;
|
||||||
|
|
||||||
|
public PlayerContext(MsbPlayer player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void init();
|
||||||
|
|
||||||
|
public void firstSpawn() {}
|
||||||
|
|
||||||
|
public abstract void dispose();
|
||||||
|
|
||||||
|
public static <T extends PlayerContext> T of(Player player, Class<T> clazz) {
|
||||||
|
return ((MsbPlayer) player).getContext(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package ru.dragonestia.msb3.api.player;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.network.player.GameProfile;
|
||||||
|
import net.minestom.server.network.player.PlayerConnection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@UtilityClass
|
||||||
|
public class PlayerContextManager {
|
||||||
|
|
||||||
|
private final Map<String, Function<MsbPlayer, ? extends PlayerContext>> contexts = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public MsbPlayer create(@NotNull PlayerConnection playerConnection, @NotNull GameProfile gameProfile) {
|
||||||
|
var player = new MsbPlayer(playerConnection, gameProfile);
|
||||||
|
|
||||||
|
for (var entry: contexts.entrySet()) {
|
||||||
|
var ctxKey = entry.getKey();
|
||||||
|
var ctx = entry.getValue().apply(player);
|
||||||
|
|
||||||
|
player.putContext(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerContext(Class<? extends PlayerContext> clazz, Function<MsbPlayer, ? extends PlayerContext> ctxProvider) {
|
||||||
|
var key = clazz.getName();
|
||||||
|
var prev = contexts.put(key, ctxProvider);
|
||||||
|
|
||||||
|
if (prev != null) {
|
||||||
|
log.warn("Duplicate context registration for {}", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends PlayerContext> T getContext(Player player, Class<T> clazz) {
|
||||||
|
return ((MsbPlayer) player).getContext(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package ru.dragonestia.msb3.api.player.defaults;
|
||||||
|
|
||||||
|
import net.kyori.adventure.bossbar.BossBar;
|
||||||
|
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
||||||
|
import ru.dragonestia.msb3.api.player.PlayerContext;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class KeyedBossBarContext extends PlayerContext {
|
||||||
|
|
||||||
|
private final Map<String, BossBar> bossBars = new HashMap<>();
|
||||||
|
|
||||||
|
public KeyedBossBarContext(MsbPlayer player) {
|
||||||
|
super(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
hideAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideAll() {
|
||||||
|
bossBars.forEach((barId, bossBar) -> getPlayer().hideBossBar(bossBar));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hide(String bossBarId) {
|
||||||
|
var bossBar = bossBars.remove(bossBarId);
|
||||||
|
if (bossBar == null) return;
|
||||||
|
|
||||||
|
getPlayer().hideBossBar(bossBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossBar show(String bossBarId, BossBar bossBar) {
|
||||||
|
var prev = bossBars.put(bossBarId, bossBar);
|
||||||
|
if (prev != null) getPlayer().hideBossBar(prev);
|
||||||
|
getPlayer().showBossBar(bossBar);
|
||||||
|
return bossBar;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
package ru.dragonestia.msb3.api.player.defaults;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.kyori.adventure.bossbar.BossBar;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import net.minestom.server.coordinate.Point;
|
||||||
|
import net.minestom.server.coordinate.Vec;
|
||||||
|
import net.minestom.server.event.player.PlayerMoveEvent;
|
||||||
|
import ru.dragonestia.msb3.api.player.MsbPlayer;
|
||||||
|
import ru.dragonestia.msb3.api.player.PlayerContext;
|
||||||
|
import ru.dragonestia.msb3.api.ui.bossbar.KeyedBossBars;
|
||||||
|
import ru.dragonestia.msb3.api.ui.navigator.Navigator;
|
||||||
|
import ru.dragonestia.msb3.api.ui.navigator.Navigators;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.MinecraftFont;
|
||||||
|
|
||||||
|
public class NavigatorContext extends PlayerContext implements Navigator {
|
||||||
|
|
||||||
|
private final static Component EMPTY = Component.text(" ");
|
||||||
|
|
||||||
|
private BossBar bossBar;
|
||||||
|
private Point target;
|
||||||
|
private String text;
|
||||||
|
private MinecraftFont.Text cachedText;
|
||||||
|
private boolean prevTargetEmpty = true;
|
||||||
|
private boolean enabled = true;
|
||||||
|
@Setter private String format = "[ %s ]";
|
||||||
|
@Setter private TextColor textColor = TextColor.color(0xF8FF91);
|
||||||
|
@Setter private GlyphImage arrowBackground = Navigators.GLYPH_BACKGROUND;
|
||||||
|
|
||||||
|
public NavigatorContext(MsbPlayer player) {
|
||||||
|
super(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firstSpawn() {
|
||||||
|
getPlayer().eventNode().addListener(PlayerMoveEvent.class, event -> update());
|
||||||
|
|
||||||
|
bossBar = KeyedBossBars.showText(getPlayer(), "msb3_navigator", EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
target = null;
|
||||||
|
text = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTarget(Point point) {
|
||||||
|
setTarget(point, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void setTarget(Point target, String text) {
|
||||||
|
prevTargetEmpty = this.target == null;
|
||||||
|
this.target = target;
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeTarget() {
|
||||||
|
setTarget(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
if (!enabled || (prevTargetEmpty && target == null)) return;
|
||||||
|
|
||||||
|
if (bossBar != null) bossBar.name(render());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component render() {
|
||||||
|
if (target == null) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new GlyphComponentBuilder();
|
||||||
|
|
||||||
|
builder.append((int) (arrowBackground.width() / -2.15) + 1, arrowBackground);
|
||||||
|
|
||||||
|
// Text
|
||||||
|
if (text != null) {
|
||||||
|
builder.setColorTo(textColor);
|
||||||
|
if (cachedText == null) {
|
||||||
|
cachedText = MinecraftFont.translateByLineNumber(0, format.formatted(text));
|
||||||
|
}
|
||||||
|
builder.append((int) (cachedText.width() / -2.15), cachedText);
|
||||||
|
builder.resetColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrow
|
||||||
|
builder.append(0, getArrowGlyph());
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlyphImage getArrowGlyph() {
|
||||||
|
var playerPos = getPlayer().getPosition();
|
||||||
|
var angle = computeAngle(playerPos.asVec(), playerPos.direction(), Vec.fromPoint(target));
|
||||||
|
|
||||||
|
if (!(angle < -2.749) && !(angle >= 2.749)) {
|
||||||
|
if (angle < -1.963) return Navigators.GLYPH_DOWN_RIGHT;
|
||||||
|
if (angle < -1.178) return Navigators.GLYPH_RIGHT;
|
||||||
|
if (angle < -0.393) return Navigators.GLYPH_UP_RIGHT;
|
||||||
|
if (angle < 0.393) return Navigators.GLYPH_UP;
|
||||||
|
if (angle < 1.178) return Navigators.GLYPH_UP_LEFT;
|
||||||
|
if (angle < 1.963) return Navigators.GLYPH_LEFT;
|
||||||
|
if (angle < 2.749) return Navigators.GLYPH_DOWN_LEFT;
|
||||||
|
}
|
||||||
|
return Navigators.GLYPH_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double computeAngle(Vec start, Vec direction, Vec destination) {
|
||||||
|
Vec destinationDirection = destination.sub(start);
|
||||||
|
double playerAngle = Math.atan2(direction.z(), direction.x());
|
||||||
|
double locAngle = Math.atan2(destinationDirection.z(), destinationDirection.x());
|
||||||
|
|
||||||
|
double angle;
|
||||||
|
for (angle = playerAngle - locAngle; angle > Math.PI; angle -= (Math.PI * 2D)) {}
|
||||||
|
|
||||||
|
while (angle < -Math.PI) {
|
||||||
|
angle += (Math.PI * 2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package ru.dragonestia.msb3.api.player.defaults;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
@Setter private boolean ignoreDialogConditions = false;
|
||||||
|
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
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,272 @@
|
|||||||
|
package ru.dragonestia.msb3.api.resource;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.*;
|
||||||
|
import ru.dragonestia.msb3.resource.Resources;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
import ru.dragonestia.msb3.resource.utils.ImageUtils;
|
||||||
|
import team.unnamed.creative.base.Writable;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class DialogueResources {
|
||||||
|
|
||||||
|
public static final String DEFAULT = "default";
|
||||||
|
|
||||||
|
private final Map<String, GlyphImage> avatars = new HashMap<>();
|
||||||
|
private final Map<String, GlyphImage> avatarFrames = new HashMap<>();
|
||||||
|
private final Map<String, GlyphImage> scrollUp = new HashMap<>();
|
||||||
|
private final Map<String, GlyphImage> scrollDown = new HashMap<>();
|
||||||
|
private final Map<String, Background> backgrounds = new HashMap<>();
|
||||||
|
private final Map<String, Substrate> substrates = new HashMap<>();
|
||||||
|
private final Map<String, Button> buttons = new HashMap<>();
|
||||||
|
private final Map<String, TextField> activeFields = new HashMap<>();
|
||||||
|
private final Map<String, TextField> notActiveFields = new HashMap<>();
|
||||||
|
|
||||||
|
public void registerAvatar(String identifier, Writable writable) {
|
||||||
|
avatars.put(identifier, Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/avatar/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.avatar.height(),
|
||||||
|
GlyphPositions.avatar.y()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerAvatarFrame(String identifier, Writable writable) {
|
||||||
|
avatarFrames.put(identifier, Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/avatar_frame/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.avatar.frameHeight(),
|
||||||
|
GlyphPositions.avatar.y() + GlyphPositions.avatar.frameBorderSize()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerScrollUp(String identifier, Writable writable) {
|
||||||
|
scrollUp.put(identifier, Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/scroll_up/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.scrollPhraseButton.height(),
|
||||||
|
GlyphPositions.scrollPhraseButton.buttonY()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerScrollDown(String identifier, Writable writable) {
|
||||||
|
scrollDown.put(identifier, Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/scroll_down/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.scrollPhraseButton.height(),
|
||||||
|
GlyphPositions.scrollPhraseButton.buttonY()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerBackground(String identifier, Writable writable) {
|
||||||
|
BufferedImage image;
|
||||||
|
Writable part1;
|
||||||
|
Writable part2;
|
||||||
|
Writable part3;
|
||||||
|
Writable part4;
|
||||||
|
|
||||||
|
try (var steam = new ByteArrayInputStream(writable.toByteArray())) {
|
||||||
|
image = ImageIO.read(steam);
|
||||||
|
var w = image.getWidth();
|
||||||
|
var h = image.getHeight();
|
||||||
|
|
||||||
|
part1 = ImageUtils.imageToWritable(image.getSubimage(0, 0, w / 2, h / 2));
|
||||||
|
part2 = ImageUtils.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h / 2));
|
||||||
|
part3 = ImageUtils.imageToWritable(image.getSubimage(0, h / 2, w / 2, h / 2));
|
||||||
|
part4 = ImageUtils.imageToWritable(image.getSubimage(w / 2, h / 2, w / 2, h / 2));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var glyph1 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/background/" + identifier + "__1"),
|
||||||
|
part1,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.topPartsY()
|
||||||
|
);
|
||||||
|
var glyph2 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/background/" + identifier + "__2"),
|
||||||
|
part2,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.topPartsY()
|
||||||
|
);
|
||||||
|
var glyph3 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/background/" + identifier + "__3"),
|
||||||
|
part3,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.bottomPartsY()
|
||||||
|
);
|
||||||
|
var glyph4 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/background/" + identifier + "__4"),
|
||||||
|
part4,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.bottomPartsY()
|
||||||
|
);
|
||||||
|
|
||||||
|
backgrounds.put(identifier, new Background(glyph1, glyph2, glyph3, glyph4));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerSubstrate(String identifier, Writable writable) {
|
||||||
|
BufferedImage image;
|
||||||
|
Writable part1;
|
||||||
|
Writable part2;
|
||||||
|
|
||||||
|
try (var steam = new ByteArrayInputStream(writable.toByteArray())) {
|
||||||
|
image = ImageIO.read(steam);
|
||||||
|
var w = image.getWidth();
|
||||||
|
var h = image.getHeight();
|
||||||
|
|
||||||
|
part1 = ImageUtils.imageToWritable(image.getSubimage(0, 0, w / 2, h));
|
||||||
|
part2 = ImageUtils.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var glyph1 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/substrate/" + identifier + "__1"),
|
||||||
|
part1,
|
||||||
|
GlyphPositions.phraseSubstrate.height(),
|
||||||
|
GlyphPositions.phraseSubstrate.y()
|
||||||
|
);
|
||||||
|
var glyph2 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/substrate/" + identifier + "__2"),
|
||||||
|
part2,
|
||||||
|
GlyphPositions.phraseSubstrate.height(),
|
||||||
|
GlyphPositions.phraseSubstrate.y()
|
||||||
|
);
|
||||||
|
|
||||||
|
substrates.put(identifier, new Substrate(glyph1, glyph2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerButton(String identifier, Writable writable) {
|
||||||
|
var glyph1 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/button/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerButton.height(),
|
||||||
|
GlyphPositions.answerButton.topButtonY()
|
||||||
|
);
|
||||||
|
var glyph2 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/button/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerButton.height(),
|
||||||
|
GlyphPositions.answerButton.topButtonY()
|
||||||
|
);
|
||||||
|
var glyph3 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/button/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerButton.height(),
|
||||||
|
GlyphPositions.answerButton.bottomButtonY()
|
||||||
|
);
|
||||||
|
var glyph4 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/button/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerButton.height(),
|
||||||
|
GlyphPositions.answerButton.bottomButtonY()
|
||||||
|
);
|
||||||
|
|
||||||
|
buttons.put(identifier, new Button(glyph1, glyph2, glyph3, glyph4));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerActiveTextField(String identifier, Writable writable) {
|
||||||
|
var glyph1 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.topFieldY()
|
||||||
|
);
|
||||||
|
var glyph2 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.topFieldY()
|
||||||
|
);
|
||||||
|
var glyph3 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.bottomFieldY()
|
||||||
|
);
|
||||||
|
var glyph4 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.bottomFieldY()
|
||||||
|
);
|
||||||
|
|
||||||
|
activeFields.put(identifier, new TextField(glyph1, glyph2, glyph3, glyph4));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerNotActiveTextField(String identifier, Writable writable) {
|
||||||
|
var glyph1 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.topFieldY()
|
||||||
|
);
|
||||||
|
var glyph2 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.topFieldY()
|
||||||
|
);
|
||||||
|
var glyph3 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.bottomFieldY()
|
||||||
|
);
|
||||||
|
var glyph4 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "dialogue/inactive_text_field/" + identifier),
|
||||||
|
writable,
|
||||||
|
GlyphPositions.answerField.height(),
|
||||||
|
GlyphPositions.answerField.bottomFieldY()
|
||||||
|
);
|
||||||
|
|
||||||
|
notActiveFields.put(identifier, new TextField(glyph1, glyph2, glyph3, glyph4));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<GlyphImage> getAvatar(String identifier) {
|
||||||
|
return Optional.ofNullable(avatars.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<GlyphImage> getAvatarFrame(String identifier) {
|
||||||
|
return Optional.ofNullable(avatarFrames.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<GlyphImage> getScrollUp(String identifier) {
|
||||||
|
return Optional.ofNullable(scrollUp.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<GlyphImage> getScrollDown(String identifier) {
|
||||||
|
return Optional.ofNullable(scrollDown.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Background> getBackground(String identifier) {
|
||||||
|
return Optional.ofNullable(backgrounds.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Substrate> getSubstrate(String identifier) {
|
||||||
|
return Optional.ofNullable(substrates.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Button> getButton(String identifier) {
|
||||||
|
return Optional.ofNullable(buttons.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<TextField> getActiveTextField(String identifier) {
|
||||||
|
return Optional.ofNullable(activeFields.get(identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<TextField> getNotActiveTextField(String identifier) {
|
||||||
|
return Optional.ofNullable(notActiveFields.get(identifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,101 +1,58 @@
|
|||||||
package ru.dragonestia.msb3.api.resource;
|
package ru.dragonestia.msb3.api.resource;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import ru.dragonestia.msb3.api.ServerBootstrap;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.pack.ResourceIdentifier;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.pack.StringIdentifier;
|
|
||||||
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
||||||
|
import ru.dragonestia.msb3.resource.Resources;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
import team.unnamed.creative.base.Writable;
|
import team.unnamed.creative.base.Writable;
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
public class MonologueResources {
|
public class MonologueResources {
|
||||||
|
|
||||||
private static final Key MONOLOG_FONT_KEY = Key.key("msb3", "monolog");
|
|
||||||
public static final String DEFAULT = "default";
|
public static final String DEFAULT = "default";
|
||||||
|
|
||||||
private final GlyphResourcePack glyphResourcePack;
|
private final Map<String, GlyphImage> avatars = new HashMap<>();
|
||||||
private final Map<String, GlyphEntry> avatars = new HashMap<>();
|
private final Map<String, GlyphImage> frames = new HashMap<>();
|
||||||
private final Map<String, GlyphEntry> frames = new HashMap<>();
|
@Getter private static final GlyphImage speechIndicator = Resources.createGlyph(
|
||||||
private final GlyphEntry speechIndicator = initSpeechIndicator();
|
Key.key("msb3", "monologue/speech_indicator"),
|
||||||
|
ResourceFromJar.of("glyphs/monologue/speech_indicator.png"),
|
||||||
public MonologueResources(GlyphResourcePack glyphResourcePack) {
|
8,
|
||||||
this.glyphResourcePack = glyphResourcePack;
|
6
|
||||||
|
);
|
||||||
registerAvatar(DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/default_avatar.png"));
|
|
||||||
registerFrame(DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/avatar_frame.png"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private GlyphEntry initSpeechIndicator() {
|
|
||||||
var writable = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/speech_indicator.png");
|
|
||||||
var glyph = ImageGlyph.of(MONOLOG_FONT_KEY,
|
|
||||||
Texture.texture().key(Key.key("msb3", "monolog/speech_indicator.png")).data(writable).build(),
|
|
||||||
new TextureProperties(8, 6));
|
|
||||||
var glyphIdentifier = StringIdentifier.image("monolog_speech_indicator");
|
|
||||||
|
|
||||||
return new GlyphEntry(glyphIdentifier, glyph);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerAvatar(String identifier, Writable writable) {
|
public void registerAvatar(String identifier, Writable writable) {
|
||||||
var glyph = ImageGlyph.of(MONOLOG_FONT_KEY,
|
var glyph = Resources.createGlyph(
|
||||||
Texture.texture().key(Key.key("msb3", "monolog/avatar_" + identifier + ".png")).data(writable).build(),
|
Key.key("msb3", "monologue/avatar/" + identifier),
|
||||||
new TextureProperties(42, 2));
|
writable,
|
||||||
|
42,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
var glyphIdentifier = StringIdentifier.image("monolog_avatar_" + identifier);
|
avatars.put(identifier, glyph);
|
||||||
|
|
||||||
avatars.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
|
|
||||||
|
|
||||||
glyphResourcePack.with(glyphIdentifier, glyph);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerFrame(String identifier, Writable writable) {
|
public void registerFrame(String identifier, Writable writable) {
|
||||||
var glyph = ImageGlyph.of(MONOLOG_FONT_KEY,
|
var glyph = Resources.createGlyph(
|
||||||
Texture.texture().key(Key.key("msb3", "monolog/frame_" + identifier + ".png")).data(writable).build(),
|
Key.key("msb3", "monologue/frame/" + identifier),
|
||||||
new TextureProperties(50, 6));
|
writable,
|
||||||
|
50,
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
var glyphIdentifier = StringIdentifier.image("monolog_frame_" + identifier);
|
frames.put(identifier, glyph);
|
||||||
|
|
||||||
frames.put(identifier, new GlyphEntry(glyphIdentifier, glyph));
|
|
||||||
|
|
||||||
glyphResourcePack.with(glyphIdentifier, glyph);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ImageGlyph> getAvatar(String identifier) {
|
public Optional<GlyphImage> getAvatar(String identifier) {
|
||||||
return Optional.ofNullable(avatars.get(identifier))
|
return Optional.ofNullable(avatars.get(identifier));
|
||||||
.map(entry -> {
|
|
||||||
try {
|
|
||||||
return glyphResourcePack.get(entry.identifier());
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ImageGlyph> getFrame(String identifier) {
|
public Optional<GlyphImage> getFrame(String identifier) {
|
||||||
return Optional.ofNullable(frames.get(identifier))
|
return Optional.ofNullable(frames.get(identifier));
|
||||||
.map(entry -> {
|
|
||||||
try {
|
|
||||||
return glyphResourcePack.get(entry.identifier());
|
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageGlyph getSpeechIndicator() {
|
|
||||||
return glyphResourcePack.get(speechIndicator.identifier());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void compile(GlyphResourcePack resourcePack) {
|
|
||||||
resourcePack.with(speechIndicator.identifier(), speechIndicator.glyph());
|
|
||||||
}
|
|
||||||
|
|
||||||
private record GlyphEntry(ResourceIdentifier<@NotNull ImageGlyph> identifier, ImageGlyph glyph) {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,84 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.resource;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.compile.GlyphCompiler;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.font.GlyphFont;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.pack.GlyphResourcePack;
|
|
||||||
import ru.dragonestia.msb3.api.item.BlankSlotItem;
|
|
||||||
import ru.dragonestia.msb3.api.title.BlackScreen;
|
|
||||||
import team.unnamed.creative.ResourcePack;
|
|
||||||
import team.unnamed.creative.atlas.Atlas;
|
|
||||||
import team.unnamed.creative.atlas.AtlasSource;
|
|
||||||
import team.unnamed.creative.base.Writable;
|
|
||||||
import team.unnamed.creative.model.Model;
|
|
||||||
import team.unnamed.creative.serialize.minecraft.MinecraftResourcePackWriter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Log4j2
|
|
||||||
public class ResourcePackManager {
|
|
||||||
|
|
||||||
private final ResourcePack resourcePack;
|
|
||||||
private final GlyphResourcePack glyphResourcePack;
|
|
||||||
private final MonologueResources monologueResources;
|
|
||||||
|
|
||||||
public ResourcePackManager() {
|
|
||||||
resourcePack = ResourcePack.resourcePack();
|
|
||||||
resourcePack.packMeta(34, "Dragonestia MSB3 - Resource pack");
|
|
||||||
resourcePack.icon(Writable.resource(getClass().getClassLoader(), "logo.png"));
|
|
||||||
resourcePack.unknownFile("credits.txt", Writable.stringUtf8("dragonestia.ru"));
|
|
||||||
|
|
||||||
glyphResourcePack = GlyphResourcePack.create();
|
|
||||||
monologueResources = new MonologueResources(glyphResourcePack);
|
|
||||||
initDefaultGlyphs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initDefaultGlyphs() {
|
|
||||||
glyphResourcePack.withMojangSpaces();
|
|
||||||
glyphResourcePack.with(BlankSlotItem.compile());
|
|
||||||
glyphResourcePack.with(GlyphCompiler.instance().compile(BlackScreen.GLYPH));
|
|
||||||
glyphResourcePack.with(GlyphFont.compile());
|
|
||||||
monologueResources.compile(glyphResourcePack);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void compile() {
|
|
||||||
glyphResourcePack.writeAll(resourcePack);
|
|
||||||
generateAtlases();
|
|
||||||
MinecraftResourcePackWriter.minecraft().writeToZipFile(new File("resource-pack.zip"), resourcePack);
|
|
||||||
log.info("Compiled resource pack. File: ./resource-pack.zip");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateAtlases() {
|
|
||||||
Atlas.Builder atlasBuilder = Atlas.atlas();
|
|
||||||
atlasBuilder.key(Key.key("minecraft", "blocks"));
|
|
||||||
Set<String> applicableTextureKeys = new HashSet<>();
|
|
||||||
|
|
||||||
for (Model model: resourcePack.models()) {
|
|
||||||
for (var layer: model.textures().layers()) {
|
|
||||||
var key = layer.key();
|
|
||||||
if (key != null) {
|
|
||||||
applicableTextureKeys.add(key.asString().concat(".png"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var texture: model.textures().variables().values()) {
|
|
||||||
var key = texture.key();
|
|
||||||
if (key != null) {
|
|
||||||
applicableTextureKeys.add(key.asString().concat(".png"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sources = resourcePack.textures()
|
|
||||||
.stream()
|
|
||||||
//.filter(texture -> !applicableTextureKeys.contains(texture.key().asString()))
|
|
||||||
.map(texture -> (AtlasSource) AtlasSource.single(Key.key(texture.key().toString().replace(".png", "")))).toList();
|
|
||||||
atlasBuilder.sources(sources);
|
|
||||||
resourcePack.atlas(atlasBuilder.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package ru.dragonestia.msb3.api.resource.dialog;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
|
||||||
|
public record Background(
|
||||||
|
GlyphImage part1,
|
||||||
|
GlyphImage part2,
|
||||||
|
GlyphImage part3,
|
||||||
|
GlyphImage part4
|
||||||
|
) {}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package ru.dragonestia.msb3.api.resource.dialog;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
|
||||||
|
public record Button(
|
||||||
|
GlyphImage button1,
|
||||||
|
GlyphImage button2,
|
||||||
|
GlyphImage button3,
|
||||||
|
GlyphImage button4
|
||||||
|
) {
|
||||||
|
|
||||||
|
public GlyphImage get(ButtonNumber number) {
|
||||||
|
return switch (number) {
|
||||||
|
case BUTTON_1 -> button1;
|
||||||
|
case BUTTON_2 -> button2;
|
||||||
|
case BUTTON_3 -> button3;
|
||||||
|
case BUTTON_4 -> button4;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package ru.dragonestia.msb3.api.resource.dialog;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public enum ButtonNumber {
|
||||||
|
BUTTON_1(0),
|
||||||
|
BUTTON_2(1),
|
||||||
|
BUTTON_3(2),
|
||||||
|
BUTTON_4(3);
|
||||||
|
|
||||||
|
private final int index;
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package ru.dragonestia.msb3.api.resource.dialog;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class GlyphPositions {
|
||||||
|
|
||||||
|
public final GuiBackground guiBackground = new GuiBackground(35, -96, 262);
|
||||||
|
public final PhraseText phraseText = new PhraseText(11, -11, 340, 8, 8);
|
||||||
|
public final PhraseSubstrate phraseSubstrate = new PhraseSubstrate(-24, 19, 356, 116);
|
||||||
|
public final Avatar avatar = new Avatar(-144, 11, 100, 116);
|
||||||
|
public final AnswerText answerText = new AnswerText(175, 4, 8, -145, 149, -122, -170);
|
||||||
|
public final AnswerField answerField = new AnswerField(-117, -165, -153, 141, 44);
|
||||||
|
public final AnswerButton answerButton = new AnswerButton(-124, -182, 41, 97, 20);
|
||||||
|
public final ScrollPhraseButton scrollPhraseButton = new ScrollPhraseButton(0, 126, -100, 10);
|
||||||
|
public final ChestSlotsButtons chestSlotsButtons = new ChestSlotsButtons(new int[] {45, 46}, new int[] {52, 53}, new int[] {65, 66}, new int[] {68, 69}, new int[] {56, 57}, new int[] {59, 60});
|
||||||
|
|
||||||
|
public record GuiBackground(int topPartsY, int bottomPartsY, int height) {}
|
||||||
|
|
||||||
|
public record PhraseText(int maxLines, int lineX, int lineWidth, int fontHeight, int firstLineAscent) {
|
||||||
|
|
||||||
|
public TextureProperties firstLineProperties() {
|
||||||
|
return new TextureProperties(fontHeight, firstLineAscent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PhraseSubstrate(int x, int y, int width, int height) {}
|
||||||
|
|
||||||
|
public record Avatar(int x, int y, int height, int frameHeight) {
|
||||||
|
|
||||||
|
public int frameBorderSize() {
|
||||||
|
return (frameHeight - height) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record AnswerText(int lineWidth, int maxLines, int fontHeight, int leftLineX,
|
||||||
|
int rightLineX, int topFirstLineAscent, int bottomFirstLineAscent) {}
|
||||||
|
|
||||||
|
public record AnswerField(int topFieldY, int bottomFieldY, int leftFieldX, int rightFieldX, int height) {}
|
||||||
|
|
||||||
|
public record AnswerButton(int topButtonY, int bottomButtonY, int leftButtonX,
|
||||||
|
int rightButtonX, int height) {}
|
||||||
|
|
||||||
|
public record ScrollPhraseButton(int scrollUpButtonX, int scrollDownButtonX, int buttonY, int height) {}
|
||||||
|
|
||||||
|
public record ChestSlotsButtons(int[] scrollUp, int[] scrollDown, int[] answer1, int[] answer2, int[] answer3, int[] answer4) {}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package ru.dragonestia.msb3.api.resource.dialog;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
|
||||||
|
public record Substrate(
|
||||||
|
GlyphImage part1,
|
||||||
|
GlyphImage part2
|
||||||
|
) {}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package ru.dragonestia.msb3.api.resource.dialog;
|
||||||
|
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
|
||||||
|
public record TextField(
|
||||||
|
GlyphImage field1,
|
||||||
|
GlyphImage field2,
|
||||||
|
GlyphImage field3,
|
||||||
|
GlyphImage field4
|
||||||
|
) {
|
||||||
|
|
||||||
|
public GlyphImage get(ButtonNumber number) {
|
||||||
|
return switch (number) {
|
||||||
|
case BUTTON_1 -> field1;
|
||||||
|
case BUTTON_2 -> field2;
|
||||||
|
case BUTTON_3 -> field3;
|
||||||
|
case BUTTON_4 -> field4;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
package ru.dragonestia.msb3.api.glyph.glyph.image;
|
package ru.dragonestia.msb3.api.resource.dialog;
|
||||||
|
|
||||||
public record TextureProperties(int height, int ascent) {}
|
public record TextureProperties(int height, int ascent) {}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package ru.dragonestia.msb3.api.scheduler;
|
package ru.dragonestia.msb3.api.scheduler;
|
||||||
|
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
|
import net.minestom.server.entity.Entity;
|
||||||
import net.minestom.server.entity.Player;
|
import net.minestom.server.entity.Player;
|
||||||
import net.minestom.server.instance.Instance;
|
import net.minestom.server.instance.Instance;
|
||||||
import net.minestom.server.timer.Task;
|
import net.minestom.server.timer.Task;
|
||||||
@ -18,6 +19,10 @@ public interface Scheduler {
|
|||||||
return new SchedulerImpl(instance.scheduler());
|
return new SchedulerImpl(instance.scheduler());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Scheduler ofEntity(Entity entity) {
|
||||||
|
return new SchedulerImpl(entity.scheduler());
|
||||||
|
}
|
||||||
|
|
||||||
static Scheduler global() {
|
static Scheduler global() {
|
||||||
return new SchedulerImpl(MinecraftServer.getSchedulerManager());
|
return new SchedulerImpl(MinecraftServer.getSchedulerManager());
|
||||||
}
|
}
|
||||||
|
|||||||
45
api/src/main/java/ru/dragonestia/msb3/api/skin/SkinData.java
Normal file
45
api/src/main/java/ru/dragonestia/msb3/api/skin/SkinData.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package ru.dragonestia.msb3.api.skin;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.minestom.server.entity.PlayerSkin;
|
||||||
|
import team.unnamed.creative.base.Writable;
|
||||||
|
|
||||||
|
public class SkinData {
|
||||||
|
|
||||||
|
private final static Gson gson = new Gson();
|
||||||
|
|
||||||
|
@Getter private final String texture;
|
||||||
|
@Getter private final String signature;
|
||||||
|
private transient PlayerSkin skin;
|
||||||
|
|
||||||
|
public SkinData(String texture, String signature) {
|
||||||
|
this.texture = texture;
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized PlayerSkin getSkin() {
|
||||||
|
if (skin == null) {
|
||||||
|
skin = new PlayerSkin(texture, signature);
|
||||||
|
}
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String serialize() {
|
||||||
|
return gson.toJson(new Data(signature, texture));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SkinData fromSkin(PlayerSkin skin) {
|
||||||
|
return new SkinData(skin.textures(), skin.signature());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SkinData fromWriteable(Writable writable) {
|
||||||
|
try {
|
||||||
|
return gson.fromJson(writable.toUTF8String(), SkinData.class);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Data(String signature, String texture) {}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package ru.dragonestia.msb3.api.skin;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.minestom.server.entity.PlayerSkin;
|
||||||
|
import team.unnamed.creative.base.Writable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
@UtilityClass
|
||||||
|
public class SkinStorage {
|
||||||
|
|
||||||
|
public final static Key DEFAULT = Key.key("msb3", "default");
|
||||||
|
|
||||||
|
private final Map<Key, SkinData> skins = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void saveSkinToFile(PlayerSkin skin, File file) {
|
||||||
|
try (var writer = new FileWriter(file)) {
|
||||||
|
writer.write(SkinData.fromSkin(skin).serialize());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Saved skin to {}", file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadSkin(Key key, Writable writable) {
|
||||||
|
var prev = skins.put(key, SkinData.fromWriteable(writable));
|
||||||
|
|
||||||
|
if (prev != null) {
|
||||||
|
log.warn("Detected duplicate skin key: {}", key.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsSkin(Key key) {
|
||||||
|
return skins.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerSkin getSkin(Key key) {
|
||||||
|
var skin = skins.get(key);
|
||||||
|
if (skin == null) {
|
||||||
|
log.warn("No skin for key '{}'. Using default", key.asString());
|
||||||
|
|
||||||
|
return skins.get(DEFAULT).getSkin();
|
||||||
|
}
|
||||||
|
return skin.getSkin();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,21 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.talk;
|
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
import ru.dragonestia.msb3.api.talk.monologue.MonologueTheme;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class Themes {
|
|
||||||
|
|
||||||
private final Map<String, MonologueTheme> monologueThemes = new HashMap<>();
|
|
||||||
|
|
||||||
public void registerMonologueTheme(String identifier, MonologueTheme theme) {
|
|
||||||
monologueThemes.put(identifier, theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MonologueTheme getMonologueTheme(String identifier) {
|
|
||||||
return monologueThemes.get(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
package ru.dragonestia.msb3.api.title;
|
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.title.Title;
|
|
||||||
import net.minestom.server.entity.Player;
|
|
||||||
import ru.dragonestia.msb3.api.ServerBootstrap;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.Glyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.ImageGlyph;
|
|
||||||
import ru.dragonestia.msb3.api.glyph.glyph.image.TextureProperties;
|
|
||||||
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
|
||||||
import team.unnamed.creative.base.Writable;
|
|
||||||
import team.unnamed.creative.texture.Texture;
|
|
||||||
|
|
||||||
public class BlackScreen {
|
|
||||||
|
|
||||||
private static final Writable BACKGROUND_WRITABLE = ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/defaults/fullscreen_background.png");
|
|
||||||
private static final Key FULLSCREEN_BACKGROUND_KEY = Key.key(Glyph.DEFAULT_NAMESPACE, "fullscreen_background.png");
|
|
||||||
public static final ImageGlyph GLYPH = fullscreenBackgroundGlyph();
|
|
||||||
|
|
||||||
private BlackScreen() {}
|
|
||||||
|
|
||||||
public static void show(Player player) {
|
|
||||||
player.showTitle(Title.title(
|
|
||||||
GLYPH.toAdventure(),
|
|
||||||
Component.empty()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ImageGlyph fullscreenBackgroundGlyph() {
|
|
||||||
return ImageGlyph.of(FULLSCREEN_BACKGROUND_KEY,
|
|
||||||
Texture.texture(FULLSCREEN_BACKGROUND_KEY, BACKGROUND_WRITABLE),
|
|
||||||
new TextureProperties(2500, 256));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package ru.dragonestia.msb3.api.ui;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.title.Title;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
||||||
|
import ru.dragonestia.msb3.resource.Resources;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class BlackScreen {
|
||||||
|
|
||||||
|
public final GlyphImage glyph = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "black_screen"),
|
||||||
|
ResourceFromJar.of("glyphs/defaults/fullscreen_background.png"),
|
||||||
|
2500,
|
||||||
|
256);
|
||||||
|
|
||||||
|
public static void show(Player player) {
|
||||||
|
player.showTitle(Title.title(
|
||||||
|
glyph.component(),
|
||||||
|
Component.empty()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
package ru.dragonestia.msb3.api.ui;
|
||||||
|
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.kyori.adventure.text.TextComponent;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.inventory.Inventory;
|
||||||
|
import net.minestom.server.inventory.InventoryType;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions;
|
||||||
|
import ru.dragonestia.msb3.resource.Resources;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphImage;
|
||||||
|
import ru.dragonestia.msb3.resource.utils.ImageUtils;
|
||||||
|
import team.unnamed.creative.base.Writable;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class PictureBanner {
|
||||||
|
|
||||||
|
public static final int CHEST_GUI_WIDTH = 176;
|
||||||
|
|
||||||
|
private final GlyphImage glyph1;
|
||||||
|
private final GlyphImage glyph2;
|
||||||
|
private final GlyphImage glyph3;
|
||||||
|
private final GlyphImage glyph4;
|
||||||
|
|
||||||
|
public PictureBanner(String identifier, Writable writable) {
|
||||||
|
BufferedImage image;
|
||||||
|
Writable part1;
|
||||||
|
Writable part2;
|
||||||
|
Writable part3;
|
||||||
|
Writable part4;
|
||||||
|
|
||||||
|
try (var steam = new ByteArrayInputStream(writable.toByteArray())) {
|
||||||
|
image = ImageIO.read(steam);
|
||||||
|
var w = image.getWidth();
|
||||||
|
var h = image.getHeight();
|
||||||
|
|
||||||
|
part1 = ImageUtils.imageToWritable(image.getSubimage(0, 0, w / 2, h / 2));
|
||||||
|
part2 = ImageUtils.imageToWritable(image.getSubimage(w / 2, 0, w / 2, h / 2));
|
||||||
|
part3 = ImageUtils.imageToWritable(image.getSubimage(0, h / 2, w / 2, h / 2));
|
||||||
|
part4 = ImageUtils.imageToWritable(image.getSubimage(w / 2, h / 2, w / 2, h / 2));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph1 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "banner/" + identifier + "/1"),
|
||||||
|
part1,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.topPartsY()
|
||||||
|
);
|
||||||
|
glyph2 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "banner/" + identifier + "/2"),
|
||||||
|
part2,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.topPartsY()
|
||||||
|
);
|
||||||
|
glyph3 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "banner/" + identifier + "/3"),
|
||||||
|
part3,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.bottomPartsY()
|
||||||
|
);
|
||||||
|
glyph4 = Resources.createGlyph(
|
||||||
|
Key.key("msb3", "banner/" + identifier + "/4"),
|
||||||
|
part4,
|
||||||
|
GlyphPositions.guiBackground.height() / 2,
|
||||||
|
GlyphPositions.guiBackground.bottomPartsY()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextComponent render() {
|
||||||
|
var builder = new GlyphComponentBuilder();
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 2 - glyph1.width(), glyph1);
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 1, glyph2);
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 2 - glyph3.width(), glyph3);
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 1, glyph4);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(Player player) {
|
||||||
|
var inv = new Inventory(InventoryType.CHEST_6_ROW, render());
|
||||||
|
player.openInventory(inv);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package ru.dragonestia.msb3.api.ui;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import ru.dragonestia.msb3.api.ui.dialogue.DialogueTheme;
|
||||||
|
import ru.dragonestia.msb3.api.ui.monologue.MonologueTheme;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class TalksThemes {
|
||||||
|
|
||||||
|
private final Map<String, MonologueTheme> monologueThemes = new HashMap<>();
|
||||||
|
private final Map<String, DialogueTheme> dialogueThemes = new HashMap<>();
|
||||||
|
|
||||||
|
public void registerMonologueTheme(String identifier, MonologueTheme theme) {
|
||||||
|
monologueThemes.put(identifier, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<MonologueTheme> getMonologueTheme(String 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package ru.dragonestia.msb3.api.ui.bossbar;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.kyori.adventure.bossbar.BossBar;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.player.PlayerContext;
|
||||||
|
import ru.dragonestia.msb3.api.player.defaults.KeyedBossBarContext;
|
||||||
|
import ru.dragonestia.msb3.api.util.ResourceFromJar;
|
||||||
|
import ru.dragonestia.msb3.resource.Resources;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class KeyedBossBars {
|
||||||
|
|
||||||
|
static {
|
||||||
|
var rp = Resources.getResourcePack();
|
||||||
|
rp.texture(Key.key("minecraft", "gui/sprites/boss_bar/white_background.png"),
|
||||||
|
ResourceFromJar.of("glyphs/navigator/notched_20_background.png"));
|
||||||
|
|
||||||
|
rp.texture(Key.key("minecraft", "gui/sprites/boss_bar/white_progress.png"),
|
||||||
|
ResourceFromJar.of("glyphs/navigator/notched_20_progress.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideAll(Player player) {
|
||||||
|
context(player).hideAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hide(Player player, String bossBarId) {
|
||||||
|
context(player).hide(bossBarId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossBar show(Player player, String bossBarId, BossBar bossBar) {
|
||||||
|
return context(player).show(bossBarId, bossBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BossBar showText(Player player, String bossBarId, Component text) {
|
||||||
|
return show(player, bossBarId, BossBar.bossBar(text, 1, BossBar.Color.WHITE, BossBar.Overlay.PROGRESS));
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyedBossBarContext context(Player player) {
|
||||||
|
return PlayerContext.of(player, KeyedBossBarContext.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package ru.dragonestia.msb3.api.ui.dialogue;
|
||||||
|
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||||
|
|
||||||
|
public record AnswerClickContext(Player player, DialogueRenderer renderer, ButtonNumber buttonNumber, String buttonText) {}
|
||||||
@ -0,0 +1,513 @@
|
|||||||
|
package ru.dragonestia.msb3.api.ui.dialogue;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.TextComponent;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.Style;
|
||||||
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import net.minestom.server.entity.Player;
|
||||||
|
import net.minestom.server.event.EventListener;
|
||||||
|
import net.minestom.server.event.inventory.InventoryCloseEvent;
|
||||||
|
import net.minestom.server.event.inventory.InventoryPreClickEvent;
|
||||||
|
import net.minestom.server.inventory.Inventory;
|
||||||
|
import net.minestom.server.inventory.InventoryType;
|
||||||
|
import net.minestom.server.item.ItemStack;
|
||||||
|
import net.minestom.server.tag.Tag;
|
||||||
|
import org.apache.commons.text.WordUtils;
|
||||||
|
import ru.dragonestia.msb3.api.item.BlankSlotItem;
|
||||||
|
import ru.dragonestia.msb3.api.item.ItemUtil;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
|
||||||
|
import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions;
|
||||||
|
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.resource.glyph.GlyphComponent;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;
|
||||||
|
import ru.dragonestia.msb3.resource.glyph.MinecraftFont;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Log4j2
|
||||||
|
public class DialogueRenderer extends Inventory {
|
||||||
|
|
||||||
|
public static final int CHEST_GUI_WIDTH = 176;
|
||||||
|
private static final Tag<String> TAG_CLICK_TYPE = Tag.String("msb3_dialogue_click_type");
|
||||||
|
|
||||||
|
private final Player player;
|
||||||
|
@Getter @Setter private DialogueTheme theme;
|
||||||
|
@Getter private String text = "";
|
||||||
|
private int shifted = 0;
|
||||||
|
private int maxLines = 0;
|
||||||
|
private final EnumMap<ButtonNumber, String> buttonsText = new EnumMap<>(ButtonNumber.class);
|
||||||
|
private final EnumMap<ButtonNumber, Consumer<AnswerClickContext>> onAnswerClick = new EnumMap<>(ButtonNumber.class);
|
||||||
|
|
||||||
|
private ItemStack[] itemBackup;
|
||||||
|
private EventListener<InventoryCloseEvent> onCloseListener;
|
||||||
|
private EventListener<InventoryPreClickEvent> onClickListener;
|
||||||
|
@Getter @Setter private OnCloseDialog onClose;
|
||||||
|
private final Object lockButtonExecution = new Object();
|
||||||
|
private volatile boolean lockedExecution = false;
|
||||||
|
|
||||||
|
public DialogueRenderer(Player player, DialogueTheme theme) {
|
||||||
|
super(InventoryType.CHEST_6_ROW, Component.empty());
|
||||||
|
this.player = player;
|
||||||
|
this.theme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
this.shifted = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scrollUp() {
|
||||||
|
if (shifted > 0) {
|
||||||
|
shifted = Math.max(0, shifted - 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scrollDown() {
|
||||||
|
if (maxLines > shifted + GlyphPositions.phraseText.maxLines()) {
|
||||||
|
shifted = Math.min(maxLines - GlyphPositions.phraseText.maxLines(), shifted + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rerender() {
|
||||||
|
setTitle(render());
|
||||||
|
|
||||||
|
var slots = GlyphPositions.chestSlotsButtons;
|
||||||
|
|
||||||
|
for (var slot: slots.scrollUp()) {
|
||||||
|
if (shifted != 0) {
|
||||||
|
setSlotClickable(slot, null, ClickType.SCROLL_UP);
|
||||||
|
} else {
|
||||||
|
resetClickableSlot(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var slot: slots.scrollDown()) {
|
||||||
|
if (maxLines > shifted + GlyphPositions.phraseText.maxLines()) {
|
||||||
|
setSlotClickable(slot, null, ClickType.SCROLL_DOWN);
|
||||||
|
} else {
|
||||||
|
resetClickableSlot(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var slot: slots.answer1()) {
|
||||||
|
if (buttonsText.containsKey(ButtonNumber.BUTTON_1)) {
|
||||||
|
setSlotClickable(slot, splitAnswerText(buttonsText.get(ButtonNumber.BUTTON_1)), ClickType.ANSWER_1);
|
||||||
|
} else {
|
||||||
|
resetClickableSlot(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var slot: slots.answer2()) {
|
||||||
|
if (buttonsText.containsKey(ButtonNumber.BUTTON_2)) {
|
||||||
|
setSlotClickable(slot, splitAnswerText(buttonsText.get(ButtonNumber.BUTTON_2)), ClickType.ANSWER_2);
|
||||||
|
} else {
|
||||||
|
resetClickableSlot(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var slot: slots.answer3()) {
|
||||||
|
if (buttonsText.containsKey(ButtonNumber.BUTTON_3)) {
|
||||||
|
setSlotClickable(slot, splitAnswerText(buttonsText.get(ButtonNumber.BUTTON_3)), ClickType.ANSWER_3);
|
||||||
|
} else {
|
||||||
|
resetClickableSlot(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var slot: slots.answer4()) {
|
||||||
|
if (buttonsText.containsKey(ButtonNumber.BUTTON_4)) {
|
||||||
|
setSlotClickable(slot, splitAnswerText(buttonsText.get(ButtonNumber.BUTTON_4)), ClickType.ANSWER_4);
|
||||||
|
} else {
|
||||||
|
resetClickableSlot(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetClickableSlot(int slot) {
|
||||||
|
if (slot < getSize()) {
|
||||||
|
setItemStack(slot, ItemStack.AIR);
|
||||||
|
} else {
|
||||||
|
player.getInventory().setItemStack(slot - getSize(), ItemStack.AIR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSlotClickable(int slot, List<Component> component, ClickType clickType) {
|
||||||
|
var item = BlankSlotItem.getItem().builder()
|
||||||
|
.set(ItemUtil.TAG_UNDROPPABLE, true)
|
||||||
|
.set(TAG_CLICK_TYPE, clickType.name());
|
||||||
|
|
||||||
|
Component text = null;
|
||||||
|
switch (clickType) {
|
||||||
|
case SCROLL_UP -> text = getTheme().getTextScrollUp();
|
||||||
|
case SCROLL_DOWN -> text = getTheme().getTextScrollDown();
|
||||||
|
case ANSWER_1, ANSWER_2, ANSWER_3, ANSWER_4 -> {
|
||||||
|
text = getTheme().getTextPrefixAnswer();
|
||||||
|
for (var line: component) {
|
||||||
|
text = text.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var lines = new ArrayList<>(text.children());
|
||||||
|
if (lines.isEmpty()) throw new IllegalStateException("Button have zero lines");
|
||||||
|
|
||||||
|
item.customName(lines.removeFirst());
|
||||||
|
if (!lines.isEmpty()) item.lore(lines);
|
||||||
|
|
||||||
|
if (slot < getSize()) {
|
||||||
|
setItemStack(slot, item.build());
|
||||||
|
} else {
|
||||||
|
player.getInventory().setItemStack(slot - getSize(), item.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
try {
|
||||||
|
player.openInventory(this);
|
||||||
|
onOpen();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
onClose(false);
|
||||||
|
player.sendMessage(Component.text("[ERROR] " + ex.getMessage(), NamedTextColor.RED));
|
||||||
|
|
||||||
|
log.error(ex.getMessage(), ex);
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close(boolean closedByPlayer) {
|
||||||
|
onClose(closedByPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onOpen() {
|
||||||
|
synchronized (this) {
|
||||||
|
itemBackup = player.getInventory().getItemStacks();
|
||||||
|
player.getInventory().clear();
|
||||||
|
|
||||||
|
rerender();
|
||||||
|
|
||||||
|
onCloseListener = EventListener.of(InventoryCloseEvent.class, event -> {
|
||||||
|
if (event.getInventory() instanceof DialogueRenderer renderer) {
|
||||||
|
renderer.close(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
player.eventNode().addListener(onCloseListener);
|
||||||
|
|
||||||
|
onClickListener = EventListener.of(InventoryPreClickEvent.class, event -> {
|
||||||
|
var player = event.getPlayer();
|
||||||
|
var item = event.getClickedItem();
|
||||||
|
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
if (item.isAir()) return;
|
||||||
|
|
||||||
|
var type = ClickType.valueOf(item.getTag(TAG_CLICK_TYPE));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case SCROLL_UP -> {
|
||||||
|
scrollUp();
|
||||||
|
rerender();
|
||||||
|
getTheme().getOnScrollText().accept(player);
|
||||||
|
}
|
||||||
|
case SCROLL_DOWN -> {
|
||||||
|
scrollDown();
|
||||||
|
rerender();
|
||||||
|
getTheme().getOnScrollText().accept(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
case ANSWER_1, ANSWER_2, ANSWER_3, ANSWER_4 -> {
|
||||||
|
if (tryLockButtonActionExecution()) {
|
||||||
|
try {
|
||||||
|
var buttonNumber = type.buttonNumber;
|
||||||
|
if (onAnswerClick.containsKey(buttonNumber)) {
|
||||||
|
var ctx = new AnswerClickContext(
|
||||||
|
player,
|
||||||
|
this,
|
||||||
|
buttonNumber,
|
||||||
|
buttonsText.get(buttonNumber)
|
||||||
|
);
|
||||||
|
|
||||||
|
onAnswerClick.get(buttonNumber).accept(ctx);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
DebugMessage.sendError(player, "Во время обработки нажатия кнопки произошла ошибка: " + ex.getMessage());
|
||||||
|
log.error(ex.getMessage(), ex);
|
||||||
|
} finally {
|
||||||
|
unlockButtonActionExecution();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
player.eventNode().addListener(onClickListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onClose(boolean closedByPlayer) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (onCloseListener == null) return;
|
||||||
|
|
||||||
|
if (itemBackup != null) {
|
||||||
|
var inv = player.getInventory();
|
||||||
|
for (int i = 0; i < itemBackup.length; i++) {
|
||||||
|
inv.setItemStack(i, itemBackup[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (onClose != null) onClose.onClose(closedByPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component render() {
|
||||||
|
var builder = new GlyphComponentBuilder();
|
||||||
|
|
||||||
|
// GUI background
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 2 - theme.getBackground().part1().width(), theme.getBackground().part1());
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 1, theme.getBackground().part2());
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 2 - theme.getBackground().part3().width(), theme.getBackground().part3());
|
||||||
|
builder.append(CHEST_GUI_WIDTH / 2 + 1, theme.getBackground().part4());
|
||||||
|
|
||||||
|
// Phrase substrate
|
||||||
|
builder.append(GlyphPositions.phraseSubstrate.x(), theme.getSubstrate().part1());
|
||||||
|
builder.append(GlyphPositions.phraseSubstrate.x() + GlyphPositions.phraseSubstrate.width() / 2, theme.getSubstrate().part2());
|
||||||
|
|
||||||
|
// Frame and avatar
|
||||||
|
builder.append(GlyphPositions.avatar.x() - 8, theme.getAvatarFrame());
|
||||||
|
builder.append(GlyphPositions.avatar.x(), theme.getAvatar());
|
||||||
|
|
||||||
|
// Phrase text
|
||||||
|
renderText(builder, shifted);
|
||||||
|
|
||||||
|
// Scroll phrase buttons (optional)
|
||||||
|
if (shifted != 0) {
|
||||||
|
builder.append(
|
||||||
|
GlyphPositions.scrollPhraseButton.scrollUpButtonX(),
|
||||||
|
theme.getScrollTextUp()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLines > shifted + GlyphPositions.phraseText.maxLines()) {
|
||||||
|
builder.append(
|
||||||
|
GlyphPositions.scrollPhraseButton.scrollDownButtonX(),
|
||||||
|
theme.getScrollTextDown()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Answers
|
||||||
|
for (var number: ButtonNumber.values()) {
|
||||||
|
if (buttonsText.containsKey(number)) {
|
||||||
|
builder.append(number.getIndex() % 2 == 0
|
||||||
|
? GlyphPositions.answerField.leftFieldX()
|
||||||
|
: GlyphPositions.answerField.rightFieldX(),
|
||||||
|
switch (number) {
|
||||||
|
case BUTTON_1 -> theme.getActiveTextField1();
|
||||||
|
case BUTTON_2 -> theme.getActiveTextField2();
|
||||||
|
case BUTTON_3 -> theme.getActiveTextField3();
|
||||||
|
case BUTTON_4 -> theme.getActiveTextField4();
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.append(number.getIndex() % 2 == 0
|
||||||
|
? GlyphPositions.answerButton.leftButtonX()
|
||||||
|
: GlyphPositions.answerButton.rightButtonX(),
|
||||||
|
switch (number) {
|
||||||
|
case BUTTON_1 -> theme.getButton1();
|
||||||
|
case BUTTON_2 -> theme.getButton2();
|
||||||
|
case BUTTON_3 -> theme.getButton3();
|
||||||
|
case BUTTON_4 -> theme.getButton4();
|
||||||
|
});
|
||||||
|
|
||||||
|
var lines = breakIntoLines(
|
||||||
|
buttonsText.get(number),
|
||||||
|
number.getIndex() < 2
|
||||||
|
? new TextureProperties(GlyphPositions.answerText.fontHeight(), GlyphPositions.answerText.topFirstLineAscent())
|
||||||
|
: new TextureProperties(GlyphPositions.answerText.fontHeight(), GlyphPositions.answerText.bottomFirstLineAscent()),
|
||||||
|
GlyphPositions.answerText.fontHeight(),
|
||||||
|
GlyphPositions.answerText.lineWidth());
|
||||||
|
|
||||||
|
|
||||||
|
for (int lineIdx = 0; lineIdx < Math.min(lines.size(), GlyphPositions.answerText.maxLines()); lineIdx++) {
|
||||||
|
boolean endWithDots = lineIdx + 1 == GlyphPositions.answerText.maxLines() && lineIdx + 1 != lines.size();
|
||||||
|
var line = lines.get(lineIdx);
|
||||||
|
|
||||||
|
builder.setColorTo(switch (number) {
|
||||||
|
case BUTTON_1 -> theme.getColorAnswerText1();
|
||||||
|
case BUTTON_2 -> theme.getColorAnswerText2();
|
||||||
|
case BUTTON_3 -> theme.getColorAnswerText3();
|
||||||
|
case BUTTON_4 -> theme.getColorAnswerText4();
|
||||||
|
}).append(
|
||||||
|
number.getIndex() % 2 == 0
|
||||||
|
? GlyphPositions.answerText.leftLineX()
|
||||||
|
: GlyphPositions.answerText.rightLineX(),
|
||||||
|
line.toGlyphList(line.textureProperties(), 0, endWithDots)
|
||||||
|
).resetColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
builder.append(number.getIndex() % 2 == 0
|
||||||
|
? GlyphPositions.answerField.leftFieldX()
|
||||||
|
: GlyphPositions.answerField.rightFieldX(),
|
||||||
|
switch (number) {
|
||||||
|
case BUTTON_1 -> theme.getNotActiveTextField1();
|
||||||
|
case BUTTON_2 -> theme.getNotActiveTextField2();
|
||||||
|
case BUTTON_3 -> theme.getNotActiveTextField3();
|
||||||
|
case BUTTON_4 -> theme.getNotActiveTextField4();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TextLine> breakIntoLines(String input, TextureProperties firstLineProperties, int fontHeight, int maxWidth) throws IllegalStateException {
|
||||||
|
var resolvedText = MiniMessage.miniMessage().deserialize(input);
|
||||||
|
var lines = StringUtil.splitIntoParts(resolvedText, maxWidth, line -> {
|
||||||
|
int width = 0;
|
||||||
|
width += MinecraftFont.translate(firstLineProperties.height(), firstLineProperties.ascent(), line).width();
|
||||||
|
return width;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (lines == null) {
|
||||||
|
throw new IllegalStateException("Cannot fit text into dialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
var textLines = new ArrayList<TextLine>();
|
||||||
|
for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
||||||
|
var text = lines[lineIdx];
|
||||||
|
|
||||||
|
var properties = new TextureProperties(firstLineProperties.height(), firstLineProperties.ascent() - lineIdx * (fontHeight + 1));
|
||||||
|
textLines.add(new TextLine(theme, ((TextComponent) text).content(), properties));
|
||||||
|
}
|
||||||
|
return textLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderText(GlyphComponentBuilder builder, int shift) {
|
||||||
|
var textLines = breakIntoLines(
|
||||||
|
text,
|
||||||
|
GlyphPositions.phraseText.firstLineProperties(),
|
||||||
|
GlyphPositions.phraseText.fontHeight(),
|
||||||
|
GlyphPositions.phraseText.lineWidth());
|
||||||
|
maxLines = textLines.size();
|
||||||
|
|
||||||
|
for (int lineIdx = shift; (lineIdx - shift) < Math.min(textLines.size() - shift, GlyphPositions.phraseText.maxLines()); lineIdx++) {
|
||||||
|
var line = textLines.get(lineIdx);
|
||||||
|
builder.setColorTo(getTheme().getColorText())
|
||||||
|
.append(GlyphPositions.phraseText.lineX(), line.toGlyphList(
|
||||||
|
line.textureProperties(),
|
||||||
|
shift,
|
||||||
|
textLines.size() - lineIdx > 0 && lineIdx - shift == GlyphPositions.phraseText.maxLines() - 1
|
||||||
|
)).resetColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Component> splitAnswerText(String text) {
|
||||||
|
var style = Style.style()
|
||||||
|
.decoration(TextDecoration.ITALIC, false)
|
||||||
|
.color(getTheme().getColorAnswerItemText())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var lines = new ArrayList<Component>();
|
||||||
|
for (var line: text.split("\n")) {
|
||||||
|
for (var subLine: WordUtils.wrap(line, 45, "\n", false).split("\n")) {
|
||||||
|
lines.add(Component.text()
|
||||||
|
.resetStyle()
|
||||||
|
.append(Component.text(subLine, style))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if (text == null) {
|
||||||
|
buttonsText.remove(buttonNumber);
|
||||||
|
onAnswerClick.remove(buttonNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonsText.put(buttonNumber, text);
|
||||||
|
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) {
|
||||||
|
|
||||||
|
public GlyphComponent toGlyphList(TextureProperties parentProperties, int lineShift, boolean cutEnding) {
|
||||||
|
var properties = new TextureProperties(
|
||||||
|
parentProperties.height(),
|
||||||
|
parentProperties.ascent() + lineShift * (textureProperties().height() + 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
Component text = Component.text(this.text);
|
||||||
|
if (cutEnding) text = StringUtil.cutEnding(text, " <...>");
|
||||||
|
|
||||||
|
return toList(((TextComponent) text).content(), properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MinecraftFont.Text toList(String text, TextureProperties properties) {
|
||||||
|
return MinecraftFont.translate(properties.height(), properties.ascent(), text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private enum ClickType {
|
||||||
|
ANSWER_1(ButtonNumber.BUTTON_1),
|
||||||
|
ANSWER_2(ButtonNumber.BUTTON_2),
|
||||||
|
ANSWER_3(ButtonNumber.BUTTON_3),
|
||||||
|
ANSWER_4(ButtonNumber.BUTTON_4),
|
||||||
|
SCROLL_UP(null),
|
||||||
|
SCROLL_DOWN(null);
|
||||||
|
|
||||||
|
private final ButtonNumber buttonNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user