2025-12-29 15:44:48 +09:00
|
|
|
package com.beemer.essentials.gui
|
|
|
|
|
|
2025-12-30 19:40:36 +09:00
|
|
|
import com.beemer.essentials.util.SoundUtils
|
2025-12-29 15:44:48 +09:00
|
|
|
import net.minecraft.ChatFormatting
|
|
|
|
|
import net.minecraft.core.component.DataComponents
|
|
|
|
|
import net.minecraft.nbt.CompoundTag
|
|
|
|
|
import net.minecraft.network.chat.Component
|
|
|
|
|
import net.minecraft.server.level.ServerPlayer
|
|
|
|
|
import net.minecraft.world.Container
|
|
|
|
|
import net.minecraft.world.SimpleContainer
|
|
|
|
|
import net.minecraft.world.SimpleMenuProvider
|
|
|
|
|
import net.minecraft.world.entity.player.Inventory
|
|
|
|
|
import net.minecraft.world.entity.player.Player
|
|
|
|
|
import net.minecraft.world.inventory.AbstractContainerMenu
|
|
|
|
|
import net.minecraft.world.inventory.ClickType
|
|
|
|
|
import net.minecraft.world.inventory.ContainerLevelAccess
|
|
|
|
|
import net.minecraft.world.inventory.CraftingMenu
|
|
|
|
|
import net.minecraft.world.inventory.MenuType
|
|
|
|
|
import net.minecraft.world.inventory.Slot
|
|
|
|
|
import net.minecraft.world.item.ItemStack
|
|
|
|
|
import net.minecraft.world.item.Items
|
|
|
|
|
import net.minecraft.world.item.component.CustomData
|
|
|
|
|
import net.minecraft.world.item.component.ItemLore
|
|
|
|
|
|
|
|
|
|
/** Essentials 메뉴 GUI - Shift+F로 열기 스폰, 좌표, 제작대 등 주요 기능 바로가기 */
|
|
|
|
|
class EssentialsMenuGui(
|
|
|
|
|
syncId: Int,
|
|
|
|
|
playerInv: Inventory,
|
|
|
|
|
val container: Container,
|
|
|
|
|
val viewer: ServerPlayer
|
2025-12-30 19:26:10 +09:00
|
|
|
) : AbstractContainerMenu(MenuType.GENERIC_9x6, syncId) {
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
companion object {
|
2025-12-30 19:26:10 +09:00
|
|
|
const val CONTAINER_SIZE = 54 // 9x6 = 54
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-30 19:26:10 +09:00
|
|
|
// 메뉴 아이템 슬롯 위치 (9x6 기준)
|
|
|
|
|
const val SLOT_SPAWN = 19 // 스폰
|
|
|
|
|
const val SLOT_COORDINATES = 21 // 좌표
|
|
|
|
|
const val SLOT_WORKBENCH = 23 // 제작대
|
|
|
|
|
const val SLOT_TPA = 25 // TPA
|
2025-12-29 15:44:48 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
init {
|
|
|
|
|
// 컨테이너 슬롯 추가
|
|
|
|
|
for (i in 0 until CONTAINER_SIZE) {
|
|
|
|
|
val x = 8 + (i % 9) * 18
|
|
|
|
|
val y = 18 + (i / 9) * 18
|
|
|
|
|
addSlot(
|
|
|
|
|
object : Slot(container, i, x, y) {
|
|
|
|
|
override fun mayPickup(player: Player): Boolean = false
|
|
|
|
|
override fun mayPlace(stack: ItemStack): Boolean = false
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
// 플레이어 인벤토리 슬롯
|
|
|
|
|
for (row in 0 until 3) {
|
|
|
|
|
for (col in 0 until 9) {
|
|
|
|
|
val index = col + row * 9 + 9
|
|
|
|
|
val x = 8 + col * 18
|
|
|
|
|
val y = 86 + row * 18
|
|
|
|
|
addSlot(Slot(playerInv, index, x, y))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (col in 0 until 9) {
|
|
|
|
|
val x = 8 + col * 18
|
|
|
|
|
val y = 144
|
|
|
|
|
addSlot(Slot(playerInv, col, x, y))
|
|
|
|
|
}
|
2025-12-29 15:44:48 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
override fun clicked(slotId: Int, button: Int, clickType: ClickType, player: Player) {
|
2025-12-30 19:26:10 +09:00
|
|
|
// 허용된 버튼 슬롯 목록
|
|
|
|
|
val buttonSlots =
|
|
|
|
|
setOf(
|
|
|
|
|
// 스폰
|
|
|
|
|
18,
|
|
|
|
|
19,
|
|
|
|
|
20,
|
|
|
|
|
27,
|
|
|
|
|
28,
|
|
|
|
|
29,
|
|
|
|
|
// 제작대 (순서 변경)
|
|
|
|
|
21,
|
|
|
|
|
22,
|
|
|
|
|
23,
|
|
|
|
|
30,
|
|
|
|
|
31,
|
|
|
|
|
32,
|
|
|
|
|
// 좌표 (순서 변경)
|
|
|
|
|
24,
|
|
|
|
|
25,
|
|
|
|
|
26,
|
|
|
|
|
33,
|
|
|
|
|
34,
|
|
|
|
|
35,
|
|
|
|
|
// TPA
|
|
|
|
|
36,
|
|
|
|
|
37,
|
|
|
|
|
38,
|
|
|
|
|
45,
|
|
|
|
|
46,
|
|
|
|
|
47
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 버튼 슬롯이 아니면 기본 처리
|
|
|
|
|
if (slotId !in buttonSlots || player !is ServerPlayer) {
|
2025-12-29 17:27:49 +09:00
|
|
|
super.clicked(slotId, button, clickType, player)
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
val stack = slots[slotId].item
|
|
|
|
|
val tag = stack.get(DataComponents.CUSTOM_DATA)?.copyTag() ?: return
|
|
|
|
|
val action = tag.getString("action")
|
2025-12-30 19:26:10 +09:00
|
|
|
if (action.isEmpty()) return
|
2025-12-29 17:27:49 +09:00
|
|
|
|
|
|
|
|
when (action) {
|
|
|
|
|
"spawn" -> {
|
|
|
|
|
player.closeContainer()
|
|
|
|
|
player.server.commands.performPrefixedCommand(
|
|
|
|
|
player.createCommandSourceStack(),
|
|
|
|
|
"스폰"
|
|
|
|
|
)
|
2025-12-30 19:26:10 +09:00
|
|
|
// 스폰 명령어에서 이미 사운드 재생하므로 여기서는 제거
|
2025-12-29 17:27:49 +09:00
|
|
|
}
|
|
|
|
|
"coordinates" -> {
|
|
|
|
|
playClickSound(player)
|
|
|
|
|
player.closeContainer()
|
|
|
|
|
player.server.commands.performPrefixedCommand(
|
|
|
|
|
player.createCommandSourceStack(),
|
|
|
|
|
"좌표"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
"workbench" -> {
|
|
|
|
|
player.closeContainer()
|
|
|
|
|
openWorkbench(player)
|
|
|
|
|
playClickSound(player)
|
|
|
|
|
}
|
|
|
|
|
"tpa" -> {
|
2025-12-30 19:26:10 +09:00
|
|
|
// TPA 명령어에서 사운드 재생하므로 여기서는 제거
|
2025-12-29 17:27:49 +09:00
|
|
|
player.closeContainer()
|
|
|
|
|
player.server.commands.performPrefixedCommand(
|
|
|
|
|
player.createCommandSourceStack(),
|
|
|
|
|
"tpa"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun playClickSound(player: ServerPlayer) {
|
2025-12-30 19:40:36 +09:00
|
|
|
SoundUtils.playClick(player)
|
2025-12-29 15:44:48 +09:00
|
|
|
}
|
|
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
private fun openWorkbench(player: ServerPlayer) {
|
|
|
|
|
val access =
|
|
|
|
|
object : ContainerLevelAccess {
|
|
|
|
|
override fun <T : Any> evaluate(
|
|
|
|
|
function:
|
|
|
|
|
java.util.function.BiFunction<
|
|
|
|
|
net.minecraft.world.level.Level,
|
|
|
|
|
net.minecraft.core.BlockPos,
|
|
|
|
|
T>
|
|
|
|
|
): java.util.Optional<T> {
|
|
|
|
|
return java.util.Optional.ofNullable(
|
|
|
|
|
function.apply(
|
|
|
|
|
player.level(),
|
|
|
|
|
player.blockPosition()
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
player.openMenu(
|
|
|
|
|
SimpleMenuProvider(
|
|
|
|
|
{ containerId, inventory, _ ->
|
|
|
|
|
object : CraftingMenu(containerId, inventory, access) {
|
|
|
|
|
override fun stillValid(player: Player): Boolean =
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Component.translatable("container.crafting")
|
|
|
|
|
)
|
2025-12-29 15:44:48 +09:00
|
|
|
)
|
2025-12-29 17:27:49 +09:00
|
|
|
}
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
override fun stillValid(player: Player): Boolean = true
|
|
|
|
|
override fun quickMoveStack(player: Player, index: Int): ItemStack = ItemStack.EMPTY
|
2025-12-29 15:44:48 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 메뉴 컨테이너 생성 */
|
|
|
|
|
fun createEssentialsMenuContainer(): SimpleContainer {
|
2025-12-30 19:26:10 +09:00
|
|
|
val container = SimpleContainer(54) // 9x6 = 54
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-30 19:26:10 +09:00
|
|
|
// 버튼 생성 헬퍼 함수 (툴팁 포함)
|
|
|
|
|
fun createButton(action: String, name: String, description: String): ItemStack {
|
|
|
|
|
val item = ItemStack(Items.GLASS_PANE)
|
|
|
|
|
// 이름 설정 (하늘색)
|
|
|
|
|
item.set(
|
|
|
|
|
DataComponents.CUSTOM_NAME,
|
|
|
|
|
Component.literal(name).withStyle {
|
|
|
|
|
it.withColor(0xA8D8D8).withItalic(false)
|
|
|
|
|
}
|
2025-12-29 17:27:49 +09:00
|
|
|
)
|
2025-12-30 19:26:10 +09:00
|
|
|
// 설명 설정 (회색)
|
|
|
|
|
item.set(
|
|
|
|
|
DataComponents.LORE,
|
|
|
|
|
ItemLore(
|
|
|
|
|
listOf(
|
|
|
|
|
Component.literal(description).withStyle {
|
|
|
|
|
it.withColor(ChatFormatting.GRAY).withItalic(false)
|
|
|
|
|
}
|
|
|
|
|
)
|
2025-12-29 17:27:49 +09:00
|
|
|
)
|
|
|
|
|
)
|
2025-12-30 19:26:10 +09:00
|
|
|
item.set(
|
|
|
|
|
DataComponents.CUSTOM_DATA,
|
|
|
|
|
CustomData.of(CompoundTag().apply { putString("action", action) })
|
2025-12-29 17:27:49 +09:00
|
|
|
)
|
2025-12-30 19:26:10 +09:00
|
|
|
// CustomModelData를 1로 설정하여 텍스처 제거
|
|
|
|
|
item.set(
|
|
|
|
|
DataComponents.CUSTOM_MODEL_DATA,
|
|
|
|
|
net.minecraft.world.item.component.CustomModelData(1)
|
2025-12-29 17:27:49 +09:00
|
|
|
)
|
2025-12-30 19:26:10 +09:00
|
|
|
return item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 스폰 버튼 (3x2 영역: 18,19,20,27,28,29)
|
|
|
|
|
val spawnSlots = listOf(18, 19, 20, 27, 28, 29)
|
|
|
|
|
val spawnItem = createButton("spawn", "스폰", "스폰 지점으로 이동합니다")
|
|
|
|
|
spawnSlots.forEach { container.setItem(it, spawnItem.copy()) }
|
|
|
|
|
|
|
|
|
|
// 제작대 버튼 (3x2 영역: 21,22,23,30,31,32)
|
|
|
|
|
val workbenchSlots = listOf(21, 22, 23, 30, 31, 32)
|
|
|
|
|
val workbenchItem = createButton("workbench", "제작대", "제작대를 엽니다")
|
|
|
|
|
workbenchSlots.forEach { container.setItem(it, workbenchItem.copy()) }
|
|
|
|
|
|
|
|
|
|
// 좌표 버튼 (3x2 영역: 24,25,26,33,34,35)
|
|
|
|
|
val coordSlots = listOf(24, 25, 26, 33, 34, 35)
|
|
|
|
|
val coordItem = createButton("coordinates", "좌표", "저장된 좌표 목록을 엽니다")
|
|
|
|
|
coordSlots.forEach { container.setItem(it, coordItem.copy()) }
|
|
|
|
|
|
|
|
|
|
// TPA 버튼 (3x2 영역: 36,37,38,45,46,47)
|
|
|
|
|
val tpaSlots = listOf(36, 37, 38, 45, 46, 47)
|
|
|
|
|
val tpaItem = createButton("tpa", "텔레포트", "다른 플레이어에게 이동합니다")
|
|
|
|
|
tpaSlots.forEach { container.setItem(it, tpaItem.copy()) }
|
2025-12-29 15:44:48 +09:00
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
return container
|
2025-12-29 15:44:48 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 메뉴 GUI 열기 */
|
|
|
|
|
fun openEssentialsMenu(player: ServerPlayer) {
|
2025-12-29 17:27:49 +09:00
|
|
|
val container = createEssentialsMenuContainer()
|
2025-12-30 19:26:10 +09:00
|
|
|
|
|
|
|
|
// 메뉴 오픈 사운드 재생
|
2025-12-30 19:40:36 +09:00
|
|
|
SoundUtils.playMenu(player)
|
2025-12-30 19:26:10 +09:00
|
|
|
|
|
|
|
|
// 커스텀 GUI 이미지를 위한 유니코드 문자
|
|
|
|
|
// \uF808 = -8 (왼쪽으로 8px 이동)
|
|
|
|
|
// \uE000 = 메뉴 GUI 이미지
|
|
|
|
|
val customGuiPrefix = "\uF808\uE000"
|
|
|
|
|
|
2025-12-29 17:27:49 +09:00
|
|
|
player.openMenu(
|
|
|
|
|
SimpleMenuProvider(
|
|
|
|
|
{ windowId, inv, _ -> EssentialsMenuGui(windowId, inv, container, player) },
|
2025-12-30 19:26:10 +09:00
|
|
|
Component.literal(customGuiPrefix).withStyle { it.withColor(0xFFFFFF) }
|
2025-12-29 17:27:49 +09:00
|
|
|
)
|
|
|
|
|
)
|
2025-12-29 15:44:48 +09:00
|
|
|
}
|