mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-11-04 23:38:53 -05:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cb77a54d5 | ||
|
|
04553d658f | ||
|
|
99e0a26d7b | ||
|
|
71753df513 | ||
|
|
729f080f6e | ||
|
|
13eb57afb1 | ||
|
|
72b6cb06ff | ||
|
|
74a57eb972 | ||
|
|
7246c7f8e6 | ||
|
|
cd05f741ae | ||
|
|
00222d929d | ||
|
|
b8f4637cb3 | ||
|
|
9f572250f5 | ||
|
|
0927d6fa43 | ||
|
|
a293b3d71f | ||
|
|
162b1cc68f | ||
|
|
066a6b56f7 | ||
|
|
7687833add | ||
|
|
cb9ba82fcc | ||
|
|
4221993f7d | ||
|
|
a616c25c8c | ||
|
|
6ce4bf5673 | ||
|
|
1b652ba99f | ||
|
|
11b59df386 | ||
|
|
ac2b068bf3 | ||
|
|
8e8f346e52 | ||
|
|
ee8133356e | ||
|
|
d78e114b3b | ||
|
|
14ab259eee | ||
|
|
a8f78e3fd6 | ||
|
|
96ae054aae | ||
|
|
80666530db | ||
|
|
b5e912a13f | ||
|
|
945f6b5f84 | ||
|
|
bf6c668850 |
No files matched your search
+145
-12
@@ -2,28 +2,161 @@
|
||||
## [1.8.5 (MONTH xth, 2026)](#1-8-5)
|
||||
|
||||
### Additions
|
||||
- You can now drag to select multiple Pokémon inside the PC menu.
|
||||
- Added trades for Saccharine Saplings, Hearty Grains, Chipped Pot, and Masterpiece Teacup to the Wandering Trader.
|
||||
- Added brewing recipe for Throat Spray.
|
||||
- Added Berry Tree scanning for Pokedex
|
||||
- Added Datapackable Pokedex Scanning for Block Entities
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
|
||||
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Added missing crops to Botany Pots integration
|
||||
- Added a way to obtain Porygon by inserting the Upgrade or Dubious Disc into a data monitor
|
||||
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
|
||||
- Added Configs for Infinite Use TMs and Unlocking all TMs
|
||||
- Added Type Gem blocks
|
||||
- Added Type Gem Clusters
|
||||
- Added TMs
|
||||
- Added TM Machine
|
||||
- Added Deepslate Core Block
|
||||
- Added /givetm Command
|
||||
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` tracking for capturing Alpha Pokémon.
|
||||
- Added Move Dex to Pokédex
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks
|
||||
- Added intrinsic scaling to all mons (+-5%). Some rendering cases, such as shoulder mounting, revert to 1.0 scaling to preserve animation accuracy.
|
||||
- Added scaling to "baby" Pokémon (smaller (-20%) at level 1 → normally sized at level 10)
|
||||
- Added conditional seats for riding. Seats can be gated by molang such as "is_alpha".
|
||||
- Added Alpha Pokémon
|
||||
- Added alpha scaling (alphas are larger than their normal counterparts)
|
||||
- Alphas will always lead herds and will spawn in herds
|
||||
- Alphas spawn with two random TM moves from their learnset
|
||||
- Alpha Pokémon will always defend themselves from players that attack them
|
||||
- Added alpha level matching and stat boost (based on level)
|
||||
- Added pitch-down and reverb to alpha cries
|
||||
- Added alpha Pokémon bait effect
|
||||
- Added alpha eye rendering including eye overlay, bloom, eye trail, and particles.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
- Added 49 new habitat structures. 34 as typical Minecraft structures and 15 as structures that get placed as biome features to allow for more natural placement in generation. Structures placed as biome features will not be able to be located with the `/locate structure` command.
|
||||
- Vanilla village house template pools have been adjusted to account for the new abandoned village structure habitat. Weights have changed within the JSON file for every piece to ensure too many habitats don't generate in a single village.
|
||||
- Added 10 new ruin structures.
|
||||
- rooted_arch_ruins & crumbling_arch_ruins have been transferred over to biome features, allowing for better placement in generation.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
|
||||
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- A new Biome tag called `#cobblemon:is_stony_beach` has been added.
|
||||
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
|
||||
- 1 New shipwreck cove structure called the "magma_shipwreck_cove" has been added.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
|
||||
- Added 7 new advancements
|
||||
|
||||
### New rideable Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
|
||||
### Pokémon Added
|
||||
#### Generation 4
|
||||
|
||||
#### Generation 5
|
||||
|
||||
#### Generation 6
|
||||
|
||||
#### Generation 7
|
||||
|
||||
#### Generation 8
|
||||
|
||||
#### Generation 9
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
- Skitty
|
||||
- Delcatty
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Audino
|
||||
- Rockruff
|
||||
- Lycanroc
|
||||
- Snom
|
||||
- Frosmoth
|
||||
- Applin
|
||||
- Flapple
|
||||
- Appletun
|
||||
- Dipplin
|
||||
- Hydrapple
|
||||
- Sandshrew (Alolan)
|
||||
- Sandslash (Alolan)
|
||||
- Seviper
|
||||
- Zangoose
|
||||
- Stunky
|
||||
- Skuntank
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
- Kingambit
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Zubat
|
||||
- Golbat
|
||||
- Crobat
|
||||
- Pidgey
|
||||
- Pidgeotto
|
||||
- Pidgeot
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Sableye
|
||||
- Mawile
|
||||
- Nacli
|
||||
- Naclestack
|
||||
- Garganacl
|
||||
- Skarmory
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Drampa
|
||||
- Archeops
|
||||
- Skarmory
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Shellos
|
||||
- Skitty
|
||||
- Joltik
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Flygon
|
||||
|
||||
#### Added sounds to the following moves
|
||||
- Absorb
|
||||
- Aurora Beam
|
||||
- Double Team
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Charge
|
||||
- Flame Wheel
|
||||
- Giga Drain
|
||||
- Ice Punch
|
||||
- Leech Life
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Poison Gas
|
||||
- Rock Throw
|
||||
- Scary Face
|
||||
- Seed bomb
|
||||
- Smokescreen
|
||||
- String Shot
|
||||
- Tail Whip
|
||||
- Added the `healersSetSpawn` gamerule. When enabled, using a healing machine will set your spawnpoint.
|
||||
- A Pokémon holding a Power item now gains 1 point in the corresponding riding stat every two minutes while being ridden. The interval is configurable, and can be disabled with `-1`.
|
||||
- Added sounds to Gigaton Hammer and Pyro Ball
|
||||
- Added stepsounds to Venusaur, Blastoise, Dewgong, Rhydon, Rhyhorn, Parasect and Mr. Mime.
|
||||
|
||||
### Changes
|
||||
- Amethyst now grows from Typegem core blocks like typegems do
|
||||
- Updated the recipe category for the Create compatibility EXP Candy recipe
|
||||
- Added No Man's Lands' Honeyed Apple to the Leaves Leftovers tag
|
||||
|
||||
### Fixes
|
||||
- Fixed Ponigiri food values for AppleSkin integration
|
||||
|
||||
@@ -566,7 +566,6 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(SwapPartyPokemonPacket.ID, SwapPartyPokemonPacket::decode, SwapPartyPokemonHandler))
|
||||
|
||||
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(SwapPCPartyPokemonPacket.ID, SwapPCPartyPokemonPacket::decode, SwapPCPartyPokemonHandler))
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
package com.cobblemon.mod.common.api.riding.stats
|
||||
|
||||
import com.cobblemon.mod.common.api.cooking.Flavour
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.world.item.Item
|
||||
|
||||
/**
|
||||
* A stat that is used for riding Pokémon. The exact implementation of each of these depends on the [com.cobblemon.mod.common.api.riding.controller.RideController].
|
||||
@@ -20,13 +23,14 @@ import net.minecraft.network.chat.MutableComponent
|
||||
* @since February 17th, 2025
|
||||
*/
|
||||
enum class RidingStat(
|
||||
val flavour: Flavour
|
||||
val flavour: Flavour,
|
||||
val powerItem: TagKey<Item>
|
||||
) {
|
||||
ACCELERATION(Flavour.SPICY),
|
||||
SKILL(Flavour.DRY),
|
||||
SPEED(Flavour.SWEET),
|
||||
STAMINA(Flavour.SOUR),
|
||||
JUMP(Flavour.BITTER);
|
||||
ACCELERATION(Flavour.SPICY, CobblemonItemTags.POWER_BRACER),
|
||||
SKILL(Flavour.DRY, CobblemonItemTags.POWER_BELT),
|
||||
SPEED(Flavour.SWEET, CobblemonItemTags.POWER_ANKLET),
|
||||
STAMINA(Flavour.SOUR, CobblemonItemTags.POWER_WEIGHT),
|
||||
JUMP(Flavour.BITTER, CobblemonItemTags.POWER_BAND);
|
||||
|
||||
companion object {
|
||||
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
|
||||
|
||||
@@ -29,7 +29,6 @@ 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.SetPCPokemonPacket
|
||||
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.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
@@ -271,34 +270,6 @@ 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> {
|
||||
return StoreCoordinates(this, PCPosition(nbt.getShort(DataKeys.STORE_BOX).toInt(), nbt.getByte(DataKeys.STORE_SLOT).toInt()))
|
||||
}
|
||||
|
||||
@@ -15,13 +15,19 @@ import com.cobblemon.mod.common.api.berry.GrowthPoint
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.pokedex.scanner.BlockScanData
|
||||
import com.cobblemon.mod.common.pokedex.scanner.Frame
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableBlockEntity
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.ResourceLocationException
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
@@ -35,11 +41,10 @@ import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.CropBlock.UPDATE_CLIENTS
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
|
||||
class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.BERRY, pos, state) {
|
||||
class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.BERRY, pos, state), ScannableBlockEntity {
|
||||
lateinit var berryIdentifier: ResourceLocation
|
||||
private val ticksPerMinute = 1200
|
||||
var renderState: RenderState? = null
|
||||
@@ -375,6 +380,22 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
return stageTimer <= 0
|
||||
}
|
||||
|
||||
override fun resolveBlockScan(): BlockScanData {
|
||||
val berryName = berry()?.item()?.let { ItemStack(it).hoverName.copy() }
|
||||
?: berryIdentifier.toString().text()
|
||||
|
||||
val frames = mutableSetOf<Frame>(
|
||||
Frame(1, lang("ui.pokedex.scan.berry.stage", blockState.getValue(BerryBlock.AGE))),
|
||||
Frame(2, berryName)
|
||||
)
|
||||
|
||||
if (mulchVariant != MulchVariant.NONE) {
|
||||
frames.add(Frame(3, Component.translatable("item.cobblemon.${mulchVariant.serializedName}_mulch")))
|
||||
}
|
||||
|
||||
return BlockScanData(frames)
|
||||
}
|
||||
|
||||
//Using a similar approach to ComputerCraft, implement this client side
|
||||
interface RenderState : AutoCloseable {
|
||||
var needsRebuild: Boolean
|
||||
|
||||
@@ -111,7 +111,6 @@ object CobblemonClient {
|
||||
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
|
||||
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
|
||||
val rideControlsOverlay: RideControlsOverlay by lazy { RideControlsOverlay() }
|
||||
var pcHealthToggle: Boolean = false
|
||||
|
||||
fun onLogin() {
|
||||
clientPlayerData = ClientGeneralPlayerData()
|
||||
|
||||
@@ -29,8 +29,7 @@ class BoxStorageSlot(
|
||||
|
||||
val grabbedSlot = parent.grabbedSlot
|
||||
return if (grabbedSlot == null) {
|
||||
val pokemon = getPokemon()
|
||||
pokemon == null || !parent.isInMultiSelection(pokemon, position)
|
||||
true
|
||||
} else {
|
||||
grabbedSlot.getPokemon() != getPokemon()
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ import net.minecraft.network.chat.Component
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import kotlin.isInitialized
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.FastColor
|
||||
|
||||
class PCGUI(
|
||||
val pc: ClientPC,
|
||||
@@ -99,8 +98,6 @@ class PCGUI(
|
||||
private val buttonWallpaperResource = cobblemonResource("textures/gui/pc/pc_button_set_wallpaper.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 bottomSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_bottom.png")
|
||||
private val rightSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_right.png")
|
||||
@@ -132,55 +129,6 @@ class PCGUI(
|
||||
var selectPointerOffsetIncrement = 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 renderMenuBackground(context: GuiGraphics) {}
|
||||
@@ -271,6 +219,7 @@ class PCGUI(
|
||||
storageWidget.setupStorageSlots()
|
||||
for (button in optionButtons) {
|
||||
button.visible = displayOptions
|
||||
button.highlighted = false
|
||||
}
|
||||
}.also { it.highlighted = displayOptions }
|
||||
)
|
||||
@@ -301,28 +250,12 @@ class PCGUI(
|
||||
configuration.showParty = isVisible
|
||||
wallpaperWidget.visible = !isVisible
|
||||
(it as IconButton).highlighted = !isVisible
|
||||
if (unseenWallpapers.isNotEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
|
||||
if (!unseenWallpapers.isEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
|
||||
}.also {
|
||||
it.visible = displayOptions
|
||||
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 ->
|
||||
val typeName = sortType.name.lowercase()
|
||||
this.addRenderableWidget(IconButton(
|
||||
@@ -382,20 +315,6 @@ class PCGUI(
|
||||
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) {
|
||||
val matrices = context.pose()
|
||||
renderBackground(context, mouseX, mouseY, delta)
|
||||
@@ -536,20 +455,6 @@ class PCGUI(
|
||||
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(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
@@ -780,14 +685,12 @@ class PCGUI(
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
if (optionButtons.isNotEmpty() && displayOptions) {
|
||||
if (!optionButtons.isEmpty() && displayOptions) {
|
||||
for (button in optionButtons) button.showAlt = hasShiftDown()
|
||||
}
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
getSelectionArea()?.let { renderSelectionBox(context, it) }
|
||||
|
||||
// Item Tooltip
|
||||
if (pokemon != null) {
|
||||
val displayedItem = if (showCosmeticItem) pokemon.cosmeticItem else pokemon.heldItemNoCopy()
|
||||
@@ -809,20 +712,6 @@ class PCGUI(
|
||||
}
|
||||
|
||||
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
|
||||
if (::boxNameWidget.isInitialized) boxNameWidget.mouseClicked(mouseX, mouseY, button)
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
@@ -851,43 +740,9 @@ class PCGUI(
|
||||
storageWidget.pastureWidget?.let { pasture ->
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
val boxNameFocused = this::boxNameWidget.isInitialized && boxNameWidget.isFocused
|
||||
val filterFocused = this::filterWidget.isInitialized && filterWidget.isFocused
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
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,13 +45,11 @@ class ReleaseButton(
|
||||
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
val pokemonCount = parent.multiSelection?.getPokemonCount() ?: 0
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = if (pokemonCount > 1)
|
||||
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
|
||||
text = lang("ui.pc.release").bold(),
|
||||
x = x + (WIDTH / 2),
|
||||
y = y + 3.5,
|
||||
centered = true,
|
||||
|
||||
@@ -10,11 +10,10 @@ package com.cobblemon.mod.common.client.gui.pc
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
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.PokemonGUIAnimationStyle
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
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.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
||||
@@ -23,19 +22,13 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import juuxel.adorn.util.Colors
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.util.Mth
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
open class StorageSlot(
|
||||
x: Int, y: Int,
|
||||
@@ -61,17 +54,9 @@ open class StorageSlot(
|
||||
}
|
||||
|
||||
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()) {
|
||||
renderSlot(context, x, y, delta)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun renderSlot(context: GuiGraphics, posX: Int, posY: Int, partialTicks: Float) {
|
||||
@@ -145,23 +130,6 @@ open class StorageSlot(
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.gui.pc
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
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.pc.PCPosition
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
@@ -38,9 +39,8 @@ import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import java.util.UUID
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket.PCPokemonMove
|
||||
|
||||
class StorageWidget(
|
||||
pX: Int, pY: Int,
|
||||
@@ -77,14 +77,9 @@ class StorageWidget(
|
||||
|
||||
var displayConfirmRelease = false
|
||||
var screenLoaded = false
|
||||
var selectedPosition: PartyPosition? = null
|
||||
var selectedPosition: StorePosition? = 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
|
||||
set(value) {
|
||||
field =
|
||||
@@ -118,22 +113,16 @@ class StorageWidget(
|
||||
subKey = "ui.generic.yes",
|
||||
onPress = {
|
||||
if (canDeleteSelected() && displayConfirmRelease) {
|
||||
val selection = multiSelection
|
||||
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 position = selectedPosition ?: return@ReleaseConfirmButton
|
||||
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
|
||||
|
||||
CobblemonNetwork.sendToServer(ReleasePartyPokemonPacket(pokemon.uuid, position))
|
||||
val packet = when (position) {
|
||||
is PartyPosition -> ReleasePartyPokemonPacket(pokemon.uuid, position)
|
||||
is PCPosition -> ReleasePCPokemonPacket(pokemon.uuid, position)
|
||||
else -> return@ReleaseConfirmButton
|
||||
}
|
||||
|
||||
CobblemonNetwork.sendToServer(packet)
|
||||
playSound(CobblemonSounds.PC_RELEASE)
|
||||
resetSelected()
|
||||
displayConfirmRelease = false
|
||||
@@ -160,8 +149,9 @@ class StorageWidget(
|
||||
}
|
||||
|
||||
fun canDeleteSelected(): Boolean {
|
||||
multiSelection?.let { return it.pokemonEntries().isNotEmpty() }
|
||||
return party.filterNotNull().size > 1 && selectedPosition != null && grabbedSlot != null
|
||||
return !(selectedPosition is PartyPosition && party.filterNotNull().size <= 1)
|
||||
&& selectedPosition != null
|
||||
&& grabbedSlot != null
|
||||
}
|
||||
|
||||
fun setupStorageSlots() {
|
||||
@@ -180,7 +170,7 @@ class StorageWidget(
|
||||
parent = this,
|
||||
pc = pc,
|
||||
position = PCPosition(box, index),
|
||||
onPress = { pendingStorageSlotClick = it }
|
||||
onPress = { this.onStorageSlotClicked(it) }
|
||||
).also { widget ->
|
||||
this.addWidget(widget)
|
||||
this.boxSlots.add(widget)
|
||||
@@ -210,7 +200,7 @@ class StorageWidget(
|
||||
parent = this,
|
||||
party = party,
|
||||
position = PartyPosition(partyIndex),
|
||||
onPress = { pendingStorageSlotClick = it }
|
||||
onPress = { this.onStorageSlotClicked(it) }
|
||||
).also { widget ->
|
||||
this.addWidget(widget)
|
||||
this.partySlots.add(widget)
|
||||
@@ -220,117 +210,14 @@ class StorageWidget(
|
||||
|
||||
private fun getSelectedPokemon(): Pokemon? {
|
||||
val selectedPosition = this.selectedPosition ?: return null
|
||||
return party.get(selectedPosition)
|
||||
}
|
||||
|
||||
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
|
||||
return when (selectedPosition) {
|
||||
is PCPosition -> pc.get(selectedPosition)
|
||||
is PartyPosition -> party.get(selectedPosition)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
updatePendingMove()
|
||||
val matrices = context.pose()
|
||||
// Party Label
|
||||
if (pcGui.configuration.showParty) {
|
||||
@@ -353,14 +240,11 @@ class StorageWidget(
|
||||
shadow = true
|
||||
)
|
||||
|
||||
val pokemonCount = multiSelection?.getPokemonCount() ?: 0
|
||||
|
||||
if (canDeleteSelected() && displayConfirmRelease) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = if (pokemonCount > 1)
|
||||
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
|
||||
text = lang("ui.pc.release").bold(),
|
||||
x = x + 223,
|
||||
y = y + 119,
|
||||
centered = true
|
||||
@@ -424,7 +308,6 @@ class StorageWidget(
|
||||
slot.render(context, mouseX, mouseY, delta)
|
||||
val pokemon = slot.getPokemon()
|
||||
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
||||
&& multiSelection == null
|
||||
&& pokemon != null && pokemon != pcGui.previewPokemon
|
||||
) {
|
||||
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: false)
|
||||
@@ -441,7 +324,6 @@ class StorageWidget(
|
||||
slot.render(context, mouseX, mouseY, delta)
|
||||
val pokemon = slot.getPokemon()
|
||||
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
|
||||
&& multiSelection == null
|
||||
&& pokemon != null && pokemon != pcGui.previewPokemon
|
||||
) {
|
||||
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: true)
|
||||
@@ -453,32 +335,10 @@ class StorageWidget(
|
||||
pastureWidget?.render(context, mouseX, mouseY, delta)
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 1000.0)
|
||||
renderSelectedPokemon(context, mouseX, mouseY, delta)
|
||||
grabbedSlot?.render(context, mouseX, mouseY, delta)
|
||||
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 {
|
||||
if (displayConfirmRelease) {
|
||||
if (releaseYesButton.isHovered(pMouseX, pMouseY)) releaseYesButton.mouseClicked(pMouseX, pMouseY, pButton)
|
||||
@@ -511,55 +371,23 @@ class StorageWidget(
|
||||
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
|
||||
displayConfirmRelease = false
|
||||
|
||||
// Clicking on itself, so unselect position
|
||||
if (selectedPosition == clickedPosition) {
|
||||
if (this.selectedPosition != null && this.selectedPosition == clickedPosition) {
|
||||
if (grabbedSlot != null) playSound(CobblemonSounds.PC_DROP)
|
||||
resetSelected()
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
if (selectOverride != null) {
|
||||
selectOverride(pcGui, clickedPosition, clickedPokemon)
|
||||
@@ -592,47 +420,59 @@ class StorageWidget(
|
||||
}
|
||||
}
|
||||
|
||||
if (clickedPosition is PCPosition) {
|
||||
val column = clickedPosition.slot % 6
|
||||
val row = clickedPosition.slot / 6
|
||||
multiSelection = PCMultiSelection(1, 1, clickedPosition.box, column, row).also {
|
||||
it.setPokemon(0, 0, clickedPokemon)
|
||||
}
|
||||
playSound(CobblemonSounds.PC_GRAB)
|
||||
return
|
||||
} 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)
|
||||
}
|
||||
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 {
|
||||
val sourcePosition = selectedPosition ?: return
|
||||
val selectedPokemon = party.get(sourcePosition) ?: return
|
||||
// Handle movement within the Box
|
||||
val selectedPokemon = when(this.selectedPosition) {
|
||||
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
|
||||
if (clickedPosition is PCPosition) {
|
||||
else if (this.selectedPosition is PartyPosition && clickedPosition is PCPosition) {
|
||||
if (ServerSettings.preventCompletePartyDeposit && this.party.filterNotNull().size == 1 && clickedPokemon == null) {
|
||||
return
|
||||
}
|
||||
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPokemon.uuid, clickedPosition) }
|
||||
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
|
||||
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPokemon.uuid, clickedPosition) }
|
||||
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPosition)
|
||||
packet.sendToServer()
|
||||
playSound(CobblemonSounds.PC_DROP)
|
||||
movePending = true
|
||||
resetSelected()
|
||||
}
|
||||
// Party to Party
|
||||
else if (clickedPosition is PartyPosition) {
|
||||
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, sourcePosition) }
|
||||
?: MovePartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
|
||||
else if (this.selectedPosition is PartyPosition && clickedPosition is PartyPosition) {
|
||||
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PartyPosition) }
|
||||
?: MovePartyPokemonPacket(selectedPokemon.uuid, selectedPosition as PartyPosition, clickedPosition)
|
||||
packet.sendToServer()
|
||||
playSound(CobblemonSounds.PC_DROP)
|
||||
movePending = true
|
||||
resetSelected()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,36 +480,6 @@ class StorageWidget(
|
||||
fun resetSelected() {
|
||||
selectedPosition = null
|
||||
grabbedSlot = null
|
||||
multiSelection = null
|
||||
multiGrabbedSlots.clear()
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+161
-82
@@ -19,11 +19,16 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_HEIG
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_WIDTH
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokedex.scanner.BlockScanData
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableBlockEntity
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.PokemonSizeCategory
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.DeltaTracker
|
||||
@@ -82,13 +87,45 @@ class PokedexScannerRenderer {
|
||||
fun infoFrameResource(isLeft: Boolean, tier: Int): ResourceLocation = cobblemonResource("textures/gui/pokedex/scan/scan_info_frame_${if (isLeft) "left" else "right"}_$tier.png")
|
||||
}
|
||||
|
||||
private var lastFocusedBlockTypeId: ResourceLocation? = null
|
||||
|
||||
private fun getBlockEntityInfo(entity: BlockEntity): BlockScanData? {
|
||||
val scannable = entity as? ScannableBlockEntity ?: return null
|
||||
return scannable.resolveBlockScan()
|
||||
}
|
||||
|
||||
fun renderInfoFrames(graphics: GuiGraphics, poseStack: PoseStack, usageContext: PokedexUsageContext, centerX: Int, centerY: Int, opacity: Float) {
|
||||
if (usageContext.focusIntervals > 0) {
|
||||
var infoDisplayedCounter = 0
|
||||
usageContext.availableInfoFrames.forEachIndexed {index, isLeftSide ->
|
||||
if (isLeftSide !== null) {
|
||||
if (infoDisplayedCounter > 2 && !usageContext.isPokemonInFocusOwned) return@forEachIndexed
|
||||
val isPokemonFocus = usageContext.scannableEntityInFocus != null
|
||||
if (isPokemonFocus && infoDisplayedCounter > 1 && !usageContext.isPokemonInFocusOwned) return@forEachIndexed
|
||||
infoDisplayedCounter++
|
||||
|
||||
val blockEntityInFocus: BlockEntity? = usageContext.blockEntityInFocus
|
||||
val pokedexEntityData = usageContext.scannableEntityInFocus?.resolvePokemonScan()
|
||||
|
||||
// store the block entity typeId so our close animation knows what to show
|
||||
if (pokedexEntityData != null) {
|
||||
lastFocusedBlockTypeId = null
|
||||
} else if (blockEntityInFocus != null) {
|
||||
lastFocusedBlockTypeId = BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntityInFocus.type)
|
||||
} else if (usageContext.focusIntervals <= 0f) {
|
||||
lastFocusedBlockTypeId = null
|
||||
}
|
||||
|
||||
val scanInfo = blockEntityInFocus?.let { getBlockEntityInfo(it) }
|
||||
|
||||
// if a block entity is being focused on make sure we only show the frames needed
|
||||
if (pokedexEntityData == null && scanInfo != null) {
|
||||
val hasFrameForSlot = scanInfo.frames.any { it.slot == infoDisplayedCounter }
|
||||
if (!hasFrameForSlot) {
|
||||
return@forEachIndexed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Frames
|
||||
val isInnerFrame = index == 1 || index == 2
|
||||
val frameHeight = if (isInnerFrame) INNER_INFO_FRAME_HEIGHT else OUTER_INFO_FRAME_HEIGHT
|
||||
@@ -110,77 +147,79 @@ class PokedexScannerRenderer {
|
||||
val yOffsetText = when(index) { 0 -> 5; 1 -> 4; 2 -> 8; 3 -> 42; else -> 0 }
|
||||
|
||||
// Text
|
||||
if (usageContext.focusIntervals == PokedexUsageContext.FOCUS_INTERVALS && usageContext.scannableEntityInFocus != null) {
|
||||
val pokedexEntityData = usageContext.scannableEntityInFocus?.resolvePokemonScan() ?: return
|
||||
|
||||
if (infoDisplayedCounter == 1) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.lv.number", pokedexEntityData.pokemon.level).bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
|
||||
if (infoDisplayedCounter == 2) {
|
||||
val hasTrainer = (usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)?.ownerUUID !== null
|
||||
val speciesName = pokedexEntityData.getApparentSpecies().translatedName.bold()
|
||||
var yOffsetName = if (hasTrainer) 2 else 0
|
||||
if (hasTrainer) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
text = lang("ui.pokedex.scan.trainer_owned"),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - yOffsetName,
|
||||
shadow = true,
|
||||
centered = true,
|
||||
scale = 0.5F
|
||||
)
|
||||
}
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = speciesName,
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText + yOffsetName,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
|
||||
val gender = pokedexEntityData.pokemon.gender
|
||||
val speciesNameWidth = Minecraft.getInstance().font.width(speciesName.font(CobblemonResources.DEFAULT_LARGE))
|
||||
if (gender != Gender.GENDERLESS) {
|
||||
val isMale = gender == Gender.MALE
|
||||
val textSymbol = if (isMale) "♂".text().bold() else "♀".text().bold()
|
||||
if (usageContext.focusIntervals == PokedexUsageContext.FOCUS_INTERVALS && (pokedexEntityData != null || blockEntityInFocus != null)) {
|
||||
if (pokedexEntityData != null) {
|
||||
if (infoDisplayedCounter == 1) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = textSymbol,
|
||||
x = centerX + xOffset + xOffsetText + 2 + (speciesNameWidth / 2),
|
||||
text = lang("ui.lv.number", pokedexEntityData.pokemon.level).bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
|
||||
if (infoDisplayedCounter == 2) {
|
||||
val hasTrainer =
|
||||
(usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)?.ownerUUID !== null
|
||||
val speciesName = pokedexEntityData.getApparentSpecies().translatedName.bold()
|
||||
var yOffsetName = if (hasTrainer) 2 else 0
|
||||
if (hasTrainer) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
text = lang("ui.pokedex.scan.trainer_owned"),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - yOffsetName,
|
||||
shadow = true,
|
||||
centered = true,
|
||||
scale = 0.5F
|
||||
)
|
||||
}
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = speciesName,
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText + yOffsetName,
|
||||
colour = if (isMale) 0x32CBFF else 0xFC5454,
|
||||
shadow = true
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
|
||||
val gender = pokedexEntityData.pokemon.gender
|
||||
val speciesNameWidth =
|
||||
Minecraft.getInstance().font.width(speciesName.font(CobblemonResources.DEFAULT_LARGE))
|
||||
if (gender != Gender.GENDERLESS) {
|
||||
val isMale = gender == Gender.MALE
|
||||
val textSymbol = if (isMale) "♂".text().bold() else "♀".text().bold()
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = textSymbol,
|
||||
x = centerX + xOffset + xOffsetText + 2 + (speciesNameWidth / 2),
|
||||
y = centerY + yOffset + yOffsetText + yOffsetName,
|
||||
colour = if (isMale) 0x32CBFF else 0xFC5454,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
if (usageContext.isPokemonInFocusOwned) {
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = caughtIndicator,
|
||||
x = (centerX + xOffset + xOffsetText - 7 - (speciesNameWidth / 2)) / SCALE,
|
||||
y = (centerY + yOffset + yOffsetText + yOffsetName + 2) / SCALE,
|
||||
height = 10,
|
||||
width = 10,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (usageContext.isPokemonInFocusOwned) {
|
||||
blitk(
|
||||
matrixStack = poseStack,
|
||||
texture = caughtIndicator,
|
||||
x = (centerX + xOffset + xOffsetText - 7 - (speciesNameWidth / 2)) / SCALE,
|
||||
y = (centerY + yOffset + yOffsetText + yOffsetName + 2) / SCALE,
|
||||
height = 10,
|
||||
width = 10,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (infoDisplayedCounter == 3) {
|
||||
val pokemon = pokedexEntityData.pokemon
|
||||
if (infoDisplayedCounter == 3) {
|
||||
val pokemon = pokedexEntityData.pokemon
|
||||
val entityAlpha = (usageContext.scannableEntityInFocus?.resolveEntityScan() as? PokemonEntity)
|
||||
?.entityData
|
||||
?.get(PokemonEntity.IS_ALPHA)
|
||||
@@ -203,35 +242,66 @@ class PokedexScannerRenderer {
|
||||
}
|
||||
|
||||
if (infoDisplayedCounter == 4) {
|
||||
val typeText = lang("type.suffix", pokedexEntityData.getApparentForm().types.map { it.displayName.copy() }.reduce { acc, next -> acc.plus("/").plus(next) }).bold()
|
||||
val typeWidth = Minecraft.getInstance().font.width(typeText.font(CobblemonResources.DEFAULT_LARGE))
|
||||
// Split into 2 lines if text width is too long
|
||||
if (typeWidth > (OUTER_INFO_FRAME_WIDTH - 8) && pokedexEntityData.getApparentForm().secondaryType !== null) {
|
||||
val splitLines = typeText.string.split("/")
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = text(splitLines[0], "/").bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - 4,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
val typeText = lang(
|
||||
"type.suffix",
|
||||
pokedexEntityData.getApparentForm().types.map { it.displayName.copy() }
|
||||
.reduce { acc, next -> acc.plus("/").plus(next) }).bold()
|
||||
val typeWidth =
|
||||
Minecraft.getInstance().font.width(typeText.font(CobblemonResources.DEFAULT_LARGE))
|
||||
// Split into 2 lines if text width is too long
|
||||
if (typeWidth > (OUTER_INFO_FRAME_WIDTH - 8) && pokedexEntityData.getApparentForm().secondaryType !== null) {
|
||||
val splitLines = typeText.string.split("/")
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = text(splitLines[0], "/").bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText - 4,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = text(splitLines[1]).bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText + 3,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
} else {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = typeText,
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (blockEntityInFocus != null && scanInfo != null) {
|
||||
val frame = scanInfo.frames.firstOrNull { it.slot == infoDisplayedCounter } ?: return@forEachIndexed
|
||||
|
||||
val scale = frame.scale
|
||||
if (scale != null) {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = text(splitLines[1]).bold(),
|
||||
text = frame.text.bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText + 3,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
centered = true
|
||||
centered = true,
|
||||
scale = scale
|
||||
)
|
||||
} else {
|
||||
drawScaledText(
|
||||
context = graphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = typeText,
|
||||
text = frame.text.bold(),
|
||||
x = centerX + xOffset + xOffsetText,
|
||||
y = centerY + yOffset + yOffsetText,
|
||||
shadow = true,
|
||||
@@ -462,4 +532,13 @@ class PokedexScannerRenderer {
|
||||
renderScanOverlay(graphics, tickDelta)
|
||||
}
|
||||
}
|
||||
|
||||
private fun anyToComponent(value: Any?): MutableComponent {
|
||||
return when (value) {
|
||||
null -> text("")
|
||||
is MutableComponent -> value
|
||||
is Component -> value.copy()
|
||||
else -> text(value.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,10 @@ class CobblemonConfig {
|
||||
@IntConstraint(min = 0, max = 1000)
|
||||
var maxPokemonFriendship = 255
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "power_item_ride_stat_gain_interval_seconds", SERVER)
|
||||
@IntConstraint(min = -1, max = Int.MAX_VALUE)
|
||||
var powerItemRideStatGainIntervalSeconds = 120
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "announce_drop_items", SERVER)
|
||||
var announceDropItems = true
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "default_drop_item_method", SERVER)
|
||||
|
||||
+27
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.TickingSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause.Companion.DROPS_REROLL_ASPECT
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
@@ -46,10 +47,15 @@ import kotlin.math.*
|
||||
|
||||
/** Handles purely server logic for a Pokémon */
|
||||
class PokemonServerDelegate : PokemonSideDelegate {
|
||||
companion object {
|
||||
private const val RIDE_STAT_GAIN_AMOUNT = 1F
|
||||
}
|
||||
|
||||
lateinit var entity: PokemonEntity
|
||||
var acknowledgedHPValue = -1
|
||||
var acknowledgedLevel = -1
|
||||
val passengerOffsets = mutableMapOf<Int, Vec3>()
|
||||
private var rideStatGainAnchor: Vec3? = null
|
||||
|
||||
/** Mocked properties exposed to the client [PokemonEntity]. */
|
||||
private val mock: PokemonProperties?
|
||||
@@ -355,9 +361,30 @@ class PokemonServerDelegate : PokemonSideDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
val rideStatGainIntervalSeconds = Cobblemon.config.powerItemRideStatGainIntervalSeconds
|
||||
if (rideStatGainIntervalSeconds > 0) {
|
||||
val rideStatGainIntervalTicks = rideStatGainIntervalSeconds * 20L
|
||||
if (entity.level().gameTime % rideStatGainIntervalTicks == 0L) {
|
||||
trainRideStats()
|
||||
}
|
||||
}
|
||||
|
||||
updateTrackedValues()
|
||||
}
|
||||
|
||||
private fun trainRideStats() {
|
||||
val position = entity.position()
|
||||
val anchor = rideStatGainAnchor
|
||||
rideStatGainAnchor = position
|
||||
if (anchor == null || anchor == position || entity.passengers.isEmpty()) return
|
||||
|
||||
val heldItem = entity.pokemon.heldItemNoCopy()
|
||||
if (heldItem.isEmpty) return
|
||||
|
||||
val stat = RidingStat.entries.firstOrNull { heldItem.`is`(it.powerItem) } ?: return
|
||||
entity.pokemon.addRideBoost(stat, RIDE_STAT_GAIN_AMOUNT)
|
||||
}
|
||||
|
||||
fun updateShownItem() {
|
||||
val trackedShownItem = when {
|
||||
// Show Hand Item if Held item is hidden
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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) })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.cobblemon.mod.common.pokedex.scanner
|
||||
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
data class BlockScanData(
|
||||
val frames: MutableSet<Frame> = mutableSetOf()
|
||||
)
|
||||
|
||||
data class Frame(
|
||||
val slot: Int,
|
||||
val text: MutableComponent,
|
||||
val scale: Float? = null
|
||||
)
|
||||
+47
-8
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.pokedex.scanner
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexLearnedInformation
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUI
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexScannerRenderer
|
||||
@@ -18,6 +19,8 @@ import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
@@ -26,6 +29,7 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.player.LocalPlayer
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import kotlin.math.exp
|
||||
@@ -51,6 +55,8 @@ class PokedexUsageContext {
|
||||
var infoGuiOpen: Boolean = false
|
||||
var scanningGuiOpen: Boolean = false
|
||||
var isPokemonInFocusOwned: Boolean = false
|
||||
var blockEntityInFocus: BlockEntity? = null
|
||||
var blockPosInFocus: BlockPos? = null
|
||||
var registerCompleted: Boolean = false
|
||||
var scannedSpecies: ResourceLocation? = null
|
||||
var scannableEntityInFocus: ScannableEntity? = null
|
||||
@@ -68,6 +74,8 @@ class PokedexUsageContext {
|
||||
var availableInfoFrames: MutableList<Boolean?> = mutableListOf(null, null, null, null)
|
||||
val renderer: PokedexScannerRenderer = PokedexScannerRenderer()
|
||||
|
||||
private fun hasAnyFocus() = (scannableEntityInFocus != null) || (blockEntityInFocus != null)
|
||||
|
||||
fun stopUsing(ticksInUse: Int, speciesId: ResourceLocation? = null) {
|
||||
if (ticksInUse < OPEN_SCANNER_BUFFER_TICKS) {
|
||||
openPokedexGUI(type, speciesId)
|
||||
@@ -89,7 +97,9 @@ class PokedexUsageContext {
|
||||
if (transitionIntervals == TRANSITION_INTERVALS) playSound(CobblemonSounds.POKEDEX_SCAN_CLOSE)
|
||||
transitionIntervals = max(transitionIntervals - updateInterval, 0F)
|
||||
if (transitionIntervals <= 0) {
|
||||
if (viewInfoTicks >= VIEW_INFO_BUFFER_TICKS) openPokedexGUI(type, scannableEntityInFocus!!.resolvePokemonScan()?.getApparentSpecies()?.resourceIdentifier)
|
||||
if (viewInfoTicks >= VIEW_INFO_BUFFER_TICKS && scannableEntityInFocus != null) {
|
||||
openPokedexGUI(type, scannableEntityInFocus!!.resolvePokemonScan()?.getApparentSpecies()?.resourceIdentifier)
|
||||
}
|
||||
resetState()
|
||||
}
|
||||
}
|
||||
@@ -102,7 +112,7 @@ class PokedexUsageContext {
|
||||
if (scanningProgress >= MAX_SCAN_PROGRESS) targetId?.let { FinishScanningPacket(it, zoomLevel.toInt()).sendToServer() }
|
||||
if (focusIntervals > 0) focusIntervals = max(0F, focusIntervals - updateInterval)
|
||||
} else {
|
||||
if (scannableEntityInFocus != null) {
|
||||
if (hasAnyFocus()) {
|
||||
if (focusIntervals < FOCUS_INTERVALS) focusIntervals = min(focusIntervals + updateInterval, FOCUS_INTERVALS)
|
||||
} else {
|
||||
if (focusIntervals > 0) focusIntervals = max(0F, focusIntervals - updateInterval)
|
||||
@@ -137,8 +147,9 @@ class PokedexUsageContext {
|
||||
playSound(CobblemonSounds.POKEDEX_OPEN)
|
||||
}
|
||||
|
||||
// todo display info of scanned berry to the Berry Dex later
|
||||
fun attackKeyHeld(isHeld: Boolean) {
|
||||
if (isHeld && scannableEntityInFocus !== null && viewInfoTicks < VIEW_INFO_BUFFER_TICKS && scanningProgress == 0F) {
|
||||
if (isHeld && hasAnyFocus() && viewInfoTicks < VIEW_INFO_BUFFER_TICKS && scanningProgress == 0F) {
|
||||
viewInfoTicks++
|
||||
if (viewInfoTicks % 2 == 0) playSound(CobblemonSounds.POKEDEX_SCAN_LOOP)
|
||||
} else if (viewInfoTicks > 0 && viewInfoTicks < VIEW_INFO_BUFFER_TICKS) {
|
||||
@@ -147,26 +158,30 @@ class PokedexUsageContext {
|
||||
}
|
||||
|
||||
fun tryScanPokemon(user: LocalPlayer) {
|
||||
// Pokémon/entity scan
|
||||
val targetScannableEntity = PokemonScanner.findScannableEntity(user, zoomLevel.toInt())
|
||||
if (targetScannableEntity != null) {
|
||||
if (targetScannableEntity != scannableEntityInFocus) {
|
||||
resetFocusedPokemonState()
|
||||
scannableEntityInFocus = targetScannableEntity
|
||||
|
||||
val resolvedPokemon = scannableEntityInFocus?.resolvePokemonScan()
|
||||
if(resolvedPokemon == null) {
|
||||
if (resolvedPokemon == null) {
|
||||
resetFocusedPokemonState()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if Pokémon in focus is owned
|
||||
isPokemonInFocusOwned = CobblemonClient.clientPokedexData.getHighestKnowledgeForSpecies(resolvedPokemon.getApparentSpecies().resourceIdentifier) == PokedexEntryProgress.OWNED
|
||||
isPokemonInFocusOwned =
|
||||
CobblemonClient.clientPokedexData.getHighestKnowledgeForSpecies(resolvedPokemon.getApparentSpecies().resourceIdentifier) == PokedexEntryProgress.OWNED
|
||||
|
||||
// Randomize info frames for render
|
||||
if (focusIntervals == 0F) {
|
||||
availableInfoFrames = mutableListOf(null, null, null, null)
|
||||
|
||||
for (i in 0..3) {
|
||||
var randomIndex = Random.nextInt(availableInfoFrames.size)
|
||||
if (availableInfoFrames[randomIndex] !== null) randomIndex = availableInfoFrames.indexOfFirst { it == null }
|
||||
if (availableInfoFrames[randomIndex] != null) randomIndex = availableInfoFrames.indexOfFirst { it == null }
|
||||
availableInfoFrames.set(randomIndex, Random.nextBoolean())
|
||||
}
|
||||
}
|
||||
@@ -176,9 +191,31 @@ class PokedexUsageContext {
|
||||
if (newPokemonInfo == PokedexLearnedInformation.NONE) playSound(CobblemonSounds.POKEDEX_SCAN_DETAIL)
|
||||
else scannedSpecies = resolvedPokemon.getApparentSpecies().resourceIdentifier
|
||||
}
|
||||
} else {
|
||||
resetFocusedPokemonState()
|
||||
return
|
||||
}
|
||||
|
||||
// BlockEntity scan
|
||||
val blockHit = PokemonScanner.findScannableBlockEntity(user, zoomLevel.toInt())
|
||||
if (blockHit != null) {
|
||||
val (pos, be) = blockHit
|
||||
if (be != blockEntityInFocus || pos != blockPosInFocus) {
|
||||
resetFocusedPokemonState()
|
||||
blockEntityInFocus = be
|
||||
blockPosInFocus = pos
|
||||
|
||||
if (focusIntervals == 0F) {
|
||||
availableInfoFrames = mutableListOf(null, null, null, null)
|
||||
for (i in 0..3) {
|
||||
var randomIndex = Random.nextInt(availableInfoFrames.size)
|
||||
if (availableInfoFrames[randomIndex] != null) randomIndex = availableInfoFrames.indexOfFirst { it == null }
|
||||
availableInfoFrames[randomIndex] = Random.nextBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
resetFocusedPokemonState()
|
||||
}
|
||||
|
||||
fun onServerConfirmedRegister(packet: ServerConfirmedRegisterPacket) {
|
||||
@@ -196,6 +233,8 @@ class PokedexUsageContext {
|
||||
|
||||
fun resetFocusedPokemonState() {
|
||||
scannableEntityInFocus = null
|
||||
blockEntityInFocus = null
|
||||
blockPosInFocus = null
|
||||
scannedSpecies = null
|
||||
viewInfoTicks = 0
|
||||
scanningProgress = 0F
|
||||
|
||||
@@ -10,10 +10,14 @@ package com.cobblemon.mod.common.pokedex.scanner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext.Companion.BLOCK_LENGTH_PER_ZOOM_STAGE
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
|
||||
//Handles the actual raycasting to figure out what pokemon we are looking at
|
||||
object PokemonScanner {
|
||||
@@ -100,4 +104,34 @@ object PokemonScanner {
|
||||
fun isEntityInRange(castingEntity: Entity, targetEntity: Entity, zoomLevel: Int): Boolean {
|
||||
return targetEntity.position().distanceTo(castingEntity.position()) <= Cobblemon.config.maxPokedexScanningDetectionRange + (zoomLevel * BLOCK_LENGTH_PER_ZOOM_STAGE)
|
||||
}
|
||||
|
||||
fun findScannableBlockEntity(castingEntity: Entity, zoomLevel: Int): Pair<BlockPos, BlockEntity>? {
|
||||
val eyePos = castingEntity.getEyePosition(1.0F)
|
||||
val lookVec = castingEntity.getViewVector(1.0F)
|
||||
|
||||
val maxDistance =
|
||||
Cobblemon.config.maxPokedexScanningDetectionRange + (zoomLevel * BLOCK_LENGTH_PER_ZOOM_STAGE)
|
||||
|
||||
val end = eyePos.add(lookVec.scale(maxDistance.toDouble()))
|
||||
|
||||
val hit = castingEntity.level().clip(
|
||||
ClipContext(
|
||||
eyePos,
|
||||
end,
|
||||
ClipContext.Block.OUTLINE,
|
||||
ClipContext.Fluid.NONE,
|
||||
castingEntity
|
||||
)
|
||||
)
|
||||
|
||||
if (hit.type != HitResult.Type.BLOCK) return null
|
||||
|
||||
val pos = (hit as BlockHitResult).blockPos
|
||||
val be = castingEntity.level().getBlockEntity(pos) ?: return null
|
||||
|
||||
val typeId = BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(be.type) ?: return null
|
||||
// if (BlockScanProviderRegistry.get(typeId) == null) return null
|
||||
|
||||
return pos to be
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.cobblemon.mod.common.pokedex.scanner
|
||||
|
||||
interface ScannableBlockEntity {
|
||||
fun resolveBlockScan(): BlockScanData
|
||||
}
|
||||
+837
-649
File diff suppressed because it is too large.
Load diff
+6
@@ -456,6 +456,12 @@
|
||||
"cannon_left": {
|
||||
"position": [0, 0, 3]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.25": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.25": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.25": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.25": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.blastoise.ground_run": {
|
||||
|
||||
+11
@@ -83,6 +83,7 @@
|
||||
},
|
||||
"animation.parasect.ground_walk": {
|
||||
"loop": true,
|
||||
"animation_length": 1,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": [0, 0, 0],
|
||||
@@ -134,6 +135,12 @@
|
||||
"locator_seat_1": {
|
||||
"rotation": [7.5, 0, 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.25": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.5": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.75": "q.run_molang('cobblemon:stepsound_minecraft');"
|
||||
}
|
||||
},
|
||||
"animation.parasect.ground_run": {
|
||||
@@ -258,6 +265,10 @@
|
||||
"rotation": ["5+Math.cos((query.anim_time) * 90 * 16 - 90) * 2", 0, 0],
|
||||
"position": [0, "0+Math.cos((query.anim_time) * 90 * 32) * -0.2", -0.25]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.125": "q.run_molang('cobblemon:stepsound_minecraft');"
|
||||
}
|
||||
},
|
||||
"animation.parasect.ride_ground_run": {
|
||||
|
||||
+4
@@ -391,6 +391,10 @@
|
||||
"0.7083": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0833": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"0.2917": "q.run_molang('cobblemon:stepsound_standard_medium');"
|
||||
}
|
||||
},
|
||||
"animation.dewgong.water_idle": {
|
||||
|
||||
+20
@@ -1071,6 +1071,16 @@
|
||||
"head": {
|
||||
"rotation": ["-0.8*math.cos(q.anim_time*90*8-120)", 0, "3*math.sin(q.anim_time*90*4+70)"]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.5": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.rhyhorn.ride_ground_walk": {
|
||||
@@ -1917,6 +1927,16 @@
|
||||
],
|
||||
"position": [0, "0.4+0.6*math.cos(q.anim_time*90*8-120)", 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.375": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"0.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.875": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.375": "q.run_molang('cobblemon:stepsound_standard_medium');",
|
||||
"1.5": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.875": "q.run_molang('cobblemon:stepsound_standard_medium');"
|
||||
}
|
||||
},
|
||||
"animation.rhyhorn.ride_ground_run": {
|
||||
|
||||
+12
@@ -329,6 +329,12 @@
|
||||
"locator_seat_1": {
|
||||
"rotation": [-15, 0, 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"2.0": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"3.0": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.rhydon.ride_ground_walk": {
|
||||
@@ -807,6 +813,12 @@
|
||||
"locator_seat_1": {
|
||||
"rotation": [-15, 0, 0]
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.3333": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"0.8333": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.3333": "q.run_molang('cobblemon:stepsound_standard_large');",
|
||||
"1.8333": "q.run_molang('cobblemon:stepsound_standard_large');"
|
||||
}
|
||||
},
|
||||
"animation.rhydon.ride_ground_run": {
|
||||
|
||||
+4
@@ -391,6 +391,10 @@
|
||||
"1.0": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"0.0833": "q.run_molang('cobblemon:stepsound_minecraft');",
|
||||
"0.5833": "q.run_molang('cobblemon:stepsound_minecraft');"
|
||||
}
|
||||
},
|
||||
"animation.mr_mime.ground_run": {
|
||||
|
||||
@@ -1299,7 +1299,6 @@
|
||||
"cobblemon.ui.pc.filter.tooltip": "Format: pikachu shiny held_item lvl=1...",
|
||||
"cobblemon.ui.pc.options": "Options",
|
||||
"cobblemon.ui.pc.release": "Release",
|
||||
"cobblemon.ui.pc.release_all": "Release All",
|
||||
"cobblemon.ui.sort.name": "Sort by name",
|
||||
"cobblemon.ui.sort.level": "Sort by level",
|
||||
"cobblemon.ui.sort.pokedex_number": "Sort by Pokédex number",
|
||||
@@ -1474,6 +1473,9 @@
|
||||
"cobblemon.ui.pokedex.weight": "Weight: %1$skg",
|
||||
"cobblemon.ui.pokedex.height": "Height: %1$sm",
|
||||
|
||||
"cobblemon.ui.pokedex.scan.berry.stage": "Stage: %s",
|
||||
"cobblemon.ui.pokedex.scan.berry.mulch_none": "No Mulch",
|
||||
|
||||
"cobblemon.ui.pokedex.evolution.item_interact.tooltip": "Needs %1$s item to evolve",
|
||||
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_species": "Evolves when traded with %1$s",
|
||||
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_any_species": "Evolves when traded",
|
||||
@@ -2435,6 +2437,8 @@
|
||||
"cobblemon.config.ui.max_pokemon_level.tooltip": "The maximum level Pokémon can get to.",
|
||||
"cobblemon.config.ui.max_pokemon_friendship": "Max Pokémon Friendship",
|
||||
"cobblemon.config.ui.max_pokemon_friendship.tooltip": "The maximum friendship value.",
|
||||
"cobblemon.config.ui.power_item_ride_stat_gain_interval_seconds": "Power Item Riding Stat Training Interval (Seconds)",
|
||||
"cobblemon.config.ui.power_item_ride_stat_gain_interval_seconds.tooltip": "Seconds between riding stat gains while riding a Pokémon holding a Power item. Defaults to 120 seconds. Set to -1 or 0 to disable; values below -1 are clamped to -1.",
|
||||
"cobblemon.config.ui.announce_drop_items": "Announce Drop Items",
|
||||
"cobblemon.config.ui.announce_drop_items.tooltip": "Whether to notify the Player of dropped items from defeating wild Pokémon.",
|
||||
"cobblemon.config.ui.default_drop_item_method": "Default Drop Item Method",
|
||||
|
||||
@@ -1653,6 +1653,18 @@
|
||||
"move.horndrill.target_2": {
|
||||
"sounds": [ { "name": "cobblemon:move/horndrill/horndrill_target_2", "volume": 1.0 } ]
|
||||
},
|
||||
"move.gigatonhammer.actor": {
|
||||
"sounds": [ { "name": "cobblemon:move/gigatonhammer/gigatonhammer_actor", "volume": 1.0 } ]
|
||||
},
|
||||
"move.gigatonhammer.target": {
|
||||
"sounds": [ { "name": "cobblemon:move/gigatonhammer/gigatonhammer_target", "volume": 1.0 } ]
|
||||
},
|
||||
"move.pyroball.actor": {
|
||||
"sounds": [ { "name": "cobblemon:move/pyroball/pyroball_actor", "volume": 1.0 } ]
|
||||
},
|
||||
"move.pyroball.target": {
|
||||
"sounds": [ { "name": "cobblemon:move/pyroball/pyroball_target", "volume": 1.0 } ]
|
||||
},
|
||||
"impact.normal": {
|
||||
"sounds": [
|
||||
{ "name": "cobblemon:move/impact_generic/impact_generic_fighting_1", "volume": 0.8 },
|
||||
@@ -8672,6 +8684,9 @@
|
||||
"pokemon.cinderace.cry": {
|
||||
"sounds": [ { "name": "cobblemon:pokemon/cinderace/cinderace_cry", "volume": 0.8 } ]
|
||||
},
|
||||
"pokemon.cinderace.pyroball": {
|
||||
"sounds": [ { "name": "cobblemon:pokemon/cinderace/cinderace_pyroball", "volume": 0.5 } ]
|
||||
},
|
||||
"pokemon.sobble.cry": {
|
||||
"sounds": [ { "name": "cobblemon:pokemon/sobble/sobble_cry", "volume": 0.8 } ]
|
||||
},
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 548 B |
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"timeline": [
|
||||
{
|
||||
"type": "add_holds",
|
||||
"holds": ["effects"]
|
||||
},
|
||||
{
|
||||
"type": "animation",
|
||||
"animation": ["gigatonhammer"],
|
||||
"delay": 0.3
|
||||
},
|
||||
{
|
||||
"type": "entity_molang",
|
||||
"entityCondition": "q.entity.is_user == true",
|
||||
"expressions": ["q.sound('move.gigatonhammer.actor');"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 1.1
|
||||
},
|
||||
{
|
||||
"type": "entity_molang",
|
||||
"entityCondition": "q.entity.is_user == false && q.missed(q.entity.uuid) == false",
|
||||
"expressions": ["q.sound('move.gigatonhammer.target');"]
|
||||
},
|
||||
{
|
||||
"type": "remove_holds",
|
||||
"holds": ["effects"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 1.5
|
||||
}
|
||||
]
|
||||
}
|
||||
+29
-2
@@ -3,11 +3,38 @@
|
||||
{
|
||||
"type": "add_holds",
|
||||
"holds": ["effects"]
|
||||
|
||||
},
|
||||
{
|
||||
"type": "animation",
|
||||
"animation": ["pyroball"],
|
||||
"delay": 2.05
|
||||
"animation": ["pyroball"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause" : 0.1
|
||||
},
|
||||
{
|
||||
"type": "entity_molang",
|
||||
"entityCondition": "q.entity.is_user == true",
|
||||
"expressions": ["q.sound('move.pyroball.actor');"]
|
||||
},
|
||||
{
|
||||
"type": "entity_molang",
|
||||
"entityCondition": "q.entity.is_user == true",
|
||||
"expressions": ["q.sound('pokemon.cinderace.pyroball');"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause" : 1.875
|
||||
},
|
||||
{
|
||||
"type": "entity_molang",
|
||||
"entityCondition": "q.entity.is_user == false && q.missed(q.entity.uuid) == false",
|
||||
"expressions": ["q.sound('move.pyroball.target');"]
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause" : 0.175
|
||||
},
|
||||
{
|
||||
"type": "entity_particles",
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
}
|
||||
],
|
||||
"type": "cobblemon:cooking_pot_shapeless",
|
||||
"category": "medicines",
|
||||
"category": "misc",
|
||||
"ingredients": [
|
||||
{
|
||||
"item": "create:experience_nugget"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"maleRatio": 0.875,
|
||||
"height": 16,
|
||||
"weight": 855,
|
||||
"silenceStepSounds": true,
|
||||
"pokedex": [
|
||||
"cobblemon.species.blastoise.desc"
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"maleRatio": 0.5,
|
||||
"height": 17,
|
||||
"weight": 1200,
|
||||
"silenceStepSounds": true,
|
||||
"pokedex": [
|
||||
"cobblemon.species.dewgong.desc"
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"maleRatio": 0.5,
|
||||
"height": 13,
|
||||
"weight": 545,
|
||||
"silenceStepSounds": true,
|
||||
"pokedex": [
|
||||
"cobblemon.species.mrmime.desc"
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"maleRatio": 0.5,
|
||||
"height": 10,
|
||||
"weight": 295,
|
||||
"silenceStepSounds": true,
|
||||
"pokedex": [
|
||||
"cobblemon.species.parasect.desc"
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"maleRatio": 0.5,
|
||||
"height": 19,
|
||||
"weight": 1200,
|
||||
"silenceStepSounds": true,
|
||||
"pokedex": [
|
||||
"cobblemon.species.rhydon.desc"
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"maleRatio": 0.5,
|
||||
"height": 10,
|
||||
"weight": 1150,
|
||||
"silenceStepSounds": true,
|
||||
"pokedex": [
|
||||
"cobblemon.species.rhyhorn.desc"
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"maleRatio": 0.875,
|
||||
"height": 20,
|
||||
"weight": 1000,
|
||||
"silenceStepSounds": true,
|
||||
"pokedex": [
|
||||
"cobblemon.species.venusaur.desc"
|
||||
],
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"cobblemon:tart_apple",
|
||||
"cobblemon:syrupy_apple",
|
||||
"cobblemon:candied_apple",
|
||||
{ "id": "create:honeyed_apple","required": false }
|
||||
{ "id": "create:honeyed_apple","required": false },
|
||||
{ "id": "nomansland:honeyed_apple","required": false }
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user