26 * 並び順は単一ではなく「優先順位リスト」として編集でき、先頭が第一ソート基準になる。
27 * クリックで並び順・件数上限・表示フラグを変更し、保存ボタンで永続化する。
28 */
29 class FindOptionGui(
30 private val player: Player,
31 private val profileName: String,
32 initial: FindOption,
20 *
21 * データは `plugins/AdvancedShopFinder/playerdata/<uuid>/config.json` に保存される。
22 */
23 object PlayerFindOptionUtils : KoinComponent {
24 private val plugin: AdvancedShopFinder by inject()
25
26 /** default プロファイルは常に存在し、削除できない。 */
94 buySortSlots.forEachIndexed { i, item -> pane.addItem(item, i + 1, 1) }
95 pane.addItem(sellLabelItem, 0, 2)
96 sellSortSlots.forEachIndexed { i, item -> pane.addItem(item, i + 1, 2) }
97 pane.addItem(buyLimitItem, 1, 3)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
95 pane.addItem(sellLabelItem, 0, 2)
96 sellSortSlots.forEachIndexed { i, item -> pane.addItem(item, i + 1, 2) }
97 pane.addItem(buyLimitItem, 1, 3)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
95 pane.addItem(sellLabelItem, 0, 2)
96 sellSortSlots.forEachIndexed { i, item -> pane.addItem(item, i + 1, 2) }
97 pane.addItem(buyLimitItem, 1, 3)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
96 sellSortSlots.forEachIndexed { i, item -> pane.addItem(item, i + 1, 2) }
97 pane.addItem(buyLimitItem, 1, 3)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
96 sellSortSlots.forEachIndexed { i, item -> pane.addItem(item, i + 1, 2) }
97 pane.addItem(buyLimitItem, 1, 3)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
97 pane.addItem(buyLimitItem, 1, 3)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
103 pane.addItem(closeItem, 6, 5)
97 pane.addItem(buyLimitItem, 1, 3)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
103 pane.addItem(closeItem, 6, 5)
98 pane.addItem(sellLimitItem, 3, 3)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
103 pane.addItem(closeItem, 6, 5)
104 gui.addPane(Slot.fromXY(0, 0), pane)
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
103 pane.addItem(closeItem, 6, 5)
104 gui.addPane(Slot.fromXY(0, 0), pane)
105 redraw()
99 pane.addItem(noStockItem, 5, 3)
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
103 pane.addItem(closeItem, 6, 5)
104 gui.addPane(Slot.fromXY(0, 0), pane)
105 redraw()
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
103 pane.addItem(closeItem, 6, 5)
104 gui.addPane(Slot.fromXY(0, 0), pane)
105 redraw()
106 }
100 pane.addItem(ownerMoneyItem, 7, 3)
101 pane.addItem(activeItem, 2, 5)
102 pane.addItem(saveItem, 4, 5)
103 pane.addItem(closeItem, 6, 5)
104 gui.addPane(Slot.fromXY(0, 0), pane)
105 redraw()
106 }
72 sender.sendMessage(message)
73 }
74
75 suspend fun processShops(shop: List<Shop>, sender: CommandSender, message: Component, sum: Int, options: FindOption, buying: Boolean): Pair<Component, Int> {
76 var filteredShops = shop.filter {
77 it.isBuying == buying && (withContext(Dispatchers.minecraft) {
78 if (buying) it.remainingSpace else it.remainingStock
108 async(Dispatchers.async) {
109 val luminescenceShulker = LuminescenceShulker()
110 luminescenceShulker.addTarget(sender)
111 filteredShops.stream().filter { getPlayerDistance(sender.location, it) < lightningDistance }.forEach {
112 luminescenceShulker.addBlock(it.location)
113 }
114 repeat(lightningCount) {
126
127 private suspend fun sendShopInfo(sender: CommandSender, shopChest: Shop): Component {
128 val nearPlace = getNearPlace(shopChest)
129 val nearTownDistance = hypot(nearPlace!!.x.toDouble() - shopChest.location.blockX, nearPlace.z.toDouble() - shopChest.location.blockZ)
130 val playerLocation = if (sender is Player) sender.location else Location(Bukkit.getWorld("world"), 0.0, 0.0, 0.0)
131 val distance = getPlayerDistance(playerLocation, shopChest)
132 val count = if (shopChest.isBuying) getBuyingShopCount(shopChest) else getSellingShopCount(shopChest)
127 private suspend fun sendShopInfo(sender: CommandSender, shopChest: Shop): Component {
128 val nearPlace = getNearPlace(shopChest)
129 val nearTownDistance = hypot(nearPlace!!.x.toDouble() - shopChest.location.blockX, nearPlace.z.toDouble() - shopChest.location.blockZ)
130 val playerLocation = if (sender is Player) sender.location else Location(Bukkit.getWorld("world"), 0.0, 0.0, 0.0)
131 val distance = getPlayerDistance(playerLocation, shopChest)
132 val count = if (shopChest.isBuying) getBuyingShopCount(shopChest) else getSellingShopCount(shopChest)
133 val tags = getTags(shopChest, count, distance, nearPlace, nearTownDistance)
156 }
157
158 private fun getNearestPlaceDistance(shopChest: Shop) =
159 hypot(getNearPlace(shopChest)!!.x.toDouble() - shopChest.location.blockX, getNearPlace(shopChest)!!.z.toDouble() - shopChest.location.blockZ)
160
161 private fun getTags(shopChest: Shop, count: String, distance: Int, nearPlace: PlaceData, nearTownDistance: Double): Array<TagResolver.Single> {
162 val mm = MiniMessage.miniMessage()
158 private fun getNearestPlaceDistance(shopChest: Shop) =
159 hypot(getNearPlace(shopChest)!!.x.toDouble() - shopChest.location.blockX, getNearPlace(shopChest)!!.z.toDouble() - shopChest.location.blockZ)
160
161 private fun getTags(shopChest: Shop, count: String, distance: Int, nearPlace: PlaceData, nearTownDistance: Double): Array<TagResolver.Single> {
162 val mm = MiniMessage.miniMessage()
163
164 return arrayOf(Placeholder.component("player-name", if (shopChest.isUnlimited) {
165 Component.text("アドミンショップ")
166 } else {
167 Component.text(Bukkit.getOfflinePlayer(shopChest.owner.uniqueId!!).name.toString())
168 }), Placeholder.component("price", Component.text(shopChest.price.toString())), Placeholder.component("shop-stacking-amount", Component.text(shopChest.shopStackingAmount.toString())), Placeholder.component("count", Component.text(count)), Placeholder.component("world", Component.text(shopChest.location.world.name)), Placeholder.component("x", Component.text(shopChest.location.blockX.toString())), Placeholder.component("y", Component.text(shopChest.location.blockY.toString())), Placeholder.component("z", Component.text(shopChest.location.blockZ.toString())), Placeholder.component("distance", Component.text(distance.toString())), Placeholder.component("near-town", mm.deserialize(nearPlace.placeName)), Placeholder.component("near-town-distance", Component.text(nearTownDistance.toInt().toString())), Placeholder.component("shop-type", mm.deserialize(if (shopChest.isBuying) "<color:red>買取" else "<color:green>販売")))
169 }
170
171 private suspend fun getSellingShopCount(shopChest: Shop) = withContext(Dispatchers.minecraft) {
40 }
41 val maps = translateManager.getTranslateMap(locale)
42 val item =
43 getKeys(maps, input)?.map { keys -> Material.entries.find { NamespacedKey.minecraft(it.translationKey()) == keys } }
44 ?: listOf(Material.matchMaterial(input))
45 val res = item.filter { it != null }.map { it!! }.toTypedArray()
46 return ArgumentParseResult.success(res)
46 data class ConfigData(
47 val placeData: List<PlaceData>,
48 val fuzzySearchLimit: Int = 50,
49 val format: String = "<shop-type>: オーナー:<green><player-name></green> 値段: <green><price>/<shop-stacking-amount></green>個 在庫: <green><count></green> \n座標: <yellow><world></yellow> x:<blue><x></blue> y:<blue><y></blue> z:<blue><z></blue> 距離: <green><distance></green>ブロック 最寄り: <near-town>から<green><near-town-distance></green>ブロック",
50 )
51
52 @Serializable
54 private val sellLimitItem = GuiItem(ItemStack(Material.HOPPER)) { event ->
55 event.isCancelled = true
56 val limit = option.limitAmountOption
57 option = option.copy(limitAmountOption = limit.copy(sellFindLimit = nextLimit(limit.sellFindLimit, event.click)))
58 redraw()
59 }
60 private val noStockItem = GuiItem(ItemStack(Material.BARRIER)) { event ->
36 return it
37 }
38 val classLoader = this.javaClass.classLoader
39 val inputStream: InputStream = classLoader.getResourceAsStream("minecraft/${locale.toString().lowercase()}.json")!!
40 val translateMap: Map<NamespacedKey, String> = json.decodeFromStream(MapSerializer(NamespacedKeySerializer, String.serializer()), inputStream)
41 translateData.data += (locale to translateMap)
42 return translateMap
37 }
38 val classLoader = this.javaClass.classLoader
39 val inputStream: InputStream = classLoader.getResourceAsStream("minecraft/${locale.toString().lowercase()}.json")!!
40 val translateMap: Map<NamespacedKey, String> = json.decodeFromStream(MapSerializer(NamespacedKeySerializer, String.serializer()), inputStream)
41 translateData.data += (locale to translateMap)
42 return translateMap
43 }
63 for (lang in preloadLang) {
64 val locale = Locale.of(lang)
65 val inputStream: InputStream = classLoader.getResourceAsStream("$resourceDir/${lang}.json")!!
66 val translateMap: Map<NamespacedKey, String> = json.decodeFromStream(MapSerializer(NamespacedKeySerializer, String.serializer()), inputStream)
67 dataMap[locale] = translateMap
68 }
69
56
57 }
58
59 }
91 }
92 return files
93 }
94 }
22 private val quickShop: QuickShopAPI by inject()
23
24 @Command("fuzzy-search <name>")
25 suspend fun fuzzySearch(
26 sender: CommandSender,
27 @Argument("name") name: String,
28 @Flag(value = "profile", aliases = ["p"]) profile: String?,
12 * - フラグ未指定の場合は使用中プロファイル(無ければデフォルト)。
13 * - フラグ指定かつ該当プロファイルが存在しない場合は、エラーメッセージを送信して null を返す。
14 */
15 suspend fun resolveFindOption(
16 sender: CommandSender,
17 profile: String?,
18 ): FindOption? {
17 val translateManager: TranslateManager by inject()
18 val config: ConfigData by inject()
19
20 override fun search(query: Pair<String, Locale>): Either<ShopSearchError, List<Shop>> {
21 val keys =
22 translateManager
23 .getTranslateMap(query.second)
21
22 override fun getSender(): CommandSender = sender
23
24 override fun getExecutor(): Entity? = if (sender is Entity) sender else null
25
26 override fun withLocation(p0: Location): CommandSourceStack = sender as CommandSourceStack
27