Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aeedec0b6d | ||
|
|
a37fdf709a | ||
|
|
5023f28d04 | ||
|
|
67de28386a | ||
|
|
cd37d3538e | ||
|
|
3d03ee5eb3 | ||
|
|
0b685ed453 | ||
|
|
115e990df6 | ||
|
|
2ee38f0a29 | ||
|
|
425c41bc4b | ||
|
|
9f7a99b583 | ||
|
|
d60d828e93 | ||
|
|
5d7e6f6487 | ||
|
|
76d9137ae4 | ||
|
|
9f6c44fd75 | ||
|
|
6c086f5555 | ||
|
|
100bf7fee4 | ||
|
|
be043a0ea1 | ||
|
|
a902f4a8a3 | ||
|
|
442f54839f | ||
|
|
92653c1307 | ||
|
|
c372114da5 | ||
|
|
43da7ecece | ||
|
|
f99168f1c3 | ||
|
|
adce1d8838 | ||
|
|
e95c82b5af | ||
|
|
dd273ffef2 | ||
|
|
82548b5383 | ||
|
|
ad73202e17 | ||
|
|
2965a04742 |
No files matched your search
@@ -67,24 +67,13 @@ 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.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.*
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
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.command.argument.*
|
||||
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
|
||||
@@ -105,13 +94,7 @@ 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.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.util.*
|
||||
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
|
||||
@@ -132,13 +115,16 @@ 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"
|
||||
@@ -166,7 +152,6 @@ 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
|
||||
@@ -477,27 +462,6 @@ 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 != pokemon.nickname }?.let { return false }
|
||||
nickname?.takeIf { it.string != pokemon.nickname?.string }?.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 != properties.nickname }?.let { return false }
|
||||
nickname?.takeIf { it.string != properties.nickname?.string }?.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 }
|
||||
|
||||
+6
-11
@@ -6,24 +6,19 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.config.starter
|
||||
package com.cobblemon.mod.common.api.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 name: String,
|
||||
val displayName: String,
|
||||
val displayName: Text,
|
||||
val pokemon: List<PokemonProperties>
|
||||
) {
|
||||
fun asRenderableStarterCategory() = RenderableStarterCategory(name, displayName, pokemon.map { it.asRenderablePokemon() })
|
||||
}
|
||||
)
|
||||
|
||||
data class RenderableStarterCategory(
|
||||
val name: String,
|
||||
val displayName: String,
|
||||
val displayName: Text,
|
||||
val pokemon: List<RenderablePokemon>
|
||||
) {
|
||||
val displayNameText = displayName.asTranslated()
|
||||
}
|
||||
)
|
||||
@@ -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 getStarterList(player: ServerPlayerEntity): List<StarterCategory>
|
||||
fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory>
|
||||
fun handleJoin(player: ServerPlayerEntity)
|
||||
fun requestStarterChoice(player: ServerPlayerEntity)
|
||||
fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int)
|
||||
fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.starterConfig.allowStarterOnJoin,
|
||||
starterLocked = !Cobblemon.config.allowStarterOnJoin,
|
||||
starterSelected = false,
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
|
||||
@@ -9,15 +9,23 @@
|
||||
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
|
||||
|
||||
@@ -28,7 +36,18 @@ class TumblestoneBlock(
|
||||
height: Int,
|
||||
xzOffset: Int,
|
||||
nextStage: Block?
|
||||
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
|
||||
) : 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")
|
||||
}
|
||||
|
||||
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
|
||||
if (stage == MAX_STAGE) return false
|
||||
val iterator: Iterator<BlockPos> =
|
||||
@@ -43,4 +62,31 @@ 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,20 +16,15 @@ 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.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.block.*
|
||||
import net.minecraft.entity.LivingEntity
|
||||
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
|
||||
@@ -38,12 +33,7 @@ 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.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.*
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
@@ -106,7 +96,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 {
|
||||
@@ -258,4 +248,17 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -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 = 1
|
||||
entity.beamMode = 2
|
||||
afterOnServer(seconds = SendOutPokemonHandler.SEND_OUT_DURATION) {
|
||||
entity.beamMode = 0
|
||||
}
|
||||
|
||||
+62
-38
@@ -11,7 +11,6 @@ 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
|
||||
@@ -22,7 +21,6 @@ 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
|
||||
@@ -34,11 +32,8 @@ 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
|
||||
@@ -105,6 +100,8 @@ 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 */ }
|
||||
@@ -141,39 +138,37 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
currentEntity.after(seconds = POKEBALL_AIR_TIME){
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
ballDone = true
|
||||
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
|
||||
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 ->
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +185,35 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
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 -> {
|
||||
// Scaling down into pokeball
|
||||
entityScaleModifier = 1F
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
@@ -203,6 +226,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> { /* Do nothing */ }
|
||||
}
|
||||
} else if (data == PokemonEntity.LABEL_LEVEL) {
|
||||
currentEntity.dataTracker.get(PokemonEntity.LABEL_LEVEL)
|
||||
|
||||
+21
-17
@@ -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.config.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.api.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,6 +40,7 @@ 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
|
||||
@@ -47,7 +48,7 @@ import net.minecraft.text.Text
|
||||
* @author Qu
|
||||
* @since 2022-06-18
|
||||
*/
|
||||
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
|
||||
class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
|
||||
|
||||
companion object {
|
||||
// Size of UI at scale 1
|
||||
@@ -64,8 +65,9 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
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 currentCategory: RenderableStarterCategory
|
||||
private lateinit var currentCategoryId: Identifier
|
||||
private lateinit var modelWidget: ModelWidget
|
||||
private lateinit var currentPokemon: RenderablePokemon
|
||||
private lateinit var typeWidget: TypeWidget
|
||||
@@ -95,7 +97,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
paneWidth = 71, paneHeight = BASE_HEIGHT - 11,
|
||||
topOffset = 6, bottomOffset = 5,
|
||||
entryHeight = 20, entryWidth = 57,
|
||||
categories = categories,
|
||||
categories = this.categories,
|
||||
x = x - 2, y = y + 8,
|
||||
starterSelectionScreen = this
|
||||
)
|
||||
@@ -120,8 +122,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
addDrawableChild(rightButton)
|
||||
addDrawableChild(leftButton)
|
||||
|
||||
currentCategory = categories.first()
|
||||
currentPokemon = currentCategory.pokemon[currentSelection]
|
||||
currentCategoryId = categories.firstKey()
|
||||
currentPokemon = this.currentCategory().pokemon[currentSelection]
|
||||
|
||||
with(currentPokemon) {
|
||||
modelWidget = ModelWidget(
|
||||
@@ -142,11 +144,11 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
) {
|
||||
CobblemonNetwork.sendPacketToServer(
|
||||
SelectStarterPacket(
|
||||
categoryName = currentCategory.name,
|
||||
categoryId = currentCategoryId,
|
||||
selected = currentSelection
|
||||
)
|
||||
)
|
||||
MinecraftClient.getInstance().setScreen(null)
|
||||
this.close()
|
||||
}
|
||||
|
||||
addDrawableChild(selectionButton)
|
||||
@@ -161,7 +163,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
starterRoundaboutLeft = StarterRoundabout(
|
||||
pX = x + 89, pY = height / 2 + 84,
|
||||
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
|
||||
pokemon = currentCategory.pokemon[leftOfCurrentSelection()],
|
||||
pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()],
|
||||
clickAction = { _, _ -> this.left() },
|
||||
rotationVector = this.modelWidget.rotVec
|
||||
)
|
||||
@@ -169,7 +171,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
starterRoundaboutRight = StarterRoundabout(
|
||||
pX = x + 149, pY = height / 2 + 84,
|
||||
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
|
||||
pokemon = currentCategory.pokemon[rightOfCurrentSelection()],
|
||||
pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()],
|
||||
clickAction = { _, _ -> this.right() },
|
||||
rotationVector = this.modelWidget.rotVec
|
||||
)
|
||||
@@ -276,8 +278,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
|
||||
fun changeCategory(category: RenderableStarterCategory) {
|
||||
currentCategory = category
|
||||
fun changeCategory(id: Identifier) {
|
||||
currentCategoryId = id
|
||||
currentSelection = 0
|
||||
updateSelection()
|
||||
}
|
||||
@@ -288,7 +290,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
updateSelection()
|
||||
}
|
||||
|
||||
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= currentCategory.pokemon.size - 1) currentSelection + 1 else 0
|
||||
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= this.currentCategory().pokemon.size - 1) currentSelection + 1 else 0
|
||||
|
||||
private fun left() {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
@@ -296,16 +298,16 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
updateSelection()
|
||||
}
|
||||
|
||||
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) currentCategory.pokemon.size - 1 else currentSelection - 1
|
||||
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) this.currentCategory().pokemon.size - 1 else currentSelection - 1
|
||||
|
||||
private fun updateSelection() {
|
||||
currentPokemon = currentCategory.pokemon[currentSelection].also {
|
||||
currentPokemon = this.currentCategory().pokemon[currentSelection].also {
|
||||
modelWidget.pokemon = it
|
||||
typeWidget = typeWidget(it, (width - BASE_WIDTH) / 2, (height - BASE_HEIGHT) / 2)
|
||||
}
|
||||
starterRoundaboutLeft.pokemon = currentCategory.pokemon[leftOfCurrentSelection()]
|
||||
starterRoundaboutLeft.pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()]
|
||||
starterRoundaboutCenter.pokemon = currentPokemon
|
||||
starterRoundaboutRight.pokemon = currentCategory.pokemon[rightOfCurrentSelection()]
|
||||
starterRoundaboutRight.pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()]
|
||||
}
|
||||
|
||||
private fun typeWidget(pokemon: RenderablePokemon, x: Int, y: Int) : TypeWidget {
|
||||
@@ -324,5 +326,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
|
||||
)
|
||||
}
|
||||
|
||||
private fun currentCategory(): RenderableStarterCategory = this.categories[this.currentCategoryId]!!
|
||||
|
||||
override fun shouldPause() = true
|
||||
}
|
||||
+9
-19
@@ -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.config.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.api.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: List<RenderableStarterCategory>,
|
||||
private val categories: Map<Identifier, RenderableStarterCategory>,
|
||||
val x: Int, val y: Int,
|
||||
private val minecraft: MinecraftClient = MinecraftClient.getInstance(),
|
||||
private val starterSelectionScreen: StarterSelectionScreen
|
||||
@@ -44,7 +44,6 @@ 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")
|
||||
}
|
||||
|
||||
@@ -53,19 +52,12 @@ class CategoryList(
|
||||
this.setRenderHorizontalShadows(false)
|
||||
this.setRenderBackground(false)
|
||||
this.setRenderSelection(false)
|
||||
}
|
||||
|
||||
private var entriesCreated = false
|
||||
|
||||
private fun createEntries() = categories.map {
|
||||
Category(it)
|
||||
this.categories.forEach { (id, category) ->
|
||||
this.addEntry(Category(id, category))
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (!entriesCreated) {
|
||||
createEntries().forEach { addEntry(it) }
|
||||
entriesCreated = true
|
||||
}
|
||||
context.enableScissor(
|
||||
x,
|
||||
y,
|
||||
@@ -80,15 +72,13 @@ 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 category: RenderableStarterCategory) : Entry<Category>() {
|
||||
inner class Category(private val id: Identifier, private val category: RenderableStarterCategory) : Entry<Category>() {
|
||||
|
||||
override fun render(
|
||||
context: DrawContext,
|
||||
@@ -121,7 +111,7 @@ class CategoryList(
|
||||
)
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = category.displayNameText,
|
||||
text = category.displayName.copy(),
|
||||
x = x + 28,
|
||||
y = y + 4.5F,
|
||||
scale = 1F,
|
||||
@@ -132,7 +122,7 @@ class CategoryList(
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
starterSelectionScreen.changeCategory(category = category)
|
||||
starterSelectionScreen.changeCategory(this.id)
|
||||
minecraft.soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
return true
|
||||
}
|
||||
|
||||
+5
@@ -67,6 +67,11 @@ abstract class PokeBallPoseableState : PoseableEntityState<EmptyPokeBallEntity>(
|
||||
setStatefulAnimations(currentModel!!.bedrockStateful(group, "critical"))
|
||||
}
|
||||
}
|
||||
EmptyPokeBallEntity.CaptureState.BROKEN_FREE -> {
|
||||
doLater {
|
||||
setStatefulAnimations(currentModel!!.bedrockStateful(group, "break"))
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -32,7 +32,6 @@ 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.*
|
||||
@@ -53,6 +52,7 @@ 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 == 1,
|
||||
entity.beamMode,
|
||||
entity,
|
||||
partialTicks,
|
||||
poseMatrix,
|
||||
@@ -130,7 +130,7 @@ class PokemonRenderer(
|
||||
|
||||
fun renderTransition(
|
||||
modelNow: PoseableEntityModel<PokemonEntity>,
|
||||
sendingOut: Boolean,
|
||||
beamMode: Int,
|
||||
entity: PokemonEntity,
|
||||
partialTicks: Float,
|
||||
poseMatrix: MatrixStack,
|
||||
@@ -139,7 +139,7 @@ class PokemonRenderer(
|
||||
clientDelegate: PokemonClientDelegate
|
||||
) {
|
||||
val s = clientDelegate.secondsSinceBeamEffectStarted
|
||||
if (modelNow is PokemonPoseableModel && !sendingOut) {
|
||||
if (modelNow is PokemonPoseableModel && beamMode == 3) {
|
||||
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 && sendingOut) {
|
||||
if (clientDelegate.sendOutPosition == null && beamMode == 1) {
|
||||
clientDelegate.sendOutPosition = beamSourcePosition
|
||||
} else if (sendingOut) {
|
||||
} else if (beamMode == 1) {
|
||||
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 (sendingOut && !clientDelegate.ballDone){
|
||||
if (beamMode == 1 && !clientDelegate.ballDone){
|
||||
if(entity.pokemon.caughtBall.name.toString().contains("beast")){
|
||||
// get rotation angle on x acis for facingDir
|
||||
// get rotation angle on x-axis 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 (!sendingOut) {
|
||||
if (beamMode == 3) {
|
||||
renderBeam(poseMatrix, partialTicks, entity, phaseTarget, buffer, offsetDirection)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-16
@@ -9,11 +9,7 @@
|
||||
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
|
||||
@@ -38,18 +34,7 @@ object OpenStarterScreenCommand {
|
||||
|
||||
private fun execute(context: CommandContext<ServerCommandSource>) : Int {
|
||||
val player = EntityArgumentType.getPlayer(context, "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)))
|
||||
Cobblemon.starterHandler.requestStarterChoice(player)
|
||||
return SINGLE_SUCCESS
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,12 @@ 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
|
||||
|
||||
|
||||
@@ -1,128 +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.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,6 +29,7 @@ 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
|
||||
@@ -63,6 +64,7 @@ object CobblemonDataProvider : DataProvider {
|
||||
this.register(Abilities)
|
||||
this.register(PokemonSpecies)
|
||||
this.register(SpeciesAdditions)
|
||||
this.register(StarterRegistry)
|
||||
this.register(PokeBalls)
|
||||
this.register(PropertiesCompletionProvider)
|
||||
this.register(SpawnDetailPresets)
|
||||
|
||||
+25
-15
@@ -21,9 +21,8 @@ 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.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.Schedulable
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
@@ -37,17 +36,12 @@ 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.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 com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.entity.EntityDimensions
|
||||
import net.minecraft.entity.EntityPose
|
||||
import net.minecraft.entity.EntityType
|
||||
@@ -70,6 +64,7 @@ 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 {
|
||||
@@ -78,7 +73,8 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
|
||||
FALL,
|
||||
SHAKE,
|
||||
CAPTURED,
|
||||
CAPTURED_CRITICAL
|
||||
CAPTURED_CRITICAL,
|
||||
BROKEN_FREE
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -135,6 +131,7 @@ 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)
|
||||
@@ -348,25 +345,27 @@ 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 = 1
|
||||
pokemon.beamMode = 2
|
||||
pokemon.isInvisible = false
|
||||
|
||||
if (pokemon.battleId == null) {
|
||||
pokemon.pokemon.status?.takeIf { it.status == Statuses.SLEEP }?.let { pokemon.pokemon.status = null }
|
||||
}
|
||||
|
||||
after(seconds = 0.25F) {
|
||||
captureState = CaptureState.BROKEN_FREE
|
||||
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_OPEN, volume = 0.8F)
|
||||
|
||||
after(seconds = 1F) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -381,6 +380,17 @@ 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
|
||||
@@ -389,7 +399,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 = 2
|
||||
pokemonEntity.beamMode = 3
|
||||
}
|
||||
|
||||
after(seconds = 2.2F) {
|
||||
|
||||
@@ -28,7 +28,6 @@ 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
|
||||
@@ -57,10 +56,6 @@ 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
|
||||
@@ -107,6 +102,8 @@ 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(
|
||||
@@ -333,7 +330,7 @@ open class PokemonEntity(
|
||||
* Prevents fire type Pokémon from taking fire damage.
|
||||
*/
|
||||
override fun isFireImmune(): Boolean {
|
||||
return FIRE in pokemon.types || !behaviour.moving.swim.hurtByLava
|
||||
return pokemon.isFireImmune()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,10 +375,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, 2)
|
||||
dataTracker.set(BEAM_MODE, 3)
|
||||
val state = pokemon.state
|
||||
after(seconds = SEND_OUT_DURATION) {
|
||||
// only recall if the pokemon hasn't been recalled yet for this state
|
||||
// only recall if the Pokémon hasn't been recalled yet for this state
|
||||
if (state == pokemon.state) {
|
||||
pokemon.recall()
|
||||
}
|
||||
|
||||
+1
-1
@@ -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, 2)
|
||||
entity.dataTracker.set(PokemonEntity.BEAM_MODE, 3)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-7
@@ -16,6 +16,7 @@ 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
|
||||
@@ -75,9 +76,9 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
|
||||
entity.setSidewaysSpeed(sidewaysMovement)
|
||||
state = State.WAIT
|
||||
} else if (state == State.MOVE_TO) {
|
||||
val xDist = targetX - entity.x
|
||||
val zDist = targetZ - entity.z
|
||||
val yDist = targetY - entity.y
|
||||
var xDist = targetX - entity.x
|
||||
var zDist = targetZ - entity.z
|
||||
var yDist = targetY - entity.y
|
||||
|
||||
val horizontalDistanceFromTarget = xDist * xDist + zDist * zDist
|
||||
val closeHorizontally = horizontalDistanceFromTarget < VERY_CLOSE
|
||||
@@ -99,12 +100,13 @@ 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(
|
||||
refine(xDist),
|
||||
if (inFluid || pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) refine(yDist + 0.05) else 0.0,
|
||||
refine(zDist)
|
||||
xDist,
|
||||
refine(yDist + 0.05), // + 0.05 for dealing with swimming out of water, they otherwise get stuck on the lip
|
||||
zDist
|
||||
)
|
||||
|
||||
val direction = fullDistance.normalize()
|
||||
@@ -112,8 +114,13 @@ 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 {
|
||||
val forwardSpeed = min(adjustedSpeed, sqrt(horizontalDistanceFromTarget).toFloat())
|
||||
// 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))
|
||||
entity.movementSpeed = forwardSpeed
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,14 @@ 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
|
||||
@@ -36,8 +38,12 @@ class PokeBallItem(
|
||||
|
||||
private fun throwPokeBall(world: World, player: ServerPlayerEntity) {
|
||||
val pokeBallEntity = EmptyPokeBallEntity(pokeBall, player.world, player).apply {
|
||||
// setPos(player.x, player.y + player.standingEyeHeight - 0.2, player.z)
|
||||
setVelocity(player, player.pitch - 5, player.yaw, 0.0f, pokeBall.throwPower, 1.0f)
|
||||
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)
|
||||
setPosition(pos.add(velocity.normalize().multiply(1.0)))
|
||||
owner = player
|
||||
}
|
||||
|
||||
+13
-28
@@ -9,48 +9,33 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.starter
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.config.starter.StarterCategory
|
||||
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.api.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 internal constructor(val categories: List<RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
|
||||
class OpenStarterUIPacket(val categories: Map<Identifier, RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
|
||||
|
||||
override val id = ID
|
||||
|
||||
constructor(categories: Collection<StarterCategory>) : this(categories.map { it.asRenderableStarterCategory() })
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
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) }
|
||||
buffer.writeMap(this.categories, PacketByteBuf::writeIdentifier) { categoryBuffer, category ->
|
||||
categoryBuffer.writeText(category.displayName)
|
||||
categoryBuffer.writeCollection(category.pokemon) { pokemonBuffer, renderable ->
|
||||
renderable.saveToBuffer(pokemonBuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("open_starter")
|
||||
fun decode(buffer: PacketByteBuf): OpenStarterUIPacket {
|
||||
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
|
||||
)
|
||||
)
|
||||
val categories = buffer.readMap(PacketByteBuf::readIdentifier) { categoryBuffer ->
|
||||
val displayName = buffer.readText()
|
||||
val renderablePokemon = categoryBuffer.readList(RenderablePokemon::loadFromBuffer)
|
||||
return@readMap RenderableStarterCategory(displayName, renderablePokemon)
|
||||
}
|
||||
return OpenStarterUIPacket(categories)
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SetClientPlayerDataPacket(val promptStarter: Boolean, val starterLocked: B
|
||||
playerData: PlayerData,
|
||||
resetStarterPrompt: Boolean? = null
|
||||
) : this(
|
||||
!playerData.starterPrompted || !Cobblemon.starterConfig.promptStarterOnceOnly,
|
||||
!playerData.starterPrompted || !Cobblemon.config.promptStarterOnceOnly,
|
||||
playerData.starterLocked,
|
||||
playerData.starterSelected,
|
||||
playerData.starterUUID,
|
||||
|
||||
+4
-3
@@ -11,16 +11,17 @@ 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 categoryName: String, val selected: Int) : NetworkPacket<SelectStarterPacket> {
|
||||
class SelectStarterPacket(val categoryId: Identifier, val selected: Int) : NetworkPacket<SelectStarterPacket> {
|
||||
override val id = ID
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(this.categoryName)
|
||||
buffer.writeIdentifier(this.categoryId)
|
||||
buffer.writeInt(this.selected)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("select_starter")
|
||||
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readString(), buffer.readInt())
|
||||
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readIdentifier(), buffer.readInt())
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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.categoryName, packet.selected)
|
||||
Cobblemon.starterHandler.chooseStarter(player, packet.categoryId, packet.selected)
|
||||
}
|
||||
}
|
||||
+5
-7
@@ -13,11 +13,10 @@ 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.toVec3d
|
||||
import com.cobblemon.mod.common.util.traceBlockCollision
|
||||
import com.cobblemon.mod.common.util.raycastSafeSendout
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.RaycastContext
|
||||
|
||||
object SendOutPokemonHandler : ServerNetworkPacketHandler<SendOutPokemonPacket> {
|
||||
|
||||
@@ -31,11 +30,10 @@ object SendOutPokemonHandler : ServerNetworkPacketHandler<SendOutPokemonPacket>
|
||||
return
|
||||
}
|
||||
val state = pokemon.state
|
||||
|
||||
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)
|
||||
val position = player.raycastSafeSendout(pokemon, 12.0, 5.0, RaycastContext.FluidHandling.ANY)
|
||||
|
||||
if (position != null) {
|
||||
pokemon.sendOutWithAnimation(player, player.serverWorld, position)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -49,6 +49,7 @@ 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
|
||||
@@ -72,6 +73,7 @@ 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
|
||||
@@ -80,6 +82,7 @@ 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
|
||||
@@ -88,9 +91,11 @@ 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
|
||||
@@ -534,6 +539,7 @@ 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)
|
||||
@@ -614,6 +620,52 @@ 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.
|
||||
|
||||
@@ -1,73 +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.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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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,11 +13,32 @@ 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)
|
||||
|
||||
@@ -29,27 +50,6 @@ 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, -1 * (1 - (conflict.z - box.minZ) + (1.0 / 8.0))))
|
||||
result = result.add(Vec3d(0.0, 0.0, (conflict.z - box.maxZ) - (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(-1 * (1 - (conflict.x - box.minX) + (1.0 / 8.0)), 0.0, 0.0))
|
||||
result = result.add(Vec3d((conflict.x - box.maxX) - (1.0 / 8.0), 0.0, 0.0))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
@@ -89,12 +89,90 @@ 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.setPosition(result)
|
||||
return true
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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 java.util.UUID
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
@@ -30,10 +30,11 @@ 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.math.BlockPos
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.*
|
||||
import net.minecraft.world.RaycastContext
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
// Stuff like getting their party
|
||||
fun ServerPlayerEntity.party() = Cobblemon.storage.getParty(this)
|
||||
@@ -251,6 +252,91 @@ 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,3 +124,41 @@ 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
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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,6 +7,12 @@
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:copy_name",
|
||||
"source": "block_entity"
|
||||
}
|
||||
],
|
||||
"name": "cobblemon:black_gilded_chest"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:copy_name",
|
||||
"source": "block_entity"
|
||||
}
|
||||
],
|
||||
"name": "cobblemon:blue_gilded_chest"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:copy_name",
|
||||
"source": "block_entity"
|
||||
}
|
||||
],
|
||||
"name": "cobblemon:gilded_chest"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:copy_name",
|
||||
"source": "block_entity"
|
||||
}
|
||||
],
|
||||
"name": "cobblemon:green_gilded_chest"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:copy_name",
|
||||
"source": "block_entity"
|
||||
}
|
||||
],
|
||||
"name": "cobblemon:pink_gilded_chest"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:copy_name",
|
||||
"source": "block_entity"
|
||||
}
|
||||
],
|
||||
"name": "cobblemon:white_gilded_chest"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"functions": [
|
||||
{
|
||||
"function": "minecraft:copy_name",
|
||||
"source": "block_entity"
|
||||
}
|
||||
],
|
||||
"name": "cobblemon:yellow_gilded_chest"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.kanto"
|
||||
},
|
||||
"pokemon": [
|
||||
"Bulbasaur level=10",
|
||||
"Charmander level=10",
|
||||
"Squirtle level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.johto"
|
||||
},
|
||||
"pokemon": [
|
||||
"Chikorita level=10",
|
||||
"Cyndaquil level=10",
|
||||
"Totodile level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.hoenn"
|
||||
},
|
||||
"pokemon": [
|
||||
"Treecko level=10",
|
||||
"Torchic level=10",
|
||||
"Mudkip level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.sinnoh"
|
||||
},
|
||||
"pokemon": [
|
||||
"Turtwig level=10",
|
||||
"Chimchar level=10",
|
||||
"Piplup level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.unova"
|
||||
},
|
||||
"pokemon": [
|
||||
"Snivy level=10",
|
||||
"Tepig level=10",
|
||||
"Oshawott level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.kalos"
|
||||
},
|
||||
"pokemon": [
|
||||
"Chespin level=10",
|
||||
"Fennekin level=10",
|
||||
"Froakie level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.alola"
|
||||
},
|
||||
"pokemon": [
|
||||
"Rowlet level=10",
|
||||
"Litten level=10",
|
||||
"Popplio level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.galar"
|
||||
},
|
||||
"pokemon": [
|
||||
"Grookey level=10",
|
||||
"Scorbunny level=10",
|
||||
"Sobble level=10"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"displayName": {
|
||||
"translate": "cobblemon.starterselection.category.paldea"
|
||||
},
|
||||
"pokemon": [
|
||||
"Sprigatito level=10",
|
||||
"Fuecoco level=10",
|
||||
"Quaxly level=10"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user