Compare commits

..
Author SHA1 Message Date
Slippy 2895c0a2ef I hate the crumbling arch 2024-07-18 08:57:11 -05:00
Slippy acc87f9eae Solhenge, Lunahenge, and Stonjournerhenge structures completed. 2024-07-17 12:31:55 -05:00
Slippy 928dfcdadf i hate the crumbling arch 2024-06-01 08:47:58 -05:00
Slippy 21363405fc console log spam fix for real this time 2024-05-18 12:00:11 -05:00
Slippy 08c4f4a0ff added loot to the trapped chest in unstable caves so you can't tell which one is trapped if you're using lootr muwahahahaha 2024-05-16 01:57:41 -05:00
Slippy c4a1704a8b unstable cave loot table fixes 2024-05-15 20:35:24 -05:00
Slippy 837aa5acf5 missing loot table in deserted tower 2024-05-14 16:58:55 -05:00
Slippy 969d6ac7fc console spam fix 2024-05-12 09:23:39 -05:00
Slippy 46bb9e1aec console fix 2024-05-11 21:58:50 -05:00
Slippy 9792ff3392 console spam fix 2024-05-11 21:36:29 -05:00
Slippy e863c6d094 Deleted Duplicate Loot Table 2024-05-10 16:19:15 -05:00
Slippy a070dc56bd Cleaning up unused files 2024-04-29 01:05:00 -05:00
Slippy 79f9e0f0f2 Gimmi Towers now have more junk it can generate at the top 2024-04-29 00:59:41 -05:00
Slippy 8500eb58ef Removed spam log issues and blended some structures into the world better 2024-04-29 00:49:48 -05:00
Slippy e835b833b3 One more try 2024-04-26 16:48:04 -05:00
Slippy 3b653b745e suspicious mound shoved further into the ground 2024-04-22 17:20:46 -05:00
Slippy d03541ff6f iron bar fix 2024-04-15 00:56:31 -05:00
Slippy 0d7aff0349 Please work 2024-04-15 00:24:54 -05:00
Slippy c904888c6a found a spelling error smh 2024-04-13 09:12:21 -05:00
Slippy c07d7d304e It wasnt the last D: 2024-04-09 19:19:02 -05:00
Slippy 5f94999b4f Is this the last push? 2024-04-09 19:15:02 -05:00
Slippy 6515bfa108 Decaying Crypt no longer has a random floating granite block 2024-04-05 12:14:57 -05:00
Slippy c502fec709 more bug fixes and balancing 2024-04-01 02:48:33 -05:00
242 changed files with 24051 additions and 1381 deletions
@@ -67,13 +67,24 @@ import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFa
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.ServerTickHandler
@@ -94,7 +105,13 @@ import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.sherds.CobblemonSherds
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -115,16 +132,13 @@ import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import kotlin.collections.filterIsInstance
import kotlin.collections.forEach
import kotlin.collections.listOf
import kotlin.collections.set
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import kotlin.math.roundToInt
object Cobblemon {
const val MODID = "cobblemon"
@@ -152,6 +166,7 @@ object Cobblemon {
var storage = PokemonStoreManager()
var molangData = NbtMoLangDataStoreFactory
lateinit var playerData: PlayerDataStoreManager
lateinit var starterConfig: StarterConfig
val dataProvider: DataProvider = CobblemonDataProvider
var permissionValidator: PermissionValidator by Delegates.observable(LaxPermissionValidator().also { it.initialize() }) { _, _, newValue -> newValue.initialize() }
var statProvider: StatProvider = CobblemonStatProvider
@@ -462,6 +477,27 @@ object Cobblemon {
this.saveConfig()
bestSpawner.loadConfig()
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadStarterConfig(): StarterConfig {
if (config.exportStarterConfig) {
val file = File("config/cobblemon/starters.json")
file.parentFile.mkdirs()
if (!file.exists()) {
val config = StarterConfig()
val pw = PrintWriter(file)
StarterConfig.GSON.toJson(config, pw)
pw.close()
return config
}
val reader = FileReader(file)
val config = StarterConfig.GSON.fromJson(reader, StarterConfig::class.java)
reader.close()
return config
} else {
return StarterConfig()
}
}
fun saveConfig() {
@@ -397,7 +397,7 @@ open class PokemonProperties {
return false
}
}
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
nickname?.takeIf { it != pokemon.nickname }?.let { return false }
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
@@ -437,7 +437,7 @@ open class PokemonProperties {
}
} catch (_: InvalidIdentifierException) {}
}
nickname?.takeIf { it.string != properties.nickname?.string }?.let { return false }
nickname?.takeIf { it != properties.nickname }?.let { return false }
form?.takeIf { !it.equals(properties.form, true) }?.let { return false }
friendship?.takeIf { it != properties.friendship }?.let { return false }
pokeball?.takeIf { it != properties.pokeball }?.let { return false }
@@ -8,12 +8,12 @@
package com.cobblemon.mod.common.api.starter
import com.cobblemon.mod.common.config.starter.StarterCategory
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
interface StarterHandler {
fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory>
fun getStarterList(player: ServerPlayerEntity): List<StarterCategory>
fun handleJoin(player: ServerPlayerEntity)
fun requestStarterChoice(player: ServerPlayerEntity)
fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int)
fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int)
}
@@ -1,69 +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://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.adapters.PlainJsonTextAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* The registry for starter Pokémon.
*
* The data here is used by the [CobblemonStarterHandler].
*
* 3rd party is not enforced to respect this registry.
*/
object StarterRegistry : JsonDataRegistry<StarterCategory> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(Text::class.java, PlainJsonTextAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.create()
override val typeToken: TypeToken<StarterCategory> = TypeToken.get(StarterCategory::class.java)
override val id: Identifier = cobblemonResource("starter")
override val resourcePath: String = id.path
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable: SimpleObservable<out DataRegistry> = SimpleObservable()
private val categories = hashMapOf<Identifier, StarterCategory>()
// We let the default starter handler sync the client
// This is to prevent unnecessary packets depending on impl
override fun sync(player: ServerPlayerEntity) {}
override fun reload(data: Map<Identifier, StarterCategory>) {
this.categories.clear()
this.categories += data
Cobblemon.LOGGER.info("Loaded {} starters ({} categories)", this.categories.values.sumOf { it.pokemon.size }, this.categories.size)
}
fun all(): Map<Identifier, StarterCategory> = this.categories
fun keys(): Set<Identifier> = this.categories.keys
fun categories(): Collection<StarterCategory> = this.categories.values
fun categoryOf(key: Identifier): StarterCategory? = this.categories[key]
fun categoryOf(key: String): StarterCategory? = this.categories[key.asIdentifierDefaultingNamespace()]
}
@@ -37,7 +37,7 @@ data class PlayerData(
fun defaultData(forPlayer: UUID) = PlayerData(
uuid = forPlayer,
starterPrompted = false,
starterLocked = !Cobblemon.config.allowStarterOnJoin,
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
starterSelected = false,
starterUUID = null,
keyItems = mutableSetOf(),
@@ -9,23 +9,15 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.chest.GildedChestBlock
import net.minecraft.block.*
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemPlacementContext
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.Properties
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.math.random.Random
import net.minecraft.util.shape.VoxelShape
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldView
@@ -36,18 +28,7 @@ class TumblestoneBlock(
height: Int,
xzOffset: Int,
nextStage: Block?
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage), Waterloggable {
init {
this.defaultState = this.stateManager.defaultState
.with(FACING, Direction.DOWN)
.with(WATERLOGGED, false)
}
companion object {
val WATERLOGGED = BooleanProperty.of("waterlogged")
}
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
if (stage == MAX_STAGE) return false
val iterator: Iterator<BlockPos> =
@@ -62,31 +43,4 @@ class TumblestoneBlock(
return true
}
override fun getFluidState(state: BlockState): FluidState {
return if (state.get(GildedChestBlock.WATERLOGGED)) {
Fluids.WATER.getStill(false)
} else super.getFluidState(state)
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder)
builder.add(GildedChestBlock.WATERLOGGED)
}
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
return super.getPlacementState(blockPlaceContext)?.with(GildedChestBlock.WATERLOGGED, blockPlaceContext.world.getFluidState(blockPlaceContext.blockPos).fluid == Fluids.WATER)
}
override fun getStateForNeighborUpdate(
state: BlockState,
direction: Direction,
neighborState: BlockState,
world: WorldAccess,
pos: BlockPos,
neighborPos: BlockPos
): BlockState? {
if (state.get(GildedChestBlock.WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
}
@@ -16,15 +16,20 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.Blocks
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.ShapeContext
import net.minecraft.block.Waterloggable
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.mob.PiglinBrain
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
@@ -33,7 +38,12 @@ import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.Properties
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.*
import net.minecraft.util.ActionResult
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.ItemScatterer
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
@@ -96,7 +106,7 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
neighborPos: BlockPos
): BlockState {
if (state.get(WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
override fun getFluidState(state: BlockState): FluidState {
@@ -248,17 +258,4 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
return false
}
override fun onPlaced(
world: World,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
itemStack: ItemStack
) {
val blockEntity = world.getBlockEntity(pos)
if (itemStack.hasCustomName() && blockEntity is GildedChestBlockEntity) {
blockEntity.customName = itemStack.name
}
}
}
@@ -140,7 +140,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, val state: BlockState) : BlockEnt
if (fixedPosition != null) {
entity.setPosition(fixedPosition.toCenterPos().subtract(0.0, 0.5, 0.0))
val pc = Cobblemon.storage.getPC(player.uuid)
entity.beamMode = 2
entity.beamMode = 1
afterOnServer(seconds = SendOutPokemonHandler.SEND_OUT_DURATION) {
entity.beamMode = 0
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.entity
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
@@ -21,6 +22,7 @@ import com.cobblemon.mod.common.api.scheduling.lerpOnClient
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatefulAnimation
@@ -32,8 +34,11 @@ import com.cobblemon.mod.common.util.MovingSoundInstance
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.math.MatrixStack
import java.lang.Float.min
import kotlin.math.abs
import net.minecraft.entity.Entity
import net.minecraft.entity.data.TrackedData
import net.minecraft.particle.ParticleTypes
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvent
import net.minecraft.util.Hand
@@ -100,8 +105,6 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
this.addPrimaryAnimation(primaryAnimation)
}
} else if (data == PokemonEntity.BEAM_MODE) {
// If you make adjustments to this code, make sure to Find Usages for both PokemonEntity.beamMode and PokemonEntity.BEAM_MODE
// TODO: change beamMode to an enum, or a set of booleans (send-out vs recall, delay vs delay)
val beamMode = currentEntity.beamMode
when (beamMode) {
0 -> { /* Do nothing */ }
@@ -138,37 +141,39 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
currentEntity.after(seconds = POKEBALL_AIR_TIME){
beamStartTime = System.currentTimeMillis()
ballDone = true
if (client.soundManager.get(CobblemonSounds.POKE_BALL_SEND_OUT.id) != null && !playedSendOutSound) {
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
}
// Display ball-specific particles
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.lowercase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect ->
if (client.soundManager.get(CobblemonSounds.POKE_BALL_OPEN.id) != null && !playedSendOutSound) {
currentEntity.owner?.let {
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
/// create end rod particles in a 0.1 radius around the soundPos with a count of 50 and a random velocity of 0.1
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.toLowerCase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
}
ballsendsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
ballsendsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
}
}
}
}
@@ -185,35 +190,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
}
}
}
2 -> {
// Scaling up with no delay and no particles
playedSendOutSound = false
entityScaleModifier = 0F
currentEntity.isInvisible = false
val soundPos = currentEntity.pos
val client = MinecraftClient.getInstance()
if (client.soundManager.get(CobblemonSounds.POKE_BALL_SEND_OUT.id) != null && !playedSendOutSound) {
client.world?.playSound(
client.player,
soundPos.x,
soundPos.y,
soundPos.z,
CobblemonSounds.POKE_BALL_SEND_OUT,
SoundCategory.PLAYERS,
0.6f,
1f
)
playedSendOutSound = true
}
lerpOnClient(BEAM_SHRINK_TIME) { entityScaleModifier = it }
currentEntity.after(seconds = BEAM_SHRINK_TIME * 2) {
ballOffset = 0f
ballRotOffset = 0f
sendOutPosition = null
}
}
3 -> {
else -> {
// Scaling down into pokeball
entityScaleModifier = 1F
beamStartTime = System.currentTimeMillis()
@@ -226,7 +203,6 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
}
}
}
else -> { /* Do nothing */ }
}
} else if (data == PokemonEntity.LABEL_LEVEL) {
currentEntity.dataTracker.get(PokemonEntity.LABEL_LEVEL)
@@ -27,7 +27,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.type.DualTypeWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.type.SingleTypeWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.type.TypeWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asTranslated
@@ -40,7 +40,6 @@ import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.toast.Toast
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* Starterselection Screen Thingy
@@ -48,7 +47,7 @@ import net.minecraft.util.Identifier
* @author Qu
* @since 2022-06-18
*/
class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
companion object {
// Size of UI at scale 1
@@ -65,9 +64,8 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
private val doubleTypeBackground = cobblemonResource("textures/gui/starterselection/starterselection_type_slot2.png")
}
private val categories = categories.toSortedMap { a, b -> a.compareTo(b) }
private var currentSelection = 0
private lateinit var currentCategoryId: Identifier
private lateinit var currentCategory: RenderableStarterCategory
private lateinit var modelWidget: ModelWidget
private lateinit var currentPokemon: RenderablePokemon
private lateinit var typeWidget: TypeWidget
@@ -97,7 +95,7 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
paneWidth = 71, paneHeight = BASE_HEIGHT - 11,
topOffset = 6, bottomOffset = 5,
entryHeight = 20, entryWidth = 57,
categories = this.categories,
categories = categories,
x = x - 2, y = y + 8,
starterSelectionScreen = this
)
@@ -122,8 +120,8 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
addDrawableChild(rightButton)
addDrawableChild(leftButton)
currentCategoryId = categories.firstKey()
currentPokemon = this.currentCategory().pokemon[currentSelection]
currentCategory = categories.first()
currentPokemon = currentCategory.pokemon[currentSelection]
with(currentPokemon) {
modelWidget = ModelWidget(
@@ -144,11 +142,11 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
) {
CobblemonNetwork.sendPacketToServer(
SelectStarterPacket(
categoryId = currentCategoryId,
categoryName = currentCategory.name,
selected = currentSelection
)
)
this.close()
MinecraftClient.getInstance().setScreen(null)
}
addDrawableChild(selectionButton)
@@ -163,7 +161,7 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
starterRoundaboutLeft = StarterRoundabout(
pX = x + 89, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()],
pokemon = currentCategory.pokemon[leftOfCurrentSelection()],
clickAction = { _, _ -> this.left() },
rotationVector = this.modelWidget.rotVec
)
@@ -171,7 +169,7 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
starterRoundaboutRight = StarterRoundabout(
pX = x + 149, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()],
pokemon = currentCategory.pokemon[rightOfCurrentSelection()],
clickAction = { _, _ -> this.right() },
rotationVector = this.modelWidget.rotVec
)
@@ -278,8 +276,8 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
super.render(context, mouseX, mouseY, delta)
}
fun changeCategory(id: Identifier) {
currentCategoryId = id
fun changeCategory(category: RenderableStarterCategory) {
currentCategory = category
currentSelection = 0
updateSelection()
}
@@ -290,7 +288,7 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
updateSelection()
}
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= this.currentCategory().pokemon.size - 1) currentSelection + 1 else 0
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= currentCategory.pokemon.size - 1) currentSelection + 1 else 0
private fun left() {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
@@ -298,16 +296,16 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
updateSelection()
}
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) this.currentCategory().pokemon.size - 1 else currentSelection - 1
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) currentCategory.pokemon.size - 1 else currentSelection - 1
private fun updateSelection() {
currentPokemon = this.currentCategory().pokemon[currentSelection].also {
currentPokemon = currentCategory.pokemon[currentSelection].also {
modelWidget.pokemon = it
typeWidget = typeWidget(it, (width - BASE_WIDTH) / 2, (height - BASE_HEIGHT) / 2)
}
starterRoundaboutLeft.pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()]
starterRoundaboutLeft.pokemon = currentCategory.pokemon[leftOfCurrentSelection()]
starterRoundaboutCenter.pokemon = currentPokemon
starterRoundaboutRight.pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()]
starterRoundaboutRight.pokemon = currentCategory.pokemon[rightOfCurrentSelection()]
}
private fun typeWidget(pokemon: RenderablePokemon, x: Int, y: Int) : TypeWidget {
@@ -326,7 +324,5 @@ class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCatego
)
}
private fun currentCategory(): RenderableStarterCategory = this.categories[this.currentCategoryId]!!
override fun shouldPause() = true
}
@@ -12,14 +12,14 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.startselection.StarterSelectionScreen
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.Identifier
class CategoryList(
private val paneWidth: Int,
@@ -28,7 +28,7 @@ class CategoryList(
bottomOffset: Int,
private val entryWidth: Int,
entryHeight: Int,
private val categories: Map<Identifier, RenderableStarterCategory>,
private val categories: List<RenderableStarterCategory>,
val x: Int, val y: Int,
private val minecraft: MinecraftClient = MinecraftClient.getInstance(),
private val starterSelectionScreen: StarterSelectionScreen
@@ -44,6 +44,7 @@ class CategoryList(
companion object {
private const val CATEGORY_BUTTON_WIDTH = 51.5f
private const val CATEGORY_BUTTON_HEIGHT = 16f
private const val ENTRY_X_OFFSET = 10f
private val categoryResource = cobblemonResource("textures/gui/starterselection/starterselection_slot.png")
}
@@ -52,12 +53,19 @@ class CategoryList(
this.setRenderHorizontalShadows(false)
this.setRenderBackground(false)
this.setRenderSelection(false)
this.categories.forEach { (id, category) ->
this.addEntry(Category(id, category))
}
}
private var entriesCreated = false
private fun createEntries() = categories.map {
Category(it)
}
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
if (!entriesCreated) {
createEntries().forEach { addEntry(it) }
entriesCreated = true
}
context.enableScissor(
x,
y,
@@ -72,13 +80,15 @@ class CategoryList(
this.updateSize(this.paneWidth, this.paneHeight, this.y, this.y + this.paneHeight)
this.setLeftPos(this.x)
}
private fun scale(n: Int): Int = (this.client.window.scaleFactor * n).toInt()
override fun getRowWidth() = this.entryWidth
override fun getScrollbarPositionX(): Int {
return this.left + this.width - 5
}
inner class Category(private val id: Identifier, private val category: RenderableStarterCategory) : Entry<Category>() {
inner class Category(private val category: RenderableStarterCategory) : Entry<Category>() {
override fun render(
context: DrawContext,
@@ -111,7 +121,7 @@ class CategoryList(
)
drawScaledText(
context = context,
text = category.displayName.copy(),
text = category.displayNameText,
x = x + 28,
y = y + 4.5F,
scale = 1F,
@@ -122,7 +132,7 @@ class CategoryList(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
starterSelectionScreen.changeCategory(this.id)
starterSelectionScreen.changeCategory(category = category)
minecraft.soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
return true
}
@@ -67,11 +67,6 @@ abstract class PokeBallPoseableState : PoseableEntityState<EmptyPokeBallEntity>(
setStatefulAnimations(currentModel!!.bedrockStateful(group, "critical"))
}
}
EmptyPokeBallEntity.CaptureState.BROKEN_FREE -> {
doLater {
setStatefulAnimations(currentModel!!.bedrockStateful(group, "break"))
}
}
else -> {}
}
}
@@ -32,6 +32,7 @@ import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.DoubleRange
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import kotlin.math.*
import net.minecraft.client.MinecraftClient
import net.minecraft.client.font.TextRenderer
import net.minecraft.client.render.*
@@ -52,7 +53,6 @@ import org.joml.Math
import org.joml.Quaternionf
import org.joml.Vector3f
import org.joml.Vector4f
import kotlin.math.*
class PokemonRenderer(
context: EntityRendererFactory.Context
@@ -60,7 +60,7 @@ class PokemonRenderer(
companion object {
val recallBeamColour = Vector4f(1F, 0.1F, 0.1F, 1F)
fun ease(x: Double): Double {
return 1 - (1 - x).pow(3)
return 1 - (1 - x).pow(3);
}
}
@@ -86,7 +86,7 @@ class PokemonRenderer(
if (entity.beamMode != 0) {
renderTransition(
modelNow,
entity.beamMode,
entity.beamMode == 1,
entity,
partialTicks,
poseMatrix,
@@ -130,7 +130,7 @@ class PokemonRenderer(
fun renderTransition(
modelNow: PoseableEntityModel<PokemonEntity>,
beamMode: Int,
sendingOut: Boolean,
entity: PokemonEntity,
partialTicks: Float,
poseMatrix: MatrixStack,
@@ -139,7 +139,7 @@ class PokemonRenderer(
clientDelegate: PokemonClientDelegate
) {
val s = clientDelegate.secondsSinceBeamEffectStarted
if (modelNow is PokemonPoseableModel && beamMode == 3) {
if (modelNow is PokemonPoseableModel && !sendingOut) {
if (s > BEAM_EXTEND_TIME) {
val value = (s - BEAM_EXTEND_TIME) / BEAM_SHRINK_TIME
val colourValue = 1F - min(0.6F, value)
@@ -163,9 +163,9 @@ class PokemonRenderer(
}
}
if (clientDelegate.sendOutPosition == null && beamMode == 1) {
if (clientDelegate.sendOutPosition == null && sendingOut) {
clientDelegate.sendOutPosition = beamSourcePosition
} else if (beamMode == 1) {
} else if (sendingOut) {
clientDelegate.sendOutPosition = clientDelegate.sendOutPosition!!.add(0.0, 0.04, 0.0)
beamSourcePosition = clientDelegate.sendOutPosition!!
}
@@ -184,9 +184,9 @@ class PokemonRenderer(
poseMatrix.multiply(RotationAxis.POSITIVE_Y.rotation(-angle.toFloat() + (180 * Math.PI / 180).toFloat()))
// TODO: if you want to remove the open ball, add `!clientDelegate.ballDone` to the if statement
if (beamMode == 1 && !clientDelegate.ballDone){
if (sendingOut && !clientDelegate.ballDone){
if(entity.pokemon.caughtBall.name.toString().contains("beast")){
// get rotation angle on x-axis for facingDir
// get rotation angle on x acis for facingDir
val xAngleFacingDir = MathHelper.atan2(facingDir.y, sqrt(facingDir.x * facingDir.x + facingDir.z * facingDir.z))
poseMatrix.multiply(RotationAxis.POSITIVE_X.rotation(-xAngleFacingDir.toFloat()))
}
@@ -203,7 +203,7 @@ class PokemonRenderer(
)
}
poseMatrix.pop()
if (beamMode == 3) {
if (!sendingOut) {
renderBeam(poseMatrix, partialTicks, entity, phaseTarget, buffer, offsetDirection)
}
}
@@ -9,7 +9,11 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command.SINGLE_SUCCESS
import com.mojang.brigadier.CommandDispatcher
@@ -34,7 +38,18 @@ object OpenStarterScreenCommand {
private fun execute(context: CommandContext<ServerCommandSource>) : Int {
val player = EntityArgumentType.getPlayer(context, "player")
Cobblemon.starterHandler.requestStarterChoice(player)
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
context.source.sendFeedback({ lang("ui.starter.hasalreadychosen", player.name).red() }, true)
return 0
}
if (playerData.starterLocked) {
playerData.starterLocked = false
playerData.sendToPlayer(player)
}
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
player.sendPacket(OpenStarterUIPacket(Cobblemon.starterHandler.getStarterList(player)))
return SINGLE_SUCCESS
}
@@ -113,12 +113,6 @@ class CobblemonConfig {
@NodeCategory(Category.Starter)
var exportStarterConfig = false
@NodeCategory(Category.Starter)
var allowStarterOnJoin = true
@NodeCategory(Category.Starter)
var promptStarterOnceOnly = true
@NodeCategory(Category.Battles)
var autoUpdateShowdown = true
@@ -6,19 +6,24 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.starter
package com.cobblemon.mod.common.config.starter
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asTranslated
import net.minecraft.text.Text
data class StarterCategory(
val displayName: Text,
val name: String,
val displayName: String,
val pokemon: List<PokemonProperties>
)
) {
fun asRenderableStarterCategory() = RenderableStarterCategory(name, displayName, pokemon.map { it.asRenderablePokemon() })
}
data class RenderableStarterCategory(
val displayName: Text,
val name: String,
val displayName: String,
val pokemon: List<RenderablePokemon>
)
) {
val displayNameText = displayName.asTranslated()
}
@@ -0,0 +1,128 @@
/*
* 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://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.config.starter
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.config.Category
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.NodeCategory
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.google.gson.GsonBuilder
class StarterConfig {
companion object {
val GSON = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.create()
}
@NodeCategory(Category.Starter)
var allowStarterOnJoin = true
@NodeCategory(Category.Starter)
@LastChangedVersion("1.5.0")
var promptStarterOnceOnly = true
@NodeCategory(Category.Starter)
var starters = mutableListOf(
StarterCategory(
name = "Kanto",
displayName = "cobblemon.starterselection.category.kanto",
pokemon = mutableListOf(
PokemonProperties.parse("Bulbasaur level=10"),
PokemonProperties.parse("Charmander level=10"),
PokemonProperties.parse("Squirtle level=10"),
)
),
StarterCategory(
name = "Johto",
displayName = "cobblemon.starterselection.category.johto",
pokemon = mutableListOf(
PokemonProperties.parse("Chikorita level=10"),
PokemonProperties.parse("Cyndaquil level=10"),
PokemonProperties.parse("Totodile level=10"),
)
),
StarterCategory(
name = "Hoenn",
displayName = "cobblemon.starterselection.category.hoenn",
pokemon = mutableListOf(
PokemonProperties.parse("Treecko level=10"),
PokemonProperties.parse("Torchic level=10"),
PokemonProperties.parse("Mudkip level=10"),
)
),
StarterCategory(
name = "Sinnoh",
displayName = "cobblemon.starterselection.category.sinnoh",
pokemon = mutableListOf(
PokemonProperties.parse("Turtwig level=10"),
PokemonProperties.parse("Chimchar level=10"),
PokemonProperties.parse("Piplup level=10"),
)
),
StarterCategory(
name = "Unova",
displayName = "cobblemon.starterselection.category.unova",
pokemon = mutableListOf(
PokemonProperties.parse("Snivy level=10"),
PokemonProperties.parse("Tepig level=10"),
PokemonProperties.parse("Oshawott level=10"),
)
),
StarterCategory(
name = "Kalos",
displayName = "cobblemon.starterselection.category.kalos",
pokemon = mutableListOf(
PokemonProperties.parse("Chespin level=10"),
PokemonProperties.parse("Fennekin level=10"),
PokemonProperties.parse("Froakie level=10"),
)
),
StarterCategory(
name = "Alola",
displayName = "cobblemon.starterselection.category.alola",
pokemon = mutableListOf(
PokemonProperties.parse("Rowlet level=10"),
PokemonProperties.parse("Litten level=10"),
PokemonProperties.parse("Popplio level=10"),
)
),
StarterCategory(
name = "Galar",
displayName = "cobblemon.starterselection.category.galar",
pokemon = mutableListOf(
PokemonProperties.parse("Grookey level=10"),
PokemonProperties.parse("Scorbunny level=10"),
PokemonProperties.parse("Sobble level=10"),
)
),
StarterCategory(
name = "Paldea",
displayName = "cobblemon.starterselection.category.paldea",
pokemon = mutableListOf(
PokemonProperties.parse("Sprigatito level=10"),
PokemonProperties.parse("Fuecoco level=10"),
PokemonProperties.parse("Quaxly level=10"),
)
)
//,
// StarterCategory(
// name = "Johto",
// displayName = lang("starterselection.category.johto"),
// pokemon = mutableListOf(
// PokemonProperties().also { it.level = 5 ; it.species = "Chikorita" },
// PokemonProperties().also { it.level = 5 ; it.species = "Cyndaquil" },
// PokemonProperties().also { it.level = 5 ; it.species = "Totodile" }
// )
// )
)
}
@@ -29,7 +29,6 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.api.starter.StarterRegistry
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.platform.events.PlatformEvents
@@ -64,7 +63,6 @@ object CobblemonDataProvider : DataProvider {
this.register(Abilities)
this.register(PokemonSpecies)
this.register(SpeciesAdditions)
this.register(StarterRegistry)
this.register(PokeBalls)
this.register(PropertiesCompletionProvider)
this.register(SpawnDetailPresets)
@@ -21,8 +21,9 @@ import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.text.red
@@ -36,12 +37,17 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.Poseable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonServerDelegate
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleCaptureStartPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.sendParticlesServer
import com.cobblemon.mod.common.util.setPositionSafely
import com.cobblemon.mod.common.util.update
import java.util.concurrent.CompletableFuture
import net.minecraft.entity.EntityDimensions
import net.minecraft.entity.EntityPose
import net.minecraft.entity.EntityType
@@ -64,7 +70,6 @@ import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.MathHelper.PI
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import java.util.concurrent.CompletableFuture
class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Schedulable {
enum class CaptureState {
@@ -73,8 +78,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
FALL,
SHAKE,
CAPTURED,
CAPTURED_CRITICAL,
BROKEN_FREE
CAPTURED_CRITICAL
}
companion object {
@@ -131,7 +135,6 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
CaptureState.FALL -> setNoGravity(false)
CaptureState.SHAKE -> setNoGravity(true)
CaptureState.CAPTURED, CaptureState.CAPTURED_CRITICAL -> {}
CaptureState.BROKEN_FREE -> {}
}
}
dataTrackerEmitter.emit(data)
@@ -345,27 +348,25 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_SHAKE, volume = 0.8F)
// Emits a shake by changing the value to the opposite of what it currently is. Sends an update to the client basically.
// We could replace this with a packet, but it feels awfully excessive when we already have 5 bajillion packets.
// We could replace this with a packet but it feels awfully excessive when we already have 5 bajillion packets.
dataTracker.update(SHAKE) { !it }
}
private fun breakFree() {
val pokemon = capturingPokemon ?: return
pokemon.setPosition(pos)
pokemon.beamMode = 2
pokemon.beamMode = 1
pokemon.isInvisible = false
if (pokemon.battleId == null) {
pokemon.pokemon.status?.takeIf { it.status == Statuses.SLEEP }?.let { pokemon.pokemon.status = null }
}
captureState = CaptureState.BROKEN_FREE
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_OPEN, volume = 0.8F)
after(seconds = 1F) {
after(seconds = 0.25F) {
pokemon.busyLocks.remove(this)
captureFuture.complete(false)
world.sendParticlesServer(ParticleTypes.CLOUD, pos, 20, Vec3d(0.0, 0.2, 0.0), 0.05)
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_OPEN, volume = 0.8F)
discard()
}
}
@@ -380,17 +381,6 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
captureState = CaptureState.HIT
val mul = if (random.nextBoolean()) 1 else -1
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_HIT, volume = 0.4F)
// Hit Pokémon plays recoil animation
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
pkt.sendToPlayersAround(
x = pokemonEntity.x,
y = pokemonEntity.y,
z = pokemonEntity.z,
worldKey = pokemonEntity.world.registryKey,
distance = 50.0
)
// Bounce backwards away from the hit Pokémon
velocity = displace.multiply(-1.0, 0.0, -1.0).normalize().rotateY(mul * PI/3).multiply(0.1, 0.0, 0.1).add(0.0, 1.0 / 3, 0.0)
pokemonEntity.phasingTargetId = this.id
@@ -399,7 +389,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
velocity = Vec3d.ZERO
setNoGravity(true)
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_CAPTURE_STARTED, volume = 0.6F)
pokemonEntity.beamMode = 3
pokemonEntity.beamMode = 2
}
after(seconds = 2.2F) {
@@ -28,6 +28,7 @@ import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.ElementalTypes.FIRE
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -56,6 +57,10 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolut
import com.cobblemon.mod.common.pokemon.misc.GimmighoulStashHandler
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.util.EnumSet
import java.util.Optional
import java.util.UUID
import java.util.concurrent.CompletableFuture
import net.minecraft.entity.*
import net.minecraft.entity.ai.control.MoveControl
import net.minecraft.entity.ai.goal.EatGrassGoal
@@ -102,8 +107,6 @@ import net.minecraft.util.math.Vec3d
import net.minecraft.world.EntityView
import net.minecraft.world.World
import net.minecraft.world.event.GameEvent
import java.util.*
import java.util.concurrent.CompletableFuture
@Suppress("unused")
open class PokemonEntity(
@@ -330,7 +333,7 @@ open class PokemonEntity(
* Prevents fire type Pokémon from taking fire damage.
*/
override fun isFireImmune(): Boolean {
return pokemon.isFireImmune()
return FIRE in pokemon.types || !behaviour.moving.swim.hurtByLava
}
/**
@@ -375,10 +378,10 @@ open class PokemonEntity(
if (dataTracker.get(PHASING_TARGET_ID) == -1 && owner != null) {
owner.getWorld().playSoundServer(pos, CobblemonSounds.POKE_BALL_RECALL, volume = 0.6F)
dataTracker.set(PHASING_TARGET_ID, owner.id)
dataTracker.set(BEAM_MODE, 3)
dataTracker.set(BEAM_MODE, 2)
val state = pokemon.state
after(seconds = SEND_OUT_DURATION) {
// only recall if the Pokémon hasn't been recalled yet for this state
// only recall if the pokemon hasn't been recalled yet for this state
if (state == pokemon.state) {
pokemon.recall()
}
@@ -205,7 +205,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
if (owner != null) {
entity.world.playSoundServer(owner.pos, CobblemonSounds.POKE_BALL_RECALL, volume = 0.6F)
entity.dataTracker.set(PokemonEntity.PHASING_TARGET_ID, owner.id)
entity.dataTracker.set(PokemonEntity.BEAM_MODE, 3)
entity.dataTracker.set(PokemonEntity.BEAM_MODE, 2)
}
}
@@ -16,7 +16,6 @@ import com.cobblemon.mod.common.util.getWaterAndLavaIn
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
import net.minecraft.entity.ai.control.MoveControl
@@ -76,9 +75,9 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
entity.setSidewaysSpeed(sidewaysMovement)
state = State.WAIT
} else if (state == State.MOVE_TO) {
var xDist = targetX - entity.x
var zDist = targetZ - entity.z
var yDist = targetY - entity.y
val xDist = targetX - entity.x
val zDist = targetZ - entity.z
val yDist = targetY - entity.y
val horizontalDistanceFromTarget = xDist * xDist + zDist * zDist
val closeHorizontally = horizontalDistanceFromTarget < VERY_CLOSE
@@ -100,13 +99,12 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
verticalHandled = true
entity.upwardSpeed = 0F
entity.movementSpeed = 0F
// Refinement is to prevent the entity from spinning around trying to get to a super precise location.
val refine: (Double) -> Double = { if (abs(it) < 0.05) 0.0 else it }
val fullDistance = Vec3d(
xDist,
refine(yDist + 0.05), // + 0.05 for dealing with swimming out of water, they otherwise get stuck on the lip
zDist
refine(xDist),
if (inFluid || pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) refine(yDist + 0.05) else 0.0,
refine(zDist)
)
val direction = fullDistance.normalize()
@@ -114,13 +112,8 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
val scale = min(adjustedSpeed.toDouble(), fullDistance.length())
entity.velocity = direction.multiply(scale)
xDist = fullDistance.x
zDist = fullDistance.z
yDist = fullDistance.y
} else {
// division is to slow the speed down a bit so they don't overshoot when they get there.
val forwardSpeed = min(adjustedSpeed, max(horizontalDistanceFromTarget.toFloat() / 2, 0.15F))
val forwardSpeed = min(adjustedSpeed, sqrt(horizontalDistanceFromTarget).toFloat())
entity.movementSpeed = forwardSpeed
}
@@ -12,14 +12,12 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Hand
import net.minecraft.util.TypedActionResult
import net.minecraft.world.World
import kotlin.math.cos
class PokeBallItem(
val pokeBall : PokeBall
@@ -38,12 +36,8 @@ class PokeBallItem(
private fun throwPokeBall(world: World, player: ServerPlayerEntity) {
val pokeBallEntity = EmptyPokeBallEntity(pokeBall, player.world, player).apply {
val overhandFactor: Float = if (player.pitch < 0) {
5f * cos(player.pitch.toRadians())
} else {
5f
}
setVelocity(player, player.pitch - overhandFactor, player.yaw, 0.0f, pokeBall.throwPower, 1.0f)
// setPos(player.x, player.y + player.standingEyeHeight - 0.2, player.z)
setVelocity(player, player.pitch - 5, player.yaw, 0.0f, pokeBall.throwPower, 1.0f)
setPosition(pos.add(velocity.normalize().multiply(1.0)))
owner = player
}
@@ -9,33 +9,48 @@
package com.cobblemon.mod.common.net.messages.client.starter
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.StarterCategory
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.config.starter.StarterCategory
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class OpenStarterUIPacket(val categories: Map<Identifier, RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
class OpenStarterUIPacket internal constructor(val categories: List<RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
override val id = ID
constructor(categories: Collection<StarterCategory>) : this(categories.map { it.asRenderableStarterCategory() })
override fun encode(buffer: PacketByteBuf) {
buffer.writeMap(this.categories, PacketByteBuf::writeIdentifier) { categoryBuffer, category ->
categoryBuffer.writeText(category.displayName)
categoryBuffer.writeCollection(category.pokemon) { pokemonBuffer, renderable ->
renderable.saveToBuffer(pokemonBuffer)
}
buffer.writeInt(categories.size)
categories.forEach {
buffer.writeString(it.name)
buffer.writeString(it.displayName)
buffer.writeInt(it.pokemon.size)
it.pokemon.forEach { it.saveToBuffer(buffer) }
}
}
companion object {
val ID = cobblemonResource("open_starter")
fun decode(buffer: PacketByteBuf): OpenStarterUIPacket {
val categories = buffer.readMap(PacketByteBuf::readIdentifier) { categoryBuffer ->
val displayName = buffer.readText()
val renderablePokemon = categoryBuffer.readList(RenderablePokemon::loadFromBuffer)
return@readMap RenderableStarterCategory(displayName, renderablePokemon)
val numCategories = buffer.readInt()
val categories = arrayListOf<RenderableStarterCategory>()
for (i in 0 until numCategories) {
val name = buffer.readString()
val displayName = buffer.readString()
val numProperties = buffer.readInt()
val renderablePokemon = mutableListOf<RenderablePokemon>()
repeat(times = numProperties) {
renderablePokemon.add(RenderablePokemon.loadFromBuffer(buffer))
}
categories.add(
RenderableStarterCategory(
name = name,
displayName = displayName,
pokemon = renderablePokemon
)
)
}
return OpenStarterUIPacket(categories)
}
@@ -29,7 +29,7 @@ class SetClientPlayerDataPacket(val promptStarter: Boolean, val starterLocked: B
playerData: PlayerData,
resetStarterPrompt: Boolean? = null
) : this(
!playerData.starterPrompted || !Cobblemon.config.promptStarterOnceOnly,
!playerData.starterPrompted || !Cobblemon.starterConfig.promptStarterOnceOnly,
playerData.starterLocked,
playerData.starterSelected,
playerData.starterUUID,
@@ -11,17 +11,16 @@ package com.cobblemon.mod.common.net.messages.server
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class SelectStarterPacket(val categoryId: Identifier, val selected: Int) : NetworkPacket<SelectStarterPacket> {
class SelectStarterPacket(val categoryName: String, val selected: Int) : NetworkPacket<SelectStarterPacket> {
override val id = ID
override fun encode(buffer: PacketByteBuf) {
buffer.writeIdentifier(this.categoryId)
buffer.writeString(this.categoryName)
buffer.writeInt(this.selected)
}
companion object {
val ID = cobblemonResource("select_starter")
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readIdentifier(), buffer.readInt())
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readString(), buffer.readInt())
}
}
@@ -16,6 +16,6 @@ import net.minecraft.server.network.ServerPlayerEntity
object SelectStarterPacketHandler : ServerNetworkPacketHandler<SelectStarterPacket> {
override fun handle(packet: SelectStarterPacket, server: MinecraftServer, player: ServerPlayerEntity) {
Cobblemon.starterHandler.chooseStarter(player, packet.categoryId, packet.selected)
Cobblemon.starterHandler.chooseStarter(player, packet.categoryName, packet.selected)
}
}
@@ -13,10 +13,11 @@ import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.util.raycastSafeSendout
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.traceBlockCollision
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.world.RaycastContext
import net.minecraft.util.math.Vec3d
object SendOutPokemonHandler : ServerNetworkPacketHandler<SendOutPokemonPacket> {
@@ -30,10 +31,11 @@ object SendOutPokemonHandler : ServerNetworkPacketHandler<SendOutPokemonPacket>
return
}
val state = pokemon.state
if (state is ShoulderedState || state !is ActivePokemonState) {
val position = player.raycastSafeSendout(pokemon, 12.0, 5.0, RaycastContext.FluidHandling.ANY)
if (position != null) {
if (state is ShoulderedState || state !is ActivePokemonState) {
val trace = player.traceBlockCollision(maxDistance = 15F)
if (trace != null && !player.world.getBlockState(trace.blockPos.up()).isSolid) {
val position = Vec3d(trace.location.x, trace.blockPos.up().toVec3d().y, trace.location.z)
pokemon.sendOutWithAnimation(player, player.serverWorld, position)
}
} else {
@@ -49,7 +49,6 @@ import com.cobblemon.mod.common.api.storage.StoreCoordinates
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.tera.TeraType
import com.cobblemon.mod.common.api.types.tera.TeraTypes
import com.cobblemon.mod.common.config.CobblemonConfig
@@ -73,7 +72,6 @@ import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.mojang.serialization.Dynamic
import com.mojang.serialization.JsonOps
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
@@ -82,7 +80,6 @@ import net.minecraft.nbt.NbtElement.COMPOUND_TYPE
import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.NbtString
import net.minecraft.registry.tag.FluidTags
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.text.MutableText
@@ -91,11 +88,9 @@ import net.minecraft.text.TextContent
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.InvalidIdentifierException
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.MathHelper.ceil
import net.minecraft.util.math.MathHelper.clamp
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import java.util.*
import java.util.concurrent.CompletableFuture
import kotlin.math.absoluteValue
@@ -539,7 +534,6 @@ open class Pokemon : ShowdownIdentifiable {
val rotatedOffset = Vec3d(shoulderHorizontalOffset, approxShoulderMonHight, 0.0).rotateY(-rotation * (Math.PI.toFloat() / 180f))
val currentPosition = player.pos.add(rotatedOffset)
recall()
sendOut(level, currentPosition) {
// Play some sound indicating hopping off
level.playSoundServer(currentPosition, CobblemonSounds.PC_DROP, volume = 0.6F)
@@ -620,52 +614,6 @@ open class Pokemon : ShowdownIdentifiable {
}
}
fun isFireImmune(): Boolean {
return ElementalTypes.FIRE in types || !form.behaviour.moving.swim.hurtByLava
}
fun isPositionSafe(world: World, pos: Vec3d): Boolean {
return isPositionSafe(world, pos.toBlockPos())
}
fun isPositionSafe(world: World, pos1: BlockPos): Boolean {
// To make sure a location is safe, both the block the Pokemon is standing ON,
// and the block it's standing IN need to be safe. pos2 represents the other position in that set.
val pos2: BlockPos = if (world.getBlockState(pos1).isSolid) {
pos1.up()
} else {
pos1.down()
}
val positions = arrayOf(pos1, pos2)
var isSafe = true
for (pos in positions) {
if (isSafe) {
val block = world.getBlockState(pos).block
if (block is SweetBerryBushBlock ||
block is CactusBlock ||
block is WitherRoseBlock
) {
isSafe = false
}
if (!this.isFireImmune()) {
if (block is FireBlock ||
block is MagmaBlock ||
block is CampfireBlock ||
world.getBlockState(pos).fluidState.isIn(FluidTags.LAVA)
) {
isSafe = false
}
}
}
}
return isSafe
}
/**
* A utility method that checks if this Pokémon species or form data contains the [CobblemonPokemonLabels.LEGENDARY] label.
* This is used in Pokémon officially considered legendary.
@@ -0,0 +1,73 @@
/*
* 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://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.server.network.ServerPlayerEntity
open class CobblemonStarterHandler : StarterHandler {
override fun getStarterList(player: ServerPlayerEntity) = Cobblemon.starterConfig.starters
override fun handleJoin(player: ServerPlayerEntity) {}
override fun requestStarterChoice(player: ServerPlayerEntity) {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
playerData.sendToPlayer(player)
player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
} else if (playerData.starterLocked) {
player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
} else {
OpenStarterUIPacket(getStarterList(player)).sendToPlayer(player)
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
}
}
override fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int) {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
return player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
} else if (playerData.starterLocked) {
return player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
}
val category = getStarterList(player).find { it.name == categoryName } ?: return
if (index > category.pokemon.size) {
return
}
val properties = category.pokemon[index]
val pokemon = properties.create()
CobblemonEvents.STARTER_CHOSEN.postThen(StarterChosenEvent(player, properties, pokemon)) {
Cobblemon.storage.getParty(player).add(
it.pokemon.also {
playerData.starterSelected = true
playerData.starterUUID = it.uuid
if (player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)) { pokemon.shiny = true }
}
)
CobblemonCriteria.PICK_STARTER.trigger(player, pokemon)
Cobblemon.playerData.saveSingle(playerData)
playerData.sendToPlayer(player)
}
}
}
@@ -1,96 +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://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.StarterCategory
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.starter.StarterRegistry
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class CobblemonStarterHandler : StarterHandler {
override fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory> = StarterRegistry.all()
// No OP the starters are sent out when the open UI packet is sent out instead
override fun handleJoin(player: ServerPlayerEntity) {}
override fun requestStarterChoice(player: ServerPlayerEntity) {
if (!this.isAbleToSelect(player)) {
return
}
val playerData = Cobblemon.playerData.get(player)
val renderable = this.getStarters(player).entries.associate { (id, category) ->
val renderable = category.pokemon.map {
// Let the actual Pokémon be created and apply the shiny accordingly
// This won't be accurate with random but I mean what else do you want me to do
val pokemon = it.create()
// While they can still be shiny a random shiny from the #create doesn't mean the actual starter will be shiny
// This tries to be realistic
pokemon.shiny = player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)
pokemon.asRenderablePokemon()
}
id to RenderableStarterCategory(category.displayName, renderable)
}
OpenStarterUIPacket(renderable).sendToPlayer(player)
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
}
override fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int) {
if (!this.isAbleToSelect(player)) {
return
}
val category = this.getStarters(player)[categoryId] ?: return
val properties = category.pokemon.getOrNull(index) ?: return
val pokemon = properties.create()
val playerData = Cobblemon.playerData.get(player)
CobblemonEvents.STARTER_CHOSEN.postThen(StarterChosenEvent(player, properties, pokemon)) { event ->
Cobblemon.storage.getParty(player).add(
event.pokemon.also {
playerData.starterSelected = true
playerData.starterUUID = it.uuid
if (player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)) { pokemon.shiny = true }
}
)
CobblemonCriteria.PICK_STARTER.trigger(player, pokemon)
Cobblemon.playerData.saveSingle(playerData)
playerData.sendToPlayer(player)
}
}
/**
* Checks if the [player] can select a starter.
* If not gives them an appropriate feedback message.
*
* @param player The [ServerPlayerEntity] being tested.
* @return If the [player] can pick a starter.
*/
private fun isAbleToSelect(player: ServerPlayerEntity): Boolean {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
return false
} else if (playerData.starterLocked) {
player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
return false
}
return true
}
}
@@ -13,32 +13,11 @@ import net.minecraft.entity.data.DataTracker
import net.minecraft.entity.data.TrackedData
import net.minecraft.util.function.BooleanBiFunction
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Direction
import net.minecraft.util.math.Vec3d
import net.minecraft.util.shape.VoxelShapes
fun Entity.setPositionSafely(pos: Vec3d): Boolean {
// TODO: Rework this function. Best way to do it would be to define a vertical and horizontal min/max shift based on the Pokemon's hitbox.
// Then loop through horizontal/vertical shifts, and detect collisions in three categories: suffocation, damaging blocks, and general collision
// The closest position with the least severe collision types will be selected to move the Pokemon to
// The throw could be cancelled if there are no viable locations without severe problems
// Optional: use getBlockCollisions iterator and VoxelShapes.combineAndSimplify to create a single cube to represent collision area
// Use that cube to "push" the Pokemon out of the wall at an angle
// Note: may not work well with L shape wall collisions
// val collisions = world.getBlockCollisions(this, box).iterator()
// if (collisions.hasNext()) {
// var collisionShape = collisions.next()
// while (collisions.hasNext()) {
// collisionShape = VoxelShapes.union(collisionShape, collisions.next())
// println(collisionShape)
// }
// } else {
// setPosition(pos)
// return true
// }
var result = pos
val eyes = pos.withAxis(Direction.Axis.Y, pos.y + this.standingEyeHeight)
@@ -50,6 +29,27 @@ fun Entity.setPositionSafely(pos: Vec3d): Boolean {
return true
}
val yChanges = listOf(1.0, -1.0, 2.0, -2.0)
var previousChange = 0.0
for (yChange in yChanges) {
box = box.offset(0.0, yChange - previousChange, 0.0)
val it = world.getBlockCollisions(this, box).iterator()
previousChange = yChange
if (it.hasNext()) {
continue
} else {
val roundedY = (pos.y + yChange).toInt()
box = box.offset(0.0, roundedY - pos.y, 0.0)
// If the rounded position actually collides again, then don't round at all.
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
setPosition(pos.add(0.0, yChange, 0.0))
return true
}
setPosition(Vec3d(pos.x, roundedY.toDouble(), pos.z))
return true
}
}
for (target in BlockPos.stream(box)) {
val blockState = this.world.getBlockState(target)
val collides = !blockState.isAir &&
@@ -73,13 +73,13 @@ fun Entity.setPositionSafely(pos: Vec3d): Boolean {
result = result.add(Vec3d(0.0, 0.0, 1 + (conflict.z - box.minZ + (1.0 / 8.0))))
}
Direction.SOUTH -> {
result = result.add(Vec3d(0.0, 0.0, (conflict.z - box.maxZ) - (1.0 / 8.0)))
result = result.add(Vec3d(0.0, 0.0, -1 * (1 - (conflict.z - box.minZ) + (1.0 / 8.0))))
}
Direction.WEST -> {
result = result.add(Vec3d(1 + (conflict.x - box.minX + (1.0 / 8.0)), 0.0, 0.0))
}
Direction.EAST -> {
result = result.add(Vec3d((conflict.x - box.maxX) - (1.0 / 8.0), 0.0, 0.0))
result = result.add(Vec3d(-1 * (1 - (conflict.x - box.minX) + (1.0 / 8.0)), 0.0, 0.0))
}
else -> {}
}
@@ -89,90 +89,12 @@ fun Entity.setPositionSafely(pos: Vec3d): Boolean {
}
}
box = boundingBox.offset(result)
if (!world.getBlockCollisions(this, box).iterator().hasNext()) {
this.setPosition(result)
return true
} else {
val yChanges = listOf(1.0, -1.0, 2.0, -2.0)
var previousChange = 0.0
// If the Pokemon still collides with blocks after horizontal shifting, try vertical shifting from the shifted position
for (yChange in yChanges) {
box = box.offset(0.0, yChange - previousChange, 0.0)
previousChange = yChange
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
continue
} else {
val roundedY = (result.y + yChange).toInt()
box = box.offset(0.0, roundedY - result.y, 0.0)
// If the rounded position actually collides again, then don't round at all.
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
result = result.add(0.0, yChange, 0.0)
this.setPosition(result)
return true
} else {
result = Vec3d(result.x, roundedY.toDouble(), result.z)
this.setPosition(result)
return true
}
}
}
// If vertical shifting from the new position still collides, try vertical shifting from the original pos
previousChange = 0.0
box = boundingBox.offset(pos)
for (yChange in yChanges) {
box = box.offset(0.0, yChange - previousChange, 0.0)
previousChange = yChange
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
continue
} else {
val roundedY = (result.y + yChange).toInt()
box = box.offset(0.0, roundedY - result.y, 0.0)
// If the rounded position actually collides again, then don't round at all.
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
result = result.add(0.0, yChange, 0.0)
this.setPosition(result)
return true
} else {
result = Vec3d(result.x, roundedY.toDouble(), result.z)
this.setPosition(result)
return true
}
}
}
}
if (conflicts.size >= 3) {
this.setPosition(pos)
}
// This final check guarantees that the sendout will return to the original position if the Pokemon will suffocate in the new one
// This will only happen if the horizontal shift moved the Pokemon into a suffocating position, and there was no valid vertical shift
val resultEyes = result.withAxis(Direction.Axis.Y, result.y + this.standingEyeHeight)
val resultEyeBox = Box.of(resultEyes, width.toDouble(), 1.0E-6, width.toDouble())
var collides = false
for (target in BlockPos.stream(resultEyeBox)) {
val blockState = this.world.getBlockState(target)
collides = !blockState.isAir &&
blockState.shouldSuffocate(this.world, target) &&
VoxelShapes.matchesAnywhere(
blockState.getCollisionShape(this.world, target)
.offset(target.x.toDouble(), target.y.toDouble(), target.z.toDouble()),
VoxelShapes.cuboid(box),
BooleanBiFunction.AND
)
if (collides) break
}
if (collides) {
this.setPosition(pos)
return true
} else {
this.setPosition(result)
return true
}
this.setPosition(result)
return true
}
fun Entity.distanceTo(pos: BlockPos): Double {
@@ -19,7 +19,7 @@ import com.cobblemon.mod.common.api.reactive.Observable.Companion.filter
import com.cobblemon.mod.common.api.reactive.Observable.Companion.takeFirst
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.UUID
import net.minecraft.block.BlockState
import net.minecraft.entity.Entity
import net.minecraft.entity.player.PlayerEntity
@@ -30,11 +30,10 @@ import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvents
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.*
import net.minecraft.world.RaycastContext
import java.util.*
import kotlin.math.min
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Direction
import net.minecraft.util.math.Vec3d
// Stuff like getting their party
fun ServerPlayerEntity.party() = Cobblemon.storage.getParty(this)
@@ -252,91 +251,6 @@ fun findDirectionForIntercept(p0: Vec3d, p1: Vec3d, blockPos: BlockPos): Directi
return minDirection
}
fun ServerPlayerEntity.raycast(maxDistance: Float, fluidHandling: RaycastContext.FluidHandling?): BlockHitResult {
val f = pitch
val g = yaw
val vec3d = eyePos
val h = MathHelper.cos(-g * 0.017453292f - 3.1415927f)
val i = MathHelper.sin(-g * 0.017453292f - 3.1415927f)
val j = -MathHelper.cos(-f * 0.017453292f)
val k = MathHelper.sin(-f * 0.017453292f)
val l = i * j
val n = h * j
val vec3d2 = vec3d.add(l.toDouble() * maxDistance, k.toDouble() * maxDistance, n.toDouble() * maxDistance)
return world.raycast(RaycastContext(vec3d, vec3d2, RaycastContext.ShapeType.OUTLINE, fluidHandling, this))
}
fun ServerPlayerEntity.raycastSafeSendout(pokemon: Pokemon, maxDistance: Double, dropHeight: Double, fluidHandling: RaycastContext.FluidHandling?): Vec3d? {
// Crazy math stuff, don't worry about it
val f = pitch
val g = yaw
val vec3d = eyePos
val h = MathHelper.cos(-g * 0.017453292f - 3.1415927f)
val i = MathHelper.sin(-g * 0.017453292f - 3.1415927f)
val j = -MathHelper.cos(-f * 0.017453292f)
val k = MathHelper.sin(-f * 0.017453292f)
val l = i * j
val n = h * j
val vec3d2 = vec3d.add(l.toDouble() * maxDistance, k.toDouble() * maxDistance, n.toDouble() * maxDistance)
val result = world.raycast(RaycastContext(vec3d, vec3d2, RaycastContext.ShapeType.OUTLINE, fluidHandling, this))
if (world.getBlockState(result.blockPos).isAir) {
// If the trace returns air, the player isn't aiming at any blocks within range
var traceDown: TraceResult?
val minDrop = min(2.5, maxDistance)
val stepDistance = 0.05
var step = minDrop
var stepDrop = minDrop
var stepPos: Vec3d
var traceHeight: Double
var smallestHeight = dropHeight
var fallLoc: TraceResult? = null
// Try to find a valid block below the player's aim instead
while (step <= maxDistance) {
stepPos = vec3d.add(l.toDouble() * step, k.toDouble() * step, n.toDouble() * step)
if (minDrop != maxDistance) {
stepDrop = ((step - minDrop) / (maxDistance - minDrop)) * dropHeight
}
traceDown = stepPos.traceDownwards(this.world, maxDistance = stepDrop.toFloat())
if (traceDown != null && pokemon.isPositionSafe(world, traceDown.blockPos)) {
traceHeight = (stepPos.y - traceDown.location.y)
if (traceHeight < smallestHeight) {
smallestHeight = traceHeight
fallLoc = traceDown
}
}
step += stepDistance
}
// If a valid block was found below the player's aim, return the location directly above it
return fallLoc?.blockPos?.up()?.toCenterPos()
} else if (result.side != Direction.UP) {
// If the player targets the side or bottom of a block, try to find a valid spot in front of / below that block
val offset: Double = if (result.side == Direction.DOWN) {
0.125 + pokemon.form.hitbox.height*pokemon.form.baseScale*0.5
} else {
0.125 + pokemon.form.hitbox.width*pokemon.form.baseScale*0.5
}
val posOffset = result.pos.offset(result.side, offset)
val traceDown = posOffset.traceDownwards(this.world, maxDistance = dropHeight.toFloat())
return if (traceDown == null || !pokemon.isPositionSafe(world, traceDown.blockPos)) {
null
} else {
return Vec3d(traceDown.location.x, traceDown.blockPos.up().toVec3d().y, traceDown.location.z)
}
} else if (!this.world.getBlockState(result.blockPos.up()).isSolid && pokemon.isPositionSafe(world, result.blockPos)) {
// If the player is targeting the top of a block, and the block above it isn't solid, it will spawn on that block
return Vec3d(result.pos.x, result.blockPos.up().toVec3d().y, result.pos.z)
}
return null
}
fun PlayerInventory.usableItems() = offHand + main
/**
@@ -124,41 +124,3 @@ val World.itemRegistry: Registry<Item>
get() = registryManager.get(RegistryKeys.ITEM)
val World.biomeRegistry: Registry<Biome>
get() = registryManager.get(RegistryKeys.BIOME)
fun Vec3d.traceDownwards(
world: World,
maxDistance: Float = 10F,
stepDistance: Float = 0.5F,
): TraceResult? {
var step = stepDistance
val startPos = Vec3d(x, y, z)
val direction = Vec3d(0.0, -1.0, 0.0)
var lastBlockPos = startPos.toBlockPos()
while (step <= maxDistance) {
val location = startPos.add(direction.multiply(step.toDouble()))
step += stepDistance
val blockPos = location.toBlockPos()
if (blockPos == lastBlockPos) {
continue
} else {
lastBlockPos = blockPos
}
val block = world.getBlockState(blockPos)
if (!block.isAir) {
val dir = findDirectionForIntercept(startPos, location, blockPos)
return TraceResult(
location = location,
blockPos = blockPos,
direction = dir
)
}
}
return null
}
@@ -1,21 +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://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.util.adapters
import com.google.gson.*
import net.minecraft.text.Text
import java.lang.reflect.Type
object PlainJsonTextAdapter : JsonDeserializer<Text>, JsonSerializer<Text> {
override fun deserialize(jElement: JsonElement, type: Type, context: JsonDeserializationContext): Text {
return Text.Serializer.fromJson(jElement) ?: throw IllegalArgumentException("Cannot resolve null text")
}
override fun serialize(text: Text, type: Type, context: JsonSerializationContext): JsonElement = JsonPrimitive(Text.Serializer.toJson(text))
}
@@ -7,12 +7,6 @@
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
}
],
"name": "cobblemon:black_gilded_chest"
}
],
@@ -7,12 +7,6 @@
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
}
],
"name": "cobblemon:blue_gilded_chest"
}
],
@@ -7,12 +7,6 @@
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
}
],
"name": "cobblemon:gilded_chest"
}
],
@@ -7,12 +7,6 @@
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
}
],
"name": "cobblemon:green_gilded_chest"
}
],
@@ -7,12 +7,6 @@
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
}
],
"name": "cobblemon:pink_gilded_chest"
}
],
@@ -7,12 +7,6 @@
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
}
],
"name": "cobblemon:white_gilded_chest"
}
],
@@ -7,12 +7,6 @@
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
}
],
"name": "cobblemon:yellow_gilded_chest"
}
],
@@ -29,11 +29,6 @@
"name": "minecraft:coal",
"weight": 6
},
{
"type": "minecraft:item",
"name": "minecraft:bone",
"weight": 6
},
{
"type": "minecraft:item",
"name": "cobblemon:big_root",
@@ -58,5 +58,5 @@
"rolls": 1.0
}
],
"random_sequence": "cobblemon:fossils/uncommon/prehistoric_hydrothermal_vent"
"random_sequence": "cobblemon:fossils/uncommon/prehistoric_hydrothermal_vents"
}
@@ -58,5 +58,5 @@
"rolls": 1.0
}
],
"random_sequence": "cobblemon:fossils/uncommon/prehistoric_vibrant_hydrothermal_vent"
"random_sequence": "cobblemon:fossils/uncommon/prehistoric_vibrant_hydrothermal_vents"
}
@@ -0,0 +1,52 @@
{
"type": "minecraft:archaeology",
"pools": [
{
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:copper_ingot",
"weight": 10
},
{
"type": "minecraft:item",
"name": "minecraft:iron_ingot",
"weight": 5
},
{
"type": "minecraft:item",
"name": "minecraft:gold_ingot",
"weight": 1
},
{
"type": "minecraft:item",
"name": "cobblemon:relic_coin",
"weight": 25
},
{
"type": "minecraft:item",
"name": "cobblemon:dome_sherd",
"weight": 1
},
{
"type": "minecraft:item",
"name": "cobblemon:psychic_gem",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:dark_gem",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:rock_gem",
"weight": 5
}
],
"rolls": 1.0
}
],
"random_sequence": "cobblemon:ruins/common/luna_henge_ruins"
}
@@ -0,0 +1,52 @@
{
"type": "minecraft:archaeology",
"pools": [
{
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:copper_ingot",
"weight": 10
},
{
"type": "minecraft:item",
"name": "minecraft:iron_ingot",
"weight": 5
},
{
"type": "minecraft:item",
"name": "minecraft:gold_ingot",
"weight": 1
},
{
"type": "minecraft:item",
"name": "cobblemon:relic_coin",
"weight": 25
},
{
"type": "minecraft:item",
"name": "cobblemon:helix_sherd",
"weight": 1
},
{
"type": "minecraft:item",
"name": "cobblemon:psychic_gem",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:fire_gem",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:rock_gem",
"weight": 5
}
],
"rolls": 1.0
}
],
"random_sequence": "cobblemon:ruins/common/sol_henge_ruins"
}
@@ -0,0 +1,42 @@
{
"type": "minecraft:archaeology",
"pools": [
{
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:copper_ingot",
"weight": 10
},
{
"type": "minecraft:item",
"name": "minecraft:iron_ingot",
"weight": 5
},
{
"type": "minecraft:item",
"name": "minecraft:gold_ingot",
"weight": 1
},
{
"type": "minecraft:item",
"name": "cobblemon:relic_coin",
"weight": 25
},
{
"type": "minecraft:item",
"name": "cobblemon:nostalgic_sherd",
"weight": 1
},
{
"type": "minecraft:item",
"name": "cobblemon:rock_gem",
"weight": 10
}
],
"rolls": 1.0
}
],
"random_sequence": "cobblemon:ruins/common/stonjourner_henge_ruins"
}
@@ -79,7 +79,7 @@
{
"rolls": {
"type": "minecraft:uniform",
"max": 3.0,
"max": 1.0,
"min": 0.0
},
"bonus_rolls": 0.0,
@@ -93,7 +93,7 @@
"function": "set_count",
"count": {
"min": 1,
"max": 2
"max": 3
}
}
]
@@ -103,8 +103,8 @@
{
"rolls": {
"type": "minecraft:uniform",
"max": 5.0,
"min": 4.0
"max": 3.0,
"min": 2.0
},
"bonus_rolls": 0.0,
"entries": [
@@ -149,7 +149,7 @@
{
"type": "minecraft:item",
"name": "cobblemon:link_cable",
"weight": 1
"weight": 2
},
{
"type": "minecraft:item",
@@ -159,7 +159,7 @@
"function": "enchant_randomly"
}
],
"weight": 1
"weight": 2
},
{
"type": "minecraft:item",
@@ -169,7 +169,7 @@
"function": "enchant_randomly"
}
],
"weight": 1
"weight": 2
},
{
"type": "minecraft:item",
@@ -179,7 +179,7 @@
"function": "enchant_randomly"
}
],
"weight": 1
"weight": 2
},
{
"type": "minecraft:item",
@@ -189,7 +189,7 @@
"function": "enchant_randomly"
}
],
"weight": 1
"weight": 2
},
{
"type": "minecraft:item",
@@ -199,7 +199,7 @@
"function": "enchant_randomly"
}
],
"weight": 1
"weight": 2
},
{
"type": "minecraft:item",
@@ -209,12 +209,12 @@
"function": "enchant_randomly"
}
],
"weight": 1
"weight": 2
},
{
"type": "minecraft:item",
"name": "cobblemon:automaton_armor_trim_smithing_template",
"weight": 2
"weight": 1
}
]
}
@@ -27,7 +27,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:nostalgic_sherd",
"weight": 30
"weight": 15
},
{
"type": "minecraft:item",
"name": "cobblemon:bygone_sherd",
"weight": 15
}
],
"rolls": 1.0
@@ -7,12 +7,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:kings_rock",
"weight": 5
"weight": 3
},
{
"type": "minecraft:item",
"name": "cobblemon:cracked_pot",
"weight": 5
"weight": 3
},
{
"type": "minecraft:item",
@@ -22,7 +22,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:bygone_sherd",
"weight": 20
"weight": 10
},
{
"type": "minecraft:item",
"name": "cobblemon:dome_sherd",
"weight": 10
}
],
"rolls": 1.0
@@ -27,7 +27,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:capture_sherd",
"weight": 30
"weight": 15
},
{
"type": "minecraft:item",
"name": "cobblemon:suspicious_sherd",
"weight": 15
}
],
"rolls": 1.0
@@ -27,7 +27,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:capture_sherd",
"weight": 30
"weight": 15
},
{
"type": "minecraft:item",
"name": "cobblemon:dome_sherd",
"weight": 15
}
],
"rolls": 1.0
@@ -52,7 +52,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:helix_sherd",
"weight": 90
"weight": 45
},
{
"type": "minecraft:item",
"name": "cobblemon:nostalgic_sherd",
"weight": 45
}
],
"rolls": 1.0
@@ -27,7 +27,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:helix_sherd",
"weight": 70
"weight": 35
},
{
"type": "minecraft:item",
"name": "cobblemon:bygone_sherd",
"weight": 35
}
],
"rolls": 1.0
@@ -52,7 +52,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:suspicious_sherd",
"weight": 90
"weight": 45
},
{
"type": "minecraft:item",
"name": "cobblemon:capture_sherd",
"weight": 45
}
],
"rolls": 1.0
@@ -0,0 +1,32 @@
{
"type": "minecraft:archaeology",
"pools": [
{
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "cobblemon:covert_cloak",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:spell_tag",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:dome_sherd",
"weight": 10
},
{
"type": "minecraft:item",
"name": "cobblemon:suspicious_sherd",
"weight": 10
}
],
"rolls": 1.0
}
],
"random_sequence": "cobblemon:ruins/uncommon/luna_henge_ruins"
}
@@ -22,7 +22,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:dome_sherd",
"weight": 30
"weight": 15
},
{
"type": "minecraft:item",
"name": "cobblemon:helix_sherd",
"weight": 15
}
],
"rolls": 1.0
@@ -27,7 +27,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:nostalgic_sherd",
"weight": 30
"weight": 15
},
{
"type": "minecraft:item",
"name": "cobblemon:bygone_sherd",
"weight": 15
}
],
"rolls": 1.0
@@ -0,0 +1,32 @@
{
"type": "minecraft:archaeology",
"pools": [
{
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "cobblemon:kings_rock",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:cleanse_tag",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:helix_sherd",
"weight": 10
},
{
"type": "minecraft:item",
"name": "cobblemon:capture_sherd",
"weight": 10
}
],
"rolls": 1.0
}
],
"random_sequence": "cobblemon:ruins/uncommon/luna_henge_ruins"
}
@@ -0,0 +1,32 @@
{
"type": "minecraft:archaeology",
"pools": [
{
"bonus_rolls": 0.0,
"entries": [
{
"type": "minecraft:item",
"name": "cobblemon:covert_cloak",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:shell_bell",
"weight": 5
},
{
"type": "minecraft:item",
"name": "cobblemon:nostalgic_sherd",
"weight": 10
},
{
"type": "minecraft:item",
"name": "cobblemon:bygone_sherd",
"weight": 10
}
],
"rolls": 1.0
}
],
"random_sequence": "cobblemon:ruins/uncommon/stonjourner_henge_ruins"
}
@@ -32,7 +32,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:helix_sherd",
"weight": 40
"weight": 20
},
{
"type": "minecraft:item",
"name": "cobblemon:dome_sherd",
"weight": 20
}
],
"rolls": 1.0
@@ -22,7 +22,12 @@
{
"type": "minecraft:item",
"name": "cobblemon:suspicious_sherd",
"weight": 40
"weight": 20
},
{
"type": "minecraft:item",
"name": "cobblemon:capture_sherd",
"weight": 20
}
],
"rolls": 1.0
@@ -12,7 +12,7 @@
{
"type": "minecraft:item",
"name": "cobblemon:exp_candy_xs",
"weight": 32,
"weight": 24,
"functions": [
{
"function": "set_count",
@@ -26,7 +26,7 @@
{
"type": "minecraft:item",
"name": "cobblemon:exp_candy_s",
"weight": 16,
"weight": 15,
"functions": [
{
"function": "set_count",
@@ -40,20 +40,6 @@
{
"type": "minecraft:item",
"name": "cobblemon:exp_candy_m",
"weight": 8,
"functions": [
{
"function": "set_count",
"count": {
"min": 1,
"max": 2
}
}
]
},
{
"type": "minecraft:item",
"name": "cobblemon:exp_candy_l",
"weight": 1,
"functions": [
{
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.kanto"
},
"pokemon": [
"Bulbasaur level=10",
"Charmander level=10",
"Squirtle level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.johto"
},
"pokemon": [
"Chikorita level=10",
"Cyndaquil level=10",
"Totodile level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.hoenn"
},
"pokemon": [
"Treecko level=10",
"Torchic level=10",
"Mudkip level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.sinnoh"
},
"pokemon": [
"Turtwig level=10",
"Chimchar level=10",
"Piplup level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.unova"
},
"pokemon": [
"Snivy level=10",
"Tepig level=10",
"Oshawott level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.kalos"
},
"pokemon": [
"Chespin level=10",
"Fennekin level=10",
"Froakie level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.alola"
},
"pokemon": [
"Rowlet level=10",
"Litten level=10",
"Popplio level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.galar"
},
"pokemon": [
"Grookey level=10",
"Scorbunny level=10",
"Sobble level=10"
]
}
@@ -1,10 +0,0 @@
{
"displayName": {
"translate": "cobblemon.starterselection.category.paldea"
},
"pokemon": [
"Sprigatito level=10",
"Fuecoco level=10",
"Quaxly level=10"
]
}

Some files were not shown because too many files have changed in this diff Show More