mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-27 04:26:27 -04:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec4dea23fc | ||
|
|
ed2ad46832 | ||
|
|
48ff865c55 | ||
|
|
e0abdb900d | ||
|
|
1b74c2906d | ||
|
|
d3a616e308 | ||
|
|
9a06463871 | ||
|
|
46b5a046ed | ||
|
|
ab811ead56 | ||
|
|
09a549ffcd | ||
|
|
b393293025 | ||
|
|
68a760a595 | ||
|
|
ae7bc6e681 | ||
|
|
ed007fe429 |
@@ -2,6 +2,7 @@
|
|||||||
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
|
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
|
||||||
|
|
||||||
### Additions
|
### Additions
|
||||||
|
- You can now drag to select multiple Pokémon inside the PC menu.
|
||||||
|
|
||||||
### Pokémon Added
|
### Pokémon Added
|
||||||
#### Generation 4
|
#### Generation 4
|
||||||
|
|||||||
@@ -566,6 +566,7 @@ object CobblemonNetwork {
|
|||||||
list.add(PacketRegisterInfo(SwapPartyPokemonPacket.ID, SwapPartyPokemonPacket::decode, SwapPartyPokemonHandler))
|
list.add(PacketRegisterInfo(SwapPartyPokemonPacket.ID, SwapPartyPokemonPacket::decode, SwapPartyPokemonHandler))
|
||||||
|
|
||||||
list.add(PacketRegisterInfo(MovePCPokemonPacket.ID, MovePCPokemonPacket::decode, MovePCPokemonHandler))
|
list.add(PacketRegisterInfo(MovePCPokemonPacket.ID, MovePCPokemonPacket::decode, MovePCPokemonHandler))
|
||||||
|
list.add(PacketRegisterInfo(MovePCPokemonBatchPacket.ID, MovePCPokemonBatchPacket::decode, MovePCPokemonBatchHandler))
|
||||||
list.add(PacketRegisterInfo(MovePartyPokemonPacket.ID, MovePartyPokemonPacket::decode, MovePartyPokemonHandler))
|
list.add(PacketRegisterInfo(MovePartyPokemonPacket.ID, MovePartyPokemonPacket::decode, MovePartyPokemonHandler))
|
||||||
|
|
||||||
list.add(PacketRegisterInfo(SwapPCPartyPokemonPacket.ID, SwapPCPartyPokemonPacket::decode, SwapPCPartyPokemonHandler))
|
list.add(PacketRegisterInfo(SwapPCPartyPokemonPacket.ID, SwapPCPartyPokemonPacket::decode, SwapPCPartyPokemonHandler))
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacke
|
|||||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
|
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
|
||||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
||||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
|
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
|
||||||
|
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket
|
||||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||||
import com.cobblemon.mod.common.util.DataKeys
|
import com.cobblemon.mod.common.util.DataKeys
|
||||||
import com.cobblemon.mod.common.util.asTranslated
|
import com.cobblemon.mod.common.util.asTranslated
|
||||||
@@ -270,6 +271,34 @@ open class PCStore(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun moveBatch(moves: List<Triple<UUID, PCPosition, PCPosition>>): Boolean {
|
||||||
|
if (moves.isEmpty() || moves.size > MovePCPokemonBatchPacket.MAX_MOVES) return false
|
||||||
|
if (moves.map { it.second }.toSet().size != moves.size ||
|
||||||
|
moves.map { it.third }.toSet().size != moves.size ||
|
||||||
|
moves.map { it.first }.toSet().size != moves.size
|
||||||
|
) return false
|
||||||
|
if (moves.any { (_, source, target) -> !isValidPosition(source) || !isValidPosition(target) }) return false
|
||||||
|
|
||||||
|
val sourcePositions = moves.mapTo(mutableSetOf()) { it.second }
|
||||||
|
val pokemonMoves = moves.map { (pokemonID, source, target) ->
|
||||||
|
val pokemon = get(source)?.takeIf { it.uuid == pokemonID } ?: return false
|
||||||
|
Triple(source, target, pokemon)
|
||||||
|
}
|
||||||
|
if (pokemonMoves.any { (_, target, _) -> get(target) != null && target !in sourcePositions }) return false
|
||||||
|
|
||||||
|
pokemonMoves.forEach { (source, _, _) -> setAtPosition(source, null) }
|
||||||
|
pokemonMoves.forEach { (_, target, pokemon) -> setAtPosition(target, pokemon) }
|
||||||
|
|
||||||
|
pokemonMoves.forEach { (_, _, pokemon) ->
|
||||||
|
sendPacketToObservers(RemoveClientPokemonPacket(this, pokemon.uuid))
|
||||||
|
}
|
||||||
|
pokemonMoves.forEach { (_, target, pokemon) ->
|
||||||
|
sendPacketToObservers(SetPCPokemonPacket(uuid, target) { pokemon })
|
||||||
|
}
|
||||||
|
pcChangeObservable.emit(Unit)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
override fun loadPositionFromNBT(nbt: CompoundTag): StoreCoordinates<PCPosition> {
|
override fun loadPositionFromNBT(nbt: CompoundTag): StoreCoordinates<PCPosition> {
|
||||||
return StoreCoordinates(this, PCPosition(nbt.getShort(DataKeys.STORE_BOX).toInt(), nbt.getByte(DataKeys.STORE_SLOT).toInt()))
|
return StoreCoordinates(this, PCPosition(nbt.getShort(DataKeys.STORE_BOX).toInt(), nbt.getByte(DataKeys.STORE_SLOT).toInt()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ object CobblemonClient {
|
|||||||
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
|
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
|
||||||
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
|
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
|
||||||
val rideControlsOverlay: RideControlsOverlay by lazy { RideControlsOverlay() }
|
val rideControlsOverlay: RideControlsOverlay by lazy { RideControlsOverlay() }
|
||||||
|
var pcHealthToggle: Boolean = false
|
||||||
|
|
||||||
fun onLogin() {
|
fun onLogin() {
|
||||||
clientPlayerData = ClientGeneralPlayerData()
|
clientPlayerData = ClientGeneralPlayerData()
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class BoxStorageSlot(
|
|||||||
|
|
||||||
val grabbedSlot = parent.grabbedSlot
|
val grabbedSlot = parent.grabbedSlot
|
||||||
return if (grabbedSlot == null) {
|
return if (grabbedSlot == null) {
|
||||||
true
|
val pokemon = getPokemon()
|
||||||
|
pokemon == null || !parent.isInMultiSelection(pokemon, position)
|
||||||
} else {
|
} else {
|
||||||
grabbedSlot.getPokemon() != getPokemon()
|
grabbedSlot.getPokemon() != getPokemon()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import net.minecraft.network.chat.Component
|
|||||||
import net.minecraft.sounds.SoundEvent
|
import net.minecraft.sounds.SoundEvent
|
||||||
import kotlin.isInitialized
|
import kotlin.isInitialized
|
||||||
import net.minecraft.resources.ResourceLocation
|
import net.minecraft.resources.ResourceLocation
|
||||||
|
import net.minecraft.util.FastColor
|
||||||
|
|
||||||
class PCGUI(
|
class PCGUI(
|
||||||
val pc: ClientPC,
|
val pc: ClientPC,
|
||||||
@@ -98,6 +99,8 @@ class PCGUI(
|
|||||||
private val buttonWallpaperResource = cobblemonResource("textures/gui/pc/pc_button_set_wallpaper.png")
|
private val buttonWallpaperResource = cobblemonResource("textures/gui/pc/pc_button_set_wallpaper.png")
|
||||||
private val buttonInfoArrow = cobblemonResource("textures/gui/pc/info_arrow.png")
|
private val buttonInfoArrow = cobblemonResource("textures/gui/pc/info_arrow.png")
|
||||||
|
|
||||||
|
private val buttonHealthToggleResource = cobblemonResource("textures/gui/pc/pc_button_toggle_health.png")
|
||||||
|
|
||||||
private val topSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_top.png")
|
private val topSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_top.png")
|
||||||
private val bottomSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_bottom.png")
|
private val bottomSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_bottom.png")
|
||||||
private val rightSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_right.png")
|
private val rightSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_right.png")
|
||||||
@@ -129,6 +132,55 @@ class PCGUI(
|
|||||||
var selectPointerOffsetIncrement = false
|
var selectPointerOffsetIncrement = false
|
||||||
var displayOptions = false
|
var displayOptions = false
|
||||||
|
|
||||||
|
private var dragStartX: Int? = null
|
||||||
|
private var dragStartY: Int? = null
|
||||||
|
private var dragEndX: Int? = null
|
||||||
|
private var dragEndY: Int? = null
|
||||||
|
private var selectionDragActive = false
|
||||||
|
|
||||||
|
data class SelectionArea(val left: Int, val top: Int, val right: Int, val bottom: Int)
|
||||||
|
|
||||||
|
private data class BoxGridBounds(val left: Int, val top: Int, val right: Int, val bottom: Int) {
|
||||||
|
fun contains(x: Int, y: Int) = x in left..< right && y >= top && y < bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBoxGridBounds(): BoxGridBounds {
|
||||||
|
val left = storageWidget.x
|
||||||
|
val top = storageWidget.y
|
||||||
|
return BoxGridBounds(
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
left + StorageWidget.SCREEN_WIDTH,
|
||||||
|
top + StorageWidget.SCREEN_HEIGHT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSelectionArea(): SelectionArea? {
|
||||||
|
if (!selectionDragActive) return null
|
||||||
|
val startX = dragStartX ?: return null
|
||||||
|
val startY = dragStartY ?: return null
|
||||||
|
val endX = dragEndX ?: return null
|
||||||
|
val endY = dragEndY ?: return null
|
||||||
|
val bounds = getBoxGridBounds()
|
||||||
|
if (!bounds.contains(startX, startY)) return null
|
||||||
|
|
||||||
|
val columns = 6
|
||||||
|
val rows = 5
|
||||||
|
val slotPitch = StorageSlot.SIZE + StorageWidget.BOX_SLOT_PADDING
|
||||||
|
val gridLeft = bounds.left + StorageWidget.BOX_SLOT_START_OFFSET_X
|
||||||
|
val gridTop = bounds.top + StorageWidget.BOX_SLOT_START_OFFSET_Y + if (displayOptions) 5 else 0
|
||||||
|
val startColumn = ((startX - gridLeft) / slotPitch).coerceIn(0, columns - 1)
|
||||||
|
val endColumn = (((endX.coerceIn(bounds.left, bounds.right - 1)) - gridLeft) / slotPitch).coerceIn(0, columns - 1)
|
||||||
|
val startRow = ((startY - gridTop) / slotPitch).coerceIn(0, rows - 1)
|
||||||
|
val endRow = (((endY.coerceIn(bounds.top, bounds.bottom - 1)) - gridTop) / slotPitch).coerceIn(0, rows - 1)
|
||||||
|
return SelectionArea(
|
||||||
|
gridLeft + minOf(startColumn, endColumn) * slotPitch, // left
|
||||||
|
gridTop + minOf(startRow, endRow) * slotPitch, // top
|
||||||
|
gridLeft + (maxOf(startColumn, endColumn) * slotPitch) + StorageSlot.SIZE, // right
|
||||||
|
gridTop + (maxOf(startRow, endRow) * slotPitch) + StorageSlot.SIZE // bottom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun renderBlurredBackground(delta: Float) {}
|
override fun renderBlurredBackground(delta: Float) {}
|
||||||
|
|
||||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||||
@@ -219,7 +271,6 @@ class PCGUI(
|
|||||||
storageWidget.setupStorageSlots()
|
storageWidget.setupStorageSlots()
|
||||||
for (button in optionButtons) {
|
for (button in optionButtons) {
|
||||||
button.visible = displayOptions
|
button.visible = displayOptions
|
||||||
button.highlighted = false
|
|
||||||
}
|
}
|
||||||
}.also { it.highlighted = displayOptions }
|
}.also { it.highlighted = displayOptions }
|
||||||
)
|
)
|
||||||
@@ -250,12 +301,28 @@ class PCGUI(
|
|||||||
configuration.showParty = isVisible
|
configuration.showParty = isVisible
|
||||||
wallpaperWidget.visible = !isVisible
|
wallpaperWidget.visible = !isVisible
|
||||||
(it as IconButton).highlighted = !isVisible
|
(it as IconButton).highlighted = !isVisible
|
||||||
if (!unseenWallpapers.isEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
|
if (unseenWallpapers.isNotEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
|
||||||
}.also {
|
}.also {
|
||||||
it.visible = displayOptions
|
it.visible = displayOptions
|
||||||
optionButtons.add(it)
|
optionButtons.add(it)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val healthButton = this.addRenderableWidget(IconButton(
|
||||||
|
pX = x + 230,
|
||||||
|
pY = y + 31,
|
||||||
|
buttonWidth = 20,
|
||||||
|
buttonHeight = 20,
|
||||||
|
resource = buttonHealthToggleResource,
|
||||||
|
label = "toggle_health_bars",
|
||||||
|
) {
|
||||||
|
CobblemonClient.pcHealthToggle = !CobblemonClient.pcHealthToggle
|
||||||
|
(it as IconButton).highlighted = CobblemonClient.pcHealthToggle
|
||||||
|
}.also {
|
||||||
|
it.visible = displayOptions
|
||||||
|
optionButtons.add(it)
|
||||||
|
})
|
||||||
|
healthButton.highlighted = CobblemonClient.pcHealthToggle
|
||||||
|
|
||||||
PokemonSortMode.entries.forEachIndexed { index, sortType ->
|
PokemonSortMode.entries.forEachIndexed { index, sortType ->
|
||||||
val typeName = sortType.name.lowercase()
|
val typeName = sortType.name.lowercase()
|
||||||
this.addRenderableWidget(IconButton(
|
this.addRenderableWidget(IconButton(
|
||||||
@@ -315,6 +382,20 @@ class PCGUI(
|
|||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun renderSelectionBox(context: GuiGraphics, area: SelectionArea) {
|
||||||
|
context.pose().pushPose()
|
||||||
|
context.pose().translate(0.0, 0.0, 1500.0)
|
||||||
|
val borderColor = FastColor.ARGB32.color(255, 205, 212, 255)
|
||||||
|
val bgColor = FastColor.ARGB32.color(75, 133, 183, 230)
|
||||||
|
val thickness = 1
|
||||||
|
context.fill(area.left, area.top, area.right, area.bottom, bgColor)
|
||||||
|
context.fill(area.left, area.top, area.right, area.top + thickness, borderColor)
|
||||||
|
context.fill(area.left, area.bottom - thickness, area.right, area.bottom, borderColor)
|
||||||
|
context.fill(area.left, area.top, area.left + thickness, area.bottom, borderColor)
|
||||||
|
context.fill(area.right - thickness, area.top, area.right, area.bottom, borderColor)
|
||||||
|
context.pose().popPose()
|
||||||
|
}
|
||||||
|
|
||||||
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
val matrices = context.pose()
|
val matrices = context.pose()
|
||||||
renderBackground(context, mouseX, mouseY, delta)
|
renderBackground(context, mouseX, mouseY, delta)
|
||||||
@@ -455,6 +536,20 @@ class PCGUI(
|
|||||||
shadow = true
|
shadow = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// var dexNo = pokemon.species.nationalPokedexNumber.toString()
|
||||||
|
// while (dexNo.length < 4) {
|
||||||
|
// dexNo = "0$dexNo"
|
||||||
|
// }
|
||||||
|
// drawScaledText(
|
||||||
|
// context = context,
|
||||||
|
// font = CobblemonResources.DEFAULT_LARGE,
|
||||||
|
// text = Component.literal(dexNo).bold(),
|
||||||
|
// x = x + 38,
|
||||||
|
// y = y + 1.5,
|
||||||
|
// shadow = true,
|
||||||
|
// scale = 0.85f
|
||||||
|
// )
|
||||||
|
|
||||||
drawScaledText(
|
drawScaledText(
|
||||||
context = context,
|
context = context,
|
||||||
font = CobblemonResources.DEFAULT_LARGE,
|
font = CobblemonResources.DEFAULT_LARGE,
|
||||||
@@ -685,12 +780,14 @@ class PCGUI(
|
|||||||
scale = SCALE
|
scale = SCALE
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!optionButtons.isEmpty() && displayOptions) {
|
if (optionButtons.isNotEmpty() && displayOptions) {
|
||||||
for (button in optionButtons) button.showAlt = hasShiftDown()
|
for (button in optionButtons) button.showAlt = hasShiftDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
super.render(context, mouseX, mouseY, delta)
|
super.render(context, mouseX, mouseY, delta)
|
||||||
|
|
||||||
|
getSelectionArea()?.let { renderSelectionBox(context, it) }
|
||||||
|
|
||||||
// Item Tooltip
|
// Item Tooltip
|
||||||
if (pokemon != null) {
|
if (pokemon != null) {
|
||||||
val displayedItem = if (showCosmeticItem) pokemon.cosmeticItem else pokemon.heldItemNoCopy()
|
val displayedItem = if (showCosmeticItem) pokemon.cosmeticItem else pokemon.heldItemNoCopy()
|
||||||
@@ -712,6 +809,20 @@ class PCGUI(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||||
|
storageWidget.clearPendingStorageSlotClick()
|
||||||
|
selectionDragActive = false
|
||||||
|
val clickX = mouseX.toInt()
|
||||||
|
val clickY = mouseY.toInt()
|
||||||
|
if (storageWidget.multiSelection == null && button == 0 &&
|
||||||
|
getBoxGridBounds().contains(clickX, clickY) &&
|
||||||
|
storageWidget.pastureWidget == null) {
|
||||||
|
dragStartX = clickX
|
||||||
|
dragStartY = clickY
|
||||||
|
} else {
|
||||||
|
dragStartX = null
|
||||||
|
dragStartY = null
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger child component function as they will need to check the entire screen click area
|
// Trigger child component function as they will need to check the entire screen click area
|
||||||
if (::boxNameWidget.isInitialized) boxNameWidget.mouseClicked(mouseX, mouseY, button)
|
if (::boxNameWidget.isInitialized) boxNameWidget.mouseClicked(mouseX, mouseY, button)
|
||||||
return super.mouseClicked(mouseX, mouseY, button)
|
return super.mouseClicked(mouseX, mouseY, button)
|
||||||
@@ -740,9 +851,43 @@ class PCGUI(
|
|||||||
storageWidget.pastureWidget?.let { pasture ->
|
storageWidget.pastureWidget?.let { pasture ->
|
||||||
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||||
}
|
}
|
||||||
|
if (button == 0 && storageWidget.pastureWidget == null) {
|
||||||
|
val startX = dragStartX
|
||||||
|
val startY = dragStartY
|
||||||
|
if (startX != null && startY != null) {
|
||||||
|
val offsetX = mouseX - startX
|
||||||
|
val offsetY = mouseY - startY
|
||||||
|
if (offsetX * offsetX + offsetY * offsetY >= 25.0) {
|
||||||
|
selectionDragActive = true
|
||||||
|
storageWidget.clearPendingStorageSlotClick()
|
||||||
|
dragEndX = mouseX.toInt()
|
||||||
|
dragEndY = mouseY.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||||
|
val handled = super.mouseReleased(mouseX, mouseY, button)
|
||||||
|
if (button == 0) {
|
||||||
|
if (selectionDragActive) {
|
||||||
|
dragEndX = mouseX.toInt()
|
||||||
|
dragEndY = mouseY.toInt()
|
||||||
|
storageWidget.updateSelection()
|
||||||
|
storageWidget.clearPendingStorageSlotClick()
|
||||||
|
} else {
|
||||||
|
storageWidget.completePendingStorageSlotClick(mouseX, mouseY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dragStartX = null
|
||||||
|
dragStartY = null
|
||||||
|
dragEndX = null
|
||||||
|
dragEndY = null
|
||||||
|
selectionDragActive = false
|
||||||
|
return handled
|
||||||
|
}
|
||||||
|
|
||||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||||
val boxNameFocused = this::boxNameWidget.isInitialized && boxNameWidget.isFocused
|
val boxNameFocused = this::boxNameWidget.isInitialized && boxNameWidget.isFocused
|
||||||
val filterFocused = this::filterWidget.isInitialized && filterWidget.isFocused
|
val filterFocused = this::filterWidget.isInitialized && filterWidget.isFocused
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.cobblemon.mod.common.client.gui.pc
|
||||||
|
|
||||||
|
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||||
|
|
||||||
|
data class PCMultiSelection(
|
||||||
|
private val grid: MutableList<MutableList<Pokemon?>> = mutableListOf(),
|
||||||
|
val sourceBox: Int = 0,
|
||||||
|
val sourceColumn: Int = 0,
|
||||||
|
val sourceRow: Int = 0
|
||||||
|
) {
|
||||||
|
fun getHorizontalSize(): Int = grid.firstOrNull()?.size ?: 0
|
||||||
|
fun getVerticalSize(): Int = grid.size
|
||||||
|
|
||||||
|
constructor(horizontalSize: Int, verticalSize: Int, sourceBox: Int, sourceColumn: Int, sourceRow: Int) : this(
|
||||||
|
MutableList(verticalSize) { MutableList<Pokemon?>(horizontalSize) { null } },
|
||||||
|
sourceBox,
|
||||||
|
sourceColumn,
|
||||||
|
sourceRow
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getPokemon(x: Int, y: Int): Pokemon? {
|
||||||
|
return grid[y][x]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPokemon(x: Int, y: Int, pokemon: Pokemon?) {
|
||||||
|
grid[y][x] = pokemon
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pokemonEntries(): List<Triple<Int, Int, Pokemon>> {
|
||||||
|
return grid.flatMapIndexed { y, row ->
|
||||||
|
row.mapIndexedNotNull { x, pokemon -> pokemon?.let { Triple(x, y, it) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPokemonCount(): Int = pokemonEntries().size
|
||||||
|
|
||||||
|
}
|
||||||
@@ -45,11 +45,13 @@ class ReleaseButton(
|
|||||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||||
textureHeight = HEIGHT * 2
|
textureHeight = HEIGHT * 2
|
||||||
)
|
)
|
||||||
|
val pokemonCount = parent.multiSelection?.getPokemonCount() ?: 0
|
||||||
|
|
||||||
drawScaledText(
|
drawScaledText(
|
||||||
context = context,
|
context = context,
|
||||||
font = CobblemonResources.DEFAULT_LARGE,
|
font = CobblemonResources.DEFAULT_LARGE,
|
||||||
text = lang("ui.pc.release").bold(),
|
text = if (pokemonCount > 1)
|
||||||
|
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
|
||||||
x = x + (WIDTH / 2),
|
x = x + (WIDTH / 2),
|
||||||
y = y + 3.5,
|
y = y + 3.5,
|
||||||
centered = true,
|
centered = true,
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ package com.cobblemon.mod.common.client.gui.pc
|
|||||||
|
|
||||||
import com.cobblemon.mod.common.Cobblemon
|
import com.cobblemon.mod.common.Cobblemon
|
||||||
import com.cobblemon.mod.common.api.gui.blitk
|
import com.cobblemon.mod.common.api.gui.blitk
|
||||||
|
import com.cobblemon.mod.common.client.CobblemonClient
|
||||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||||
|
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
|
||||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||||
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
|
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
|
||||||
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
|
|
||||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||||
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
||||||
@@ -22,13 +23,19 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
|||||||
import com.cobblemon.mod.common.util.cobblemonResource
|
import com.cobblemon.mod.common.util.cobblemonResource
|
||||||
import com.cobblemon.mod.common.util.lang
|
import com.cobblemon.mod.common.util.lang
|
||||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||||
|
import juuxel.adorn.util.Colors
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.client.gui.GuiGraphics
|
import net.minecraft.client.gui.GuiGraphics
|
||||||
import net.minecraft.client.gui.components.Button
|
import net.minecraft.client.gui.components.Button
|
||||||
|
import net.minecraft.client.renderer.RenderType
|
||||||
import net.minecraft.client.sounds.SoundManager
|
import net.minecraft.client.sounds.SoundManager
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
|
import net.minecraft.util.Mth
|
||||||
import org.joml.Quaternionf
|
import org.joml.Quaternionf
|
||||||
import org.joml.Vector3f
|
import org.joml.Vector3f
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
open class StorageSlot(
|
open class StorageSlot(
|
||||||
x: Int, y: Int,
|
x: Int, y: Int,
|
||||||
@@ -54,9 +61,17 @@ open class StorageSlot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
val selectionArea = parent.pcGui.getSelectionArea()
|
||||||
|
if (selectionArea != null && parent.getSelectedPokemon(selectionArea).contains(this)) {
|
||||||
|
val highlightColor = 0x40FFFFFF
|
||||||
|
context.fill(x + 1, y, x + (SIZE - 1), y + SIZE, highlightColor)
|
||||||
|
context.fill(x, y + 1, x + 1, y + (SIZE - 1), highlightColor)
|
||||||
|
context.fill(x + SIZE - 1, y + 1, x + SIZE, y + (SIZE - 1), highlightColor)
|
||||||
|
}
|
||||||
if (shouldRender()) {
|
if (shouldRender()) {
|
||||||
renderSlot(context, x, y, delta)
|
renderSlot(context, x, y, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renderSlot(context: GuiGraphics, posX: Int, posY: Int, partialTicks: Float) {
|
fun renderSlot(context: GuiGraphics, posX: Int, posY: Int, partialTicks: Float) {
|
||||||
@@ -130,6 +145,23 @@ open class StorageSlot(
|
|||||||
matrixStack = matrices
|
matrixStack = matrices
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pokemon health
|
||||||
|
// Taken from vanilla durability rendering
|
||||||
|
val currentHealth = pokemon.currentHealth
|
||||||
|
val maxHealth = pokemon.maxHealth.toFloat()
|
||||||
|
if (currentHealth < maxHealth && CobblemonClient.pcHealthToggle) {
|
||||||
|
val damageValue = maxHealth - currentHealth
|
||||||
|
val f = max(0.0f, (maxHealth - (maxHealth - currentHealth)) / maxHealth)
|
||||||
|
val color = Mth.hsvToRgb(f / 3.0f, 1.0f, 1.0f)
|
||||||
|
val totalWidth = 12
|
||||||
|
val width = (totalWidth - damageValue * totalWidth / pokemon.maxHealth.toFloat()).roundToInt();
|
||||||
|
val k = posX + 1
|
||||||
|
val l = posY + 6
|
||||||
|
val bgColor = if (this is PartyStorageSlot) Colors.BLACK else 0xFF3E3E3E.toInt()
|
||||||
|
context.fill(RenderType.guiOverlay(), k, l, k + totalWidth, l + 1, bgColor)
|
||||||
|
context.fill(RenderType.guiOverlay(), k, l, k + width, l + 1, color or bgColor)
|
||||||
|
}
|
||||||
matrices.popPose()
|
matrices.popPose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.pc
|
|||||||
import com.cobblemon.mod.common.CobblemonNetwork
|
import com.cobblemon.mod.common.CobblemonNetwork
|
||||||
import com.cobblemon.mod.common.CobblemonSounds
|
import com.cobblemon.mod.common.CobblemonSounds
|
||||||
import com.cobblemon.mod.common.api.gui.blitk
|
import com.cobblemon.mod.common.api.gui.blitk
|
||||||
import com.cobblemon.mod.common.api.storage.StorePosition
|
|
||||||
import com.cobblemon.mod.common.api.storage.party.PartyPosition
|
import com.cobblemon.mod.common.api.storage.party.PartyPosition
|
||||||
import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||||
import com.cobblemon.mod.common.api.text.bold
|
import com.cobblemon.mod.common.api.text.bold
|
||||||
@@ -39,8 +38,9 @@ import net.minecraft.client.gui.components.Button
|
|||||||
import net.minecraft.client.gui.screens.Screen
|
import net.minecraft.client.gui.screens.Screen
|
||||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||||
import net.minecraft.network.chat.Component
|
import net.minecraft.network.chat.Component
|
||||||
import net.minecraft.resources.ResourceLocation
|
|
||||||
import net.minecraft.sounds.SoundEvent
|
import net.minecraft.sounds.SoundEvent
|
||||||
|
import java.util.UUID
|
||||||
|
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket.PCPokemonMove
|
||||||
|
|
||||||
class StorageWidget(
|
class StorageWidget(
|
||||||
pX: Int, pY: Int,
|
pX: Int, pY: Int,
|
||||||
@@ -77,9 +77,14 @@ class StorageWidget(
|
|||||||
|
|
||||||
var displayConfirmRelease = false
|
var displayConfirmRelease = false
|
||||||
var screenLoaded = false
|
var screenLoaded = false
|
||||||
var selectedPosition: StorePosition? = null
|
var selectedPosition: PartyPosition? = null
|
||||||
var grabbedSlot: GrabbedStorageSlot? = null
|
var grabbedSlot: GrabbedStorageSlot? = null
|
||||||
|
|
||||||
|
var multiSelection: PCMultiSelection? = null
|
||||||
|
private val multiGrabbedSlots = mutableMapOf<UUID, GrabbedStorageSlot>()
|
||||||
|
private var pendingStorageSlotClick: Button? = null
|
||||||
|
private var movePending = false
|
||||||
|
|
||||||
var box = 0
|
var box = 0
|
||||||
set(value) {
|
set(value) {
|
||||||
field =
|
field =
|
||||||
@@ -113,16 +118,22 @@ class StorageWidget(
|
|||||||
subKey = "ui.generic.yes",
|
subKey = "ui.generic.yes",
|
||||||
onPress = {
|
onPress = {
|
||||||
if (canDeleteSelected() && displayConfirmRelease) {
|
if (canDeleteSelected() && displayConfirmRelease) {
|
||||||
val position = selectedPosition ?: return@ReleaseConfirmButton
|
val selection = multiSelection
|
||||||
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
|
if (selection != null) {
|
||||||
|
selection.pokemonEntries().forEach { (column, row, pokemon) ->
|
||||||
|
val position = PCPosition(
|
||||||
|
selection.sourceBox,
|
||||||
|
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
|
||||||
|
)
|
||||||
|
CobblemonNetwork.sendToServer(ReleasePCPokemonPacket(pokemon.uuid, position))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val position = selectedPosition ?: return@ReleaseConfirmButton
|
||||||
|
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
|
||||||
|
|
||||||
val packet = when (position) {
|
CobblemonNetwork.sendToServer(ReleasePartyPokemonPacket(pokemon.uuid, position))
|
||||||
is PartyPosition -> ReleasePartyPokemonPacket(pokemon.uuid, position)
|
|
||||||
is PCPosition -> ReleasePCPokemonPacket(pokemon.uuid, position)
|
|
||||||
else -> return@ReleaseConfirmButton
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CobblemonNetwork.sendToServer(packet)
|
|
||||||
playSound(CobblemonSounds.PC_RELEASE)
|
playSound(CobblemonSounds.PC_RELEASE)
|
||||||
resetSelected()
|
resetSelected()
|
||||||
displayConfirmRelease = false
|
displayConfirmRelease = false
|
||||||
@@ -149,9 +160,8 @@ class StorageWidget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun canDeleteSelected(): Boolean {
|
fun canDeleteSelected(): Boolean {
|
||||||
return !(selectedPosition is PartyPosition && party.filterNotNull().size <= 1)
|
multiSelection?.let { return it.pokemonEntries().isNotEmpty() }
|
||||||
&& selectedPosition != null
|
return party.filterNotNull().size > 1 && selectedPosition != null && grabbedSlot != null
|
||||||
&& grabbedSlot != null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupStorageSlots() {
|
fun setupStorageSlots() {
|
||||||
@@ -170,7 +180,7 @@ class StorageWidget(
|
|||||||
parent = this,
|
parent = this,
|
||||||
pc = pc,
|
pc = pc,
|
||||||
position = PCPosition(box, index),
|
position = PCPosition(box, index),
|
||||||
onPress = { this.onStorageSlotClicked(it) }
|
onPress = { pendingStorageSlotClick = it }
|
||||||
).also { widget ->
|
).also { widget ->
|
||||||
this.addWidget(widget)
|
this.addWidget(widget)
|
||||||
this.boxSlots.add(widget)
|
this.boxSlots.add(widget)
|
||||||
@@ -200,7 +210,7 @@ class StorageWidget(
|
|||||||
parent = this,
|
parent = this,
|
||||||
party = party,
|
party = party,
|
||||||
position = PartyPosition(partyIndex),
|
position = PartyPosition(partyIndex),
|
||||||
onPress = { this.onStorageSlotClicked(it) }
|
onPress = { pendingStorageSlotClick = it }
|
||||||
).also { widget ->
|
).also { widget ->
|
||||||
this.addWidget(widget)
|
this.addWidget(widget)
|
||||||
this.partySlots.add(widget)
|
this.partySlots.add(widget)
|
||||||
@@ -210,14 +220,117 @@ class StorageWidget(
|
|||||||
|
|
||||||
private fun getSelectedPokemon(): Pokemon? {
|
private fun getSelectedPokemon(): Pokemon? {
|
||||||
val selectedPosition = this.selectedPosition ?: return null
|
val selectedPosition = this.selectedPosition ?: return null
|
||||||
return when (selectedPosition) {
|
return party.get(selectedPosition)
|
||||||
is PCPosition -> pc.get(selectedPosition)
|
}
|
||||||
is PartyPosition -> party.get(selectedPosition)
|
|
||||||
else -> null
|
fun getSelectedPokemon(area: PCGUI.SelectionArea): List<BoxStorageSlot> {
|
||||||
|
return boxSlots.filter { slot ->
|
||||||
|
slot.x < area.right && slot.x + StorageSlot.SIZE > area.left &&
|
||||||
|
slot.y < area.bottom && slot.y + StorageSlot.SIZE > area.top &&
|
||||||
|
slot.getPokemon()?.let(pcGui.search::passes) == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearPendingStorageSlotClick() {
|
||||||
|
pendingStorageSlotClick = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun completePendingStorageSlotClick(mouseX: Double, mouseY: Double) {
|
||||||
|
val button = pendingStorageSlotClick ?: return
|
||||||
|
pendingStorageSlotClick = null
|
||||||
|
if (button.isMouseOver(mouseX, mouseY)) {
|
||||||
|
onStorageSlotClicked(button)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSelection() {
|
||||||
|
val area = pcGui.getSelectionArea()
|
||||||
|
if (area == null) {
|
||||||
|
multiSelection = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedPosition = null
|
||||||
|
grabbedSlot = null
|
||||||
|
pcGui.setPreviewPokemon(null)
|
||||||
|
|
||||||
|
val selectedSlots = getSelectedPokemon(area)
|
||||||
|
if (selectedSlots.isEmpty()) {
|
||||||
|
multiSelection = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val minColumn = selectedSlots.minOf { it.position.slot % 6 }
|
||||||
|
val maxColumn = selectedSlots.maxOf { it.position.slot % 6 }
|
||||||
|
val minRow = selectedSlots.minOf { it.position.slot / 6 }
|
||||||
|
val maxRow = selectedSlots.maxOf { it.position.slot / 6 }
|
||||||
|
val selection = PCMultiSelection(maxColumn - minColumn + 1, maxRow - minRow + 1, box, minColumn, minRow)
|
||||||
|
|
||||||
|
selectedSlots.forEach { slot ->
|
||||||
|
val pokemon = slot.getPokemon() ?: return@forEach
|
||||||
|
val slotIndex = slot.position.slot
|
||||||
|
val column = slotIndex % 6
|
||||||
|
val row = slotIndex / 6
|
||||||
|
if (column in minColumn..maxColumn && row in minRow..maxRow) {
|
||||||
|
selection.setPokemon(column - minColumn, row - minRow, pokemon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playSound(CobblemonSounds.PC_GRAB)
|
||||||
|
|
||||||
|
multiSelection = selection
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun moveMultiSelectionTo(destination: PCPosition): Boolean {
|
||||||
|
val selection = multiSelection ?: return false
|
||||||
|
|
||||||
|
val width = selection.getHorizontalSize()
|
||||||
|
val height = selection.getVerticalSize()
|
||||||
|
val destinationColumn = destination.slot % 6
|
||||||
|
val destinationRow = destination.slot / 6
|
||||||
|
if (destinationColumn + width > 6 || destinationRow + height > 5) return false
|
||||||
|
|
||||||
|
val moves = mutableListOf<PCPokemonMove>()
|
||||||
|
val selectedSources = mutableSetOf<PCPosition>()
|
||||||
|
for (row in 0 until height) {
|
||||||
|
for (column in 0 until width) {
|
||||||
|
val pokemon = selection.getPokemon(column, row) ?: continue
|
||||||
|
val source = PCPosition(selection.sourceBox, (selection.sourceRow + row) * 6 + selection.sourceColumn + column)
|
||||||
|
val target = PCPosition(destination.box, (destinationRow + row) * 6 + destinationColumn + column)
|
||||||
|
selectedSources.add(source)
|
||||||
|
moves.add(PCPokemonMove(pokemon.uuid, source, target))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moves.isEmpty()) return false
|
||||||
|
|
||||||
|
for (row in 0 until height) {
|
||||||
|
for (column in 0 until width) {
|
||||||
|
if (selection.getPokemon(column, row) == null) continue
|
||||||
|
val target = PCPosition(destination.box, (destinationRow + row) * 6 + destinationColumn + column)
|
||||||
|
val displacedPokemon = pc.get(target) ?: continue
|
||||||
|
if (target in selectedSources) continue
|
||||||
|
|
||||||
|
val displacedSource = PCPosition(
|
||||||
|
selection.sourceBox,
|
||||||
|
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
|
||||||
|
)
|
||||||
|
if (pc.get(displacedSource) != null && displacedSource !in selectedSources) return false
|
||||||
|
moves.add(PCPokemonMove(displacedPokemon.uuid, target, displacedSource))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moves.map { it.newPosition }.toSet().size != moves.size) return false
|
||||||
|
MovePCPokemonBatchPacket(moves).sendToServer()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isInMultiSelection(pokemon: Pokemon, position: PCPosition): Boolean {
|
||||||
|
val selection = multiSelection ?: return false
|
||||||
|
return selection.sourceBox == position.box && selection.pokemonEntries().any { it.third == pokemon }
|
||||||
|
}
|
||||||
|
|
||||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
updatePendingMove()
|
||||||
val matrices = context.pose()
|
val matrices = context.pose()
|
||||||
// Party Label
|
// Party Label
|
||||||
if (pcGui.configuration.showParty) {
|
if (pcGui.configuration.showParty) {
|
||||||
@@ -240,11 +353,14 @@ class StorageWidget(
|
|||||||
shadow = true
|
shadow = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val pokemonCount = multiSelection?.getPokemonCount() ?: 0
|
||||||
|
|
||||||
if (canDeleteSelected() && displayConfirmRelease) {
|
if (canDeleteSelected() && displayConfirmRelease) {
|
||||||
drawScaledText(
|
drawScaledText(
|
||||||
context = context,
|
context = context,
|
||||||
font = CobblemonResources.DEFAULT_LARGE,
|
font = CobblemonResources.DEFAULT_LARGE,
|
||||||
text = lang("ui.pc.release").bold(),
|
text = if (pokemonCount > 1)
|
||||||
|
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
|
||||||
x = x + 223,
|
x = x + 223,
|
||||||
y = y + 119,
|
y = y + 119,
|
||||||
centered = true
|
centered = true
|
||||||
@@ -308,6 +424,7 @@ class StorageWidget(
|
|||||||
slot.render(context, mouseX, mouseY, delta)
|
slot.render(context, mouseX, mouseY, delta)
|
||||||
val pokemon = slot.getPokemon()
|
val pokemon = slot.getPokemon()
|
||||||
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
||||||
|
&& multiSelection == null
|
||||||
&& pokemon != null && pokemon != pcGui.previewPokemon
|
&& pokemon != null && pokemon != pcGui.previewPokemon
|
||||||
) {
|
) {
|
||||||
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: false)
|
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: false)
|
||||||
@@ -324,6 +441,7 @@ class StorageWidget(
|
|||||||
slot.render(context, mouseX, mouseY, delta)
|
slot.render(context, mouseX, mouseY, delta)
|
||||||
val pokemon = slot.getPokemon()
|
val pokemon = slot.getPokemon()
|
||||||
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
||||||
|
&& multiSelection == null
|
||||||
&& pokemon != null && pokemon != pcGui.previewPokemon
|
&& pokemon != null && pokemon != pcGui.previewPokemon
|
||||||
) {
|
) {
|
||||||
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: true)
|
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: true)
|
||||||
@@ -335,10 +453,32 @@ class StorageWidget(
|
|||||||
pastureWidget?.render(context, mouseX, mouseY, delta)
|
pastureWidget?.render(context, mouseX, mouseY, delta)
|
||||||
matrices.pushPose()
|
matrices.pushPose()
|
||||||
matrices.translate(0.0, 0.0, 1000.0)
|
matrices.translate(0.0, 0.0, 1000.0)
|
||||||
grabbedSlot?.render(context, mouseX, mouseY, delta)
|
renderSelectedPokemon(context, mouseX, mouseY, delta)
|
||||||
matrices.popPose()
|
matrices.popPose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun renderSelectedPokemon(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||||
|
if (movePending) return
|
||||||
|
val selection = multiSelection
|
||||||
|
if (selection == null) {
|
||||||
|
grabbedSlot?.render(context, mouseX, mouseY, delta)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val slotOffset = StorageSlot.SIZE + BOX_SLOT_PADDING
|
||||||
|
selection.pokemonEntries().forEach { (column, row, pokemon) ->
|
||||||
|
val grabbed = multiGrabbedSlots.getOrPut(pokemon.uuid) {
|
||||||
|
GrabbedStorageSlot(0, 0, this, pokemon)
|
||||||
|
}
|
||||||
|
grabbed.render(
|
||||||
|
context,
|
||||||
|
mouseX + column * slotOffset,
|
||||||
|
mouseY + row * slotOffset,
|
||||||
|
delta
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
||||||
if (displayConfirmRelease) {
|
if (displayConfirmRelease) {
|
||||||
if (releaseYesButton.isHovered(pMouseX, pMouseY)) releaseYesButton.mouseClicked(pMouseX, pMouseY, pButton)
|
if (releaseYesButton.isHovered(pMouseX, pMouseY)) releaseYesButton.mouseClicked(pMouseX, pMouseY, pButton)
|
||||||
@@ -371,23 +511,55 @@ class StorageWidget(
|
|||||||
else -> return
|
else -> return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val clickedPokemon = when (clickedPosition) {
|
||||||
|
is PCPosition -> pc.get(clickedPosition)
|
||||||
|
is PartyPosition -> party.get(clickedPosition)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
multiSelection?.let { selection ->
|
||||||
|
when (clickedPosition) {
|
||||||
|
is PCPosition -> {
|
||||||
|
if (clickedPosition.box == selection.sourceBox &&
|
||||||
|
clickedPosition.slot == selection.sourceRow * 6 + selection.sourceColumn
|
||||||
|
) {
|
||||||
|
resetSelected()
|
||||||
|
} else if (moveMultiSelectionTo(clickedPosition)) {
|
||||||
|
playSound(CobblemonSounds.PC_DROP)
|
||||||
|
movePending = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PartyPosition -> {
|
||||||
|
val selectedPokemon = selection.pokemonEntries().singleOrNull() ?: return
|
||||||
|
val (column, row, pokemon) = selectedPokemon
|
||||||
|
val source = PCPosition(
|
||||||
|
selection.sourceBox,
|
||||||
|
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
|
||||||
|
)
|
||||||
|
if (pc.get(source) !== pokemon) return
|
||||||
|
val packet = clickedPokemon?.let {
|
||||||
|
SwapPCPartyPokemonPacket(it.uuid, clickedPosition, pokemon.uuid, source)
|
||||||
|
} ?: MovePCPokemonToPartyPacket(pokemon.uuid, source, clickedPosition)
|
||||||
|
packet.sendToServer()
|
||||||
|
playSound(CobblemonSounds.PC_DROP)
|
||||||
|
movePending = true
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Reset release confirmation
|
// Reset release confirmation
|
||||||
displayConfirmRelease = false
|
displayConfirmRelease = false
|
||||||
|
|
||||||
// Clicking on itself, so unselect position
|
// Clicking on itself, so unselect position
|
||||||
if (this.selectedPosition != null && this.selectedPosition == clickedPosition) {
|
if (selectedPosition == clickedPosition) {
|
||||||
if (grabbedSlot != null) playSound(CobblemonSounds.PC_DROP)
|
if (grabbedSlot != null) playSound(CobblemonSounds.PC_DROP)
|
||||||
resetSelected()
|
resetSelected()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for selecting a Pokémon
|
// Check for selecting a Pokémon
|
||||||
val clickedPokemon = when(button) {
|
|
||||||
is BoxStorageSlot -> pc.get(clickedPosition as PCPosition)
|
|
||||||
is PartyStorageSlot -> party.get(clickedPosition as PartyPosition)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
val selectOverride = pcGui.configuration.selectOverride
|
val selectOverride = pcGui.configuration.selectOverride
|
||||||
if (selectOverride != null) {
|
if (selectOverride != null) {
|
||||||
selectOverride(pcGui, clickedPosition, clickedPokemon)
|
selectOverride(pcGui, clickedPosition, clickedPokemon)
|
||||||
@@ -420,59 +592,47 @@ class StorageWidget(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedPosition = clickedPosition
|
if (clickedPosition is PCPosition) {
|
||||||
this.pcGui.setPreviewPokemon(clickedPokemon, pcGui.isPreviewInParty ?: false)
|
val column = clickedPosition.slot % 6
|
||||||
grabbedSlot = GrabbedStorageSlot(
|
val row = clickedPosition.slot / 6
|
||||||
x = button.x,
|
multiSelection = PCMultiSelection(1, 1, clickedPosition.box, column, row).also {
|
||||||
y = button.y,
|
it.setPokemon(0, 0, clickedPokemon)
|
||||||
parent = this,
|
}
|
||||||
pokemon = clickedPokemon
|
playSound(CobblemonSounds.PC_GRAB)
|
||||||
)
|
return
|
||||||
playSound(CobblemonSounds.PC_GRAB)
|
} else if (clickedPosition is PartyPosition) {
|
||||||
|
this.selectedPosition = clickedPosition
|
||||||
|
this.pcGui.setPreviewPokemon(clickedPokemon, pcGui.isPreviewInParty ?: false)
|
||||||
|
grabbedSlot = GrabbedStorageSlot(
|
||||||
|
x = button.x,
|
||||||
|
y = button.y,
|
||||||
|
parent = this,
|
||||||
|
pokemon = clickedPokemon
|
||||||
|
)
|
||||||
|
playSound(CobblemonSounds.PC_GRAB)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle movement within the Box
|
val sourcePosition = selectedPosition ?: return
|
||||||
val selectedPokemon = when(this.selectedPosition) {
|
val selectedPokemon = party.get(sourcePosition) ?: return
|
||||||
is PCPosition -> pc.get(this.selectedPosition as PCPosition)
|
|
||||||
is PartyPosition -> party.get(this.selectedPosition as PartyPosition)
|
|
||||||
else -> null
|
|
||||||
} ?: return
|
|
||||||
|
|
||||||
// Box to Box
|
|
||||||
if (this.selectedPosition is PCPosition && clickedPosition is PCPosition) {
|
|
||||||
val packet = clickedPokemon?.let { SwapPCPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
|
|
||||||
?: MovePCPokemonPacket(selectedPokemon.uuid, selectedPosition as PCPosition, clickedPosition)
|
|
||||||
packet.sendToServer()
|
|
||||||
playSound(CobblemonSounds.PC_DROP)
|
|
||||||
resetSelected()
|
|
||||||
}
|
|
||||||
// Box to Party
|
|
||||||
else if (this.selectedPosition is PCPosition && clickedPosition is PartyPosition) {
|
|
||||||
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(clickedPokemon.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
|
|
||||||
?: MovePCPokemonToPartyPacket(selectedPokemon.uuid, this.selectedPosition as PCPosition, clickedPosition)
|
|
||||||
packet.sendToServer()
|
|
||||||
playSound(CobblemonSounds.PC_DROP)
|
|
||||||
resetSelected()
|
|
||||||
}
|
|
||||||
// Party to Box
|
// Party to Box
|
||||||
else if (this.selectedPosition is PartyPosition && clickedPosition is PCPosition) {
|
if (clickedPosition is PCPosition) {
|
||||||
if (ServerSettings.preventCompletePartyDeposit && this.party.filterNotNull().size == 1 && clickedPokemon == null) {
|
if (ServerSettings.preventCompletePartyDeposit && this.party.filterNotNull().size == 1 && clickedPokemon == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPokemon.uuid, clickedPosition) }
|
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPokemon.uuid, clickedPosition) }
|
||||||
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPosition)
|
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
|
||||||
packet.sendToServer()
|
packet.sendToServer()
|
||||||
playSound(CobblemonSounds.PC_DROP)
|
playSound(CobblemonSounds.PC_DROP)
|
||||||
resetSelected()
|
movePending = true
|
||||||
}
|
}
|
||||||
// Party to Party
|
// Party to Party
|
||||||
else if (this.selectedPosition is PartyPosition && clickedPosition is PartyPosition) {
|
else if (clickedPosition is PartyPosition) {
|
||||||
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PartyPosition) }
|
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, sourcePosition) }
|
||||||
?: MovePartyPokemonPacket(selectedPokemon.uuid, selectedPosition as PartyPosition, clickedPosition)
|
?: MovePartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
|
||||||
packet.sendToServer()
|
packet.sendToServer()
|
||||||
playSound(CobblemonSounds.PC_DROP)
|
playSound(CobblemonSounds.PC_DROP)
|
||||||
resetSelected()
|
movePending = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,6 +640,36 @@ class StorageWidget(
|
|||||||
fun resetSelected() {
|
fun resetSelected() {
|
||||||
selectedPosition = null
|
selectedPosition = null
|
||||||
grabbedSlot = null
|
grabbedSlot = null
|
||||||
|
multiSelection = null
|
||||||
|
multiGrabbedSlots.clear()
|
||||||
pcGui.setPreviewPokemon(null)
|
pcGui.setPreviewPokemon(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// annoyingly complex solution, but server/client desync causes flashing when moving pokemon. this should fix it
|
||||||
|
// if you can think of a simpler solution, feel free to implement it
|
||||||
|
private fun updatePendingMove() {
|
||||||
|
if (!movePending) return
|
||||||
|
val selection = multiSelection
|
||||||
|
|
||||||
|
if (selection != null) {
|
||||||
|
val updated = selection.pokemonEntries().any { (column, row, pokemon) ->
|
||||||
|
val source = PCPosition(selection.sourceBox, (selection.sourceRow + row) * 6 + selection.sourceColumn + column)
|
||||||
|
pc.get(source)?.uuid != pokemon.uuid
|
||||||
|
}
|
||||||
|
if (updated) {
|
||||||
|
movePending = false
|
||||||
|
resetSelected()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val source = selectedPosition
|
||||||
|
val pokemon = grabbedSlot?.getPokemon()
|
||||||
|
if (source != null && pokemon != null) {
|
||||||
|
if (party.get(source)?.uuid != pokemon.uuid) {
|
||||||
|
movePending = false
|
||||||
|
resetSelected()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Cobblemon Contributors
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://www.mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.cobblemon.mod.common.net.messages.server.storage.pc
|
||||||
|
|
||||||
|
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||||
|
import com.cobblemon.mod.common.api.storage.pc.PCPosition
|
||||||
|
import com.cobblemon.mod.common.util.cobblemonResource
|
||||||
|
import com.cobblemon.mod.common.util.readPCPosition
|
||||||
|
import com.cobblemon.mod.common.util.writePCPosition
|
||||||
|
import java.util.UUID
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||||
|
|
||||||
|
class MovePCPokemonBatchPacket(val moves: List<PCPokemonMove>) : NetworkPacket<MovePCPokemonBatchPacket> {
|
||||||
|
override val id = ID
|
||||||
|
|
||||||
|
data class PCPokemonMove(
|
||||||
|
val pokemonID: UUID,
|
||||||
|
val oldPosition: PCPosition,
|
||||||
|
val newPosition: PCPosition
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||||
|
buffer.writeVarInt(moves.size)
|
||||||
|
moves.forEach { move ->
|
||||||
|
buffer.writeUUID(move.pokemonID)
|
||||||
|
buffer.writePCPosition(move.oldPosition)
|
||||||
|
buffer.writePCPosition(move.newPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ID = cobblemonResource("move_pc_pokemon_batch")
|
||||||
|
const val MAX_MOVES = 60
|
||||||
|
|
||||||
|
fun decode(buffer: RegistryFriendlyByteBuf): MovePCPokemonBatchPacket {
|
||||||
|
val size = buffer.readVarInt()
|
||||||
|
require(size in 1..MAX_MOVES) { "Invalid batch PC move size: $size" }
|
||||||
|
return MovePCPokemonBatchPacket(
|
||||||
|
List(size) {
|
||||||
|
PCPokemonMove(
|
||||||
|
pokemonID = buffer.readUUID(),
|
||||||
|
oldPosition = buffer.readPCPosition(),
|
||||||
|
newPosition = buffer.readPCPosition()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Cobblemon Contributors
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://www.mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.cobblemon.mod.common.net.serverhandling.storage.pc
|
||||||
|
|
||||||
|
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||||
|
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
|
||||||
|
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
|
||||||
|
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket
|
||||||
|
import net.minecraft.server.MinecraftServer
|
||||||
|
import net.minecraft.server.level.ServerPlayer
|
||||||
|
|
||||||
|
object MovePCPokemonBatchHandler : ServerNetworkPacketHandler<MovePCPokemonBatchPacket> {
|
||||||
|
override fun handle(packet: MovePCPokemonBatchPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||||
|
val pc = PCLinkManager.getPC(player) ?: return run { ClosePCPacket(null).sendToPlayer(player) }
|
||||||
|
pc.moveBatch(packet.moves.map { Triple(it.pokemonID, it.oldPosition, it.newPosition) })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1299,6 +1299,7 @@
|
|||||||
"cobblemon.ui.pc.filter.tooltip": "Format: pikachu shiny held_item lvl=1...",
|
"cobblemon.ui.pc.filter.tooltip": "Format: pikachu shiny held_item lvl=1...",
|
||||||
"cobblemon.ui.pc.options": "Options",
|
"cobblemon.ui.pc.options": "Options",
|
||||||
"cobblemon.ui.pc.release": "Release",
|
"cobblemon.ui.pc.release": "Release",
|
||||||
|
"cobblemon.ui.pc.release_all": "Release All",
|
||||||
"cobblemon.ui.sort.name": "Sort by name",
|
"cobblemon.ui.sort.name": "Sort by name",
|
||||||
"cobblemon.ui.sort.level": "Sort by level",
|
"cobblemon.ui.sort.level": "Sort by level",
|
||||||
"cobblemon.ui.sort.pokedex_number": "Sort by Pokédex number",
|
"cobblemon.ui.sort.pokedex_number": "Sort by Pokédex number",
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 548 B |
Reference in New Issue
Block a user