forked from mirrors/cobblemon
Merge branch 'complex-spawn-buckets' into 'main'
Rebuilt spawn buckets to be controlled by the spawner, allowing new buckets in specific contexts, and flattened habitat configuration classes See merge request cable-mc/cobblemon!2267
This commit is contained in:
@@ -340,6 +340,8 @@
|
||||
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
|
||||
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
|
||||
- Separated all the functions of MoLangFunctions.kt into their own files.
|
||||
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
|
||||
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `moveset_builders` datapack folder for different styles of movesets.
|
||||
|
||||
+2
-3
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.entity
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
|
||||
@@ -21,7 +20,7 @@ class SpawnBucketChosenEvent(
|
||||
/** The cause of the spawn. */
|
||||
val spawnCause: SpawnCause,
|
||||
/** The bucket that was chosen. You can alter this. */
|
||||
var bucket: SpawnBucket,
|
||||
var bucket: String,
|
||||
/** The final weights that were used to make the choice. */
|
||||
val bucketWeights: Map<SpawnBucket, Float>
|
||||
val bucketWeights: Map<String, Float>
|
||||
)
|
||||
+3
-4
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
@@ -27,7 +26,7 @@ interface BaitSpawnPokemonEvent {
|
||||
*/
|
||||
data class Pre(
|
||||
val bobber: PokeRodFishingBobberEntity, // todo replace this with the bait entity
|
||||
val chosenBucket: SpawnBucket,
|
||||
val chosenBucket: String,
|
||||
val rod: ItemStack
|
||||
) : Cancelable(), BobberSpawnPokemonEvent
|
||||
|
||||
@@ -38,7 +37,7 @@ interface BaitSpawnPokemonEvent {
|
||||
* @param pokemon The Pokemon that is modified.
|
||||
*/
|
||||
data class Modify(
|
||||
val chosenBucket: SpawnBucket,
|
||||
val chosenBucket: String,
|
||||
val rod: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
@@ -52,7 +51,7 @@ interface BaitSpawnPokemonEvent {
|
||||
*/
|
||||
data class Post(
|
||||
val bobber: PokeRodFishingBobberEntity,
|
||||
val chosenBucket: SpawnBucket,
|
||||
val chosenBucket: String,
|
||||
val bait: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent
|
||||
|
||||
+1
-3
@@ -8,12 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.events.fishing
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
|
||||
/**
|
||||
* Event that is fired when a bucket is chosen by the bobber.
|
||||
* @param chosenBucket The bucket that is chosen.
|
||||
* @param buckets The list of buckets that the bobber can choose from.
|
||||
* @param luckOfTheSeaLevel The level of the Luck of the Sea enchantment on the rod.
|
||||
*/
|
||||
class BobberBucketChosenEvent(var chosenBucket: SpawnBucket, val buckets: MutableList<SpawnBucket>, val luckOfTheSeaLevel: Int)
|
||||
class BobberBucketChosenEvent(var chosenBucket: String, val buckets: MutableList<String>, val luckOfTheSeaLevel: Int)
|
||||
+2
-5
@@ -12,10 +12,7 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -73,8 +70,8 @@ interface BobberSpawnPokemonEvent {
|
||||
val bait: ItemStack,
|
||||
val pokemon: PokemonEntity
|
||||
) : BobberSpawnPokemonEvent {
|
||||
val context = mutableMapOf<String, MoValue>(
|
||||
"chosen_bucket" to ObjectValue(spawnAction.bucket, { it.name }),
|
||||
val context = mutableMapOf(
|
||||
"chosen_bucket" to StringValue(spawnAction.bucket),
|
||||
"bait" to pokemon.level().itemRegistry.wrapAsHolder(bait.item).asMoLangValue(Registries.ITEM),
|
||||
"pokemon_entity" to pokemon.asMoLangValue()
|
||||
)
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A [HabitatPool] of [ActivatedHabitatSpawn]s. Mainly just to help with deserialization and ensuring
|
||||
* a single type for everything that's applied to a habitat block.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class ActivatedHabitatPool : HabitatPool<ActivatedHabitatSpawn>() {
|
||||
override val type: ResourceLocation = ActivatedHabitatSpawn.TYPE
|
||||
override var spawns: List<ActivatedHabitatSpawn> = listOf()
|
||||
|
||||
companion object {
|
||||
fun default() = ActivatedHabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_activated_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
super.decode(buffer, buckets)
|
||||
spawns = buffer.readList { ActivatedHabitatSpawn().apply { decode(buffer, buckets) } }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
|
||||
/**
|
||||
* A basic [HabitatSpawn] that will be triggered via activated spawning in a habitat. Nothing to it, really.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class ActivatedHabitatSpawn : HabitatSpawn() {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("activated")
|
||||
}
|
||||
}
|
||||
@@ -8,39 +8,81 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A pool of [HabitatSpawn]s. This is either filled with [NaturalHabitatSpawn]s or [ActivatedHabitatSpawn]s depending
|
||||
* on whether the pool is a [NaturalHabitatPool] or an [ActivatedHabitatPool]. The pool is responsible for converting
|
||||
* A pool of [HabitatSpawn]s. This is filled with [HabitatSpawn]s. The pool is responsible for converting
|
||||
* its spawns into [PokemonSpawnDetail]s.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
abstract class HabitatPool<T : HabitatSpawn> {
|
||||
class HabitatPool {
|
||||
companion object {
|
||||
val types = mutableMapOf(
|
||||
ActivatedHabitatSpawn.TYPE to ActivatedHabitatPool::class.java,
|
||||
NaturalHabitatSpawn.TYPE to NaturalHabitatPool::class.java
|
||||
)
|
||||
fun default() = HabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
|
||||
fun writePoolToNBT(pool: HabitatPool, nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_ID, pool.id.toString())
|
||||
if (pool.id.path.startsWith("custom__")) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_NAME, pool.name)
|
||||
val spawnsListNBT = ListTag()
|
||||
for (spawn in pool.spawns) {
|
||||
val spawnTag = CompoundTag()
|
||||
spawn.writeToNBT(spawnTag)
|
||||
spawnsListNBT.add(spawnTag)
|
||||
}
|
||||
nbt.put(DataKeys.HABITAT_POOL_SPAWNS, spawnsListNBT)
|
||||
}
|
||||
}
|
||||
|
||||
fun readPoolFromNBT(nbt: CompoundTag): HabitatPool {
|
||||
val poolId = nbt.getString(DataKeys.HABITAT_POOL_ID).asIdentifierDefaultingNamespace()
|
||||
if (nbt.contains(DataKeys.HABITAT_POOL_NAME)) {
|
||||
// It's one that was stored on the block
|
||||
val pool = HabitatPool()
|
||||
pool.id = poolId
|
||||
pool.name = nbt.getString(DataKeys.HABITAT_POOL_NAME)
|
||||
val spawnsListNBT = nbt.getList(DataKeys.HABITAT_POOL_SPAWNS, Tag.TAG_COMPOUND.toInt())
|
||||
val spawns = mutableListOf<HabitatSpawn>()
|
||||
spawnsListNBT.forEach { spawnTag ->
|
||||
try {
|
||||
val spawn = HabitatSpawn().apply { readFromNBT(spawnTag as CompoundTag) }
|
||||
spawns.add(spawn)
|
||||
} catch (_: Exception) {
|
||||
// If there's an error reading a spawn, skip it and log the error.
|
||||
Cobblemon.LOGGER.error("Error reading activated habitat spawn from NBT for habitat block, skipping.")
|
||||
}
|
||||
}
|
||||
pool.spawns = spawns
|
||||
return pool
|
||||
} else {
|
||||
return HabitatPools.habitatPoolsById[poolId] ?: HabitatPool.default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var id: ResourceLocation
|
||||
var name: String = ""
|
||||
abstract val type: ResourceLocation
|
||||
abstract var spawns: List<T>
|
||||
var spawns: List<HabitatSpawn> = listOf()
|
||||
|
||||
/** Whether a pool is editable is just down to whether it's not from a datapack. */
|
||||
@Transient
|
||||
var editable: Boolean = false
|
||||
|
||||
open fun validate() {
|
||||
fun validate() {
|
||||
for (spawn in spawns) {
|
||||
if (SpawnablePosition.getByName(spawn.spawnablePositionType) == null) {
|
||||
throw IllegalArgumentException("$id mentions non-existent spawnable position type ${spawn.spawnablePositionType} on spawn for ${spawn.species}")
|
||||
@@ -58,8 +100,9 @@ abstract class HabitatPool<T : HabitatSpawn> {
|
||||
buffer.writeCollection(spawns) { _, it -> it.encode(buffer) }
|
||||
}
|
||||
|
||||
open fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
id = buffer.readResourceLocation()
|
||||
name = buffer.readUtf()
|
||||
spawns = buffer.readList { HabitatSpawn().apply { decode(buffer) } }
|
||||
}
|
||||
}
|
||||
@@ -12,16 +12,13 @@ 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.api.spawning.IntRanges
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.adapters.HabitatPoolAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RegisteredSpawnablePositionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -36,18 +33,16 @@ import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.world.level.storage.LevelResource
|
||||
|
||||
object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
object HabitatPools : JsonDataRegistry<HabitatPool> {
|
||||
override val id = cobblemonResource("habitat_pools")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override val observable = SimpleObservable<HabitatPools>()
|
||||
override val typeToken: TypeToken<HabitatPool<*>> = TypeToken.get(HabitatPool::class.java)
|
||||
override val typeToken: TypeToken<HabitatPool> = TypeToken.get(HabitatPool::class.java)
|
||||
override val resourcePath = "habitat_pools"
|
||||
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(HabitatPool::class.java, HabitatPoolAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(Species::class.java, SpeciesAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRange.adapter)
|
||||
@@ -56,10 +51,10 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
private val datapacked = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
private val worldSaved = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
private val datapacked = mutableMapOf<ResourceLocation, HabitatPool>()
|
||||
private val worldSaved = mutableMapOf<ResourceLocation, HabitatPool>()
|
||||
|
||||
val habitatPoolsById = mutableMapOf<ResourceLocation, HabitatPool<*>>()
|
||||
val habitatPoolsById = mutableMapOf<ResourceLocation, HabitatPool>()
|
||||
|
||||
private lateinit var worldSavedPoolsFolder: File
|
||||
|
||||
@@ -73,7 +68,7 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
|
||||
worldSavedPoolsFolder.listFiles { it.endsWith(".json") }.forEach {
|
||||
val identifier = ResourceLocation.parse(it.nameWithoutExtension)
|
||||
val habitat = gson.fromJson<HabitatPool<*>>(it.reader())
|
||||
val habitat = gson.fromJson<HabitatPool>(it.reader())
|
||||
habitat.id = identifier
|
||||
habitat.validate()
|
||||
habitat.editable = true
|
||||
@@ -82,7 +77,7 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
|
||||
setupHabitatPoolsById()
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, HabitatPool<*>>) {
|
||||
override fun reload(data: Map<ResourceLocation, HabitatPool>) {
|
||||
datapacked.clear()
|
||||
datapacked.putAll(data)
|
||||
data.entries.forEach { (identifier, habitat) ->
|
||||
|
||||
@@ -10,9 +10,7 @@ package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.IntRanges
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.condition.AppendageCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
@@ -30,9 +28,7 @@ import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
/**
|
||||
* A kind of simplified spawn definition for the purposes of spawning from [HabitatBlockEntity] instances. This is a
|
||||
* very shallow class tree, with [ActivatedHabitatSpawn] being a trivial subclass but the other subclass being
|
||||
* [NaturalHabitatSpawn] which introduces the need to supply a spawn bucket.
|
||||
* A kind of simplified spawn definition for the purposes of spawning from [HabitatBlockEntity] instances.
|
||||
*
|
||||
* These have only a small subset of the properties that a regular [PokemonSpawnDetail] would have to align with the
|
||||
* fact that these are configured from a simple GUI. There are limits to how many properties and conditions could be
|
||||
@@ -46,11 +42,13 @@ import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
sealed class HabitatSpawn {
|
||||
class HabitatSpawn {
|
||||
/** The species that will be spawned. */
|
||||
lateinit var species: Species
|
||||
/** The kind of position the Pokémon will spawn in. Things like "grounded", "seafloor", "fishing". */
|
||||
lateinit var spawnablePositionType: String
|
||||
/** The bucket to spawn from. */
|
||||
var bucket: String = "common"
|
||||
|
||||
/** Selection weight. */
|
||||
var weight: Float = 1F
|
||||
@@ -77,6 +75,7 @@ sealed class HabitatSpawn {
|
||||
timeRange?.let { nbt.putString(DataKeys.HABITAT_POOL_SPAWN_TIMES, TimeRange.adapter.serialize(it)) }
|
||||
minLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT, it) }
|
||||
maxLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT, it) }
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket)
|
||||
}
|
||||
|
||||
open fun readFromNBT(nbt: CompoundTag) {
|
||||
@@ -89,6 +88,7 @@ sealed class HabitatSpawn {
|
||||
timeRange = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_TIMES)) TimeRange.adapter.deserialize(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_TIMES)) else null
|
||||
minLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT) else null
|
||||
maxLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT) else null
|
||||
bucket = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_BUCKET)) nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) else "common"
|
||||
}
|
||||
|
||||
open fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -101,12 +101,10 @@ sealed class HabitatSpawn {
|
||||
buffer.writeNullable(timeRange) { _, value -> buffer.writeUtf(TimeRange.adapter.serialize(value)) }
|
||||
buffer.writeNullable(minLight) { _, value -> buffer.writeInt(value) }
|
||||
buffer.writeNullable(maxLight) { _, value -> buffer.writeInt(value) }
|
||||
buffer.writeUtf(bucket)
|
||||
}
|
||||
|
||||
open fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
open fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
species = PokemonSpecies.getByIdentifier(buffer.readResourceLocation())!!
|
||||
spawnablePositionType = buffer.readUtf()
|
||||
weight = buffer.readFloat()
|
||||
@@ -116,6 +114,7 @@ sealed class HabitatSpawn {
|
||||
timeRange = buffer.readNullable { _ -> TimeRange.adapter.deserialize(buffer.readUtf()) }
|
||||
minLight = buffer.readNullable { _ -> buffer.readInt() }
|
||||
maxLight = buffer.readNullable { _ -> buffer.readInt() }
|
||||
bucket = buffer.readUtf()
|
||||
}
|
||||
|
||||
/** Creates the [PokemonSpawnDetail] that will be used in the spawner. This is done per habitat block. */
|
||||
@@ -138,7 +137,7 @@ sealed class HabitatSpawn {
|
||||
spawnDetail.apply {
|
||||
this.id = "habitat_${species.resourceIdentifier}"
|
||||
this.pokemon = properties
|
||||
this.bucket = BestSpawner.config.buckets.first { it.weight > 0 }
|
||||
this.bucket = this@HabitatSpawn.bucket
|
||||
this.weight = this@HabitatSpawn.weight
|
||||
this.levelRange = levelRange
|
||||
this.conditions = mutableListOf(condition)
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A [HabitatPool] of [NaturalHabitatSpawn]s. Mainly just to help with deserialization and ensuring
|
||||
* a single type for everything that's applied to a habitat block.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class NaturalHabitatPool : HabitatPool<NaturalHabitatSpawn>() {
|
||||
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
|
||||
override var spawns: List<NaturalHabitatSpawn> = listOf()
|
||||
|
||||
companion object {
|
||||
fun default() = NaturalHabitatPool().apply {
|
||||
id = ResourceLocation("cobblemon", "custom__default_natural_pool")
|
||||
name = "Custom"
|
||||
}
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
|
||||
super.decode(buffer, buckets)
|
||||
spawns = buffer.readList { NaturalHabitatSpawn().apply { decode(buffer, buckets) } }
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
/**
|
||||
* A type of [HabitatSpawn] that spawns Pokémon as part of natural world spawning, and therefore
|
||||
* needs to indicate the [SpawnBucket] it will be part of.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
class NaturalHabitatSpawn : HabitatSpawn() {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("natural")
|
||||
}
|
||||
|
||||
lateinit var bucket: SpawnBucket
|
||||
|
||||
override fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
|
||||
val spawnDetail = super.createSpawnDetail(habitatBlockEntity)
|
||||
spawnDetail?.bucket = bucket
|
||||
return spawnDetail
|
||||
}
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
super.writeToNBT(nbt)
|
||||
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket.name)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
super.readFromNBT(nbt)
|
||||
bucket = Cobblemon.bestSpawner.config.buckets.first { it.name == nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) }
|
||||
}
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeUtf(bucket.name)
|
||||
}
|
||||
|
||||
override fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
super.decode(buffer, buckets)
|
||||
val bucketName = buffer.readUtf()
|
||||
bucket = buckets.first { it.name == bucketName }
|
||||
}
|
||||
}
|
||||
+1
-11
@@ -8,9 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class ActivatedHabitatSettingsDTO() {
|
||||
@@ -20,7 +18,6 @@ class ActivatedHabitatSettingsDTO() {
|
||||
var spawnRange = 16
|
||||
var maxSpawns = -1
|
||||
var maxSpawnsPerActivation = 1
|
||||
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(ActivatedHabitatPool.default())
|
||||
|
||||
constructor(activatedHabitatSpawning: ActivatedHabitatSpawning): this() {
|
||||
this.chance = activatedHabitatSpawning.chance
|
||||
@@ -29,7 +26,6 @@ class ActivatedHabitatSettingsDTO() {
|
||||
this.spawnRange = activatedHabitatSpawning.spawnRange
|
||||
this.maxSpawns = activatedHabitatSpawning.maxSpawns
|
||||
this.maxSpawnsPerActivation = activatedHabitatSpawning.maxSpawnsPerActivation
|
||||
this.habitatPool = HabitatPoolSettingsDTO(activatedHabitatSpawning.pool)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -39,20 +35,14 @@ class ActivatedHabitatSettingsDTO() {
|
||||
buffer.writeInt(spawnRange)
|
||||
buffer.writeInt(maxSpawns)
|
||||
buffer.writeInt(maxSpawnsPerActivation)
|
||||
habitatPool.encode(buffer)
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.chance = buffer.readFloat()
|
||||
this.trigger = buffer.readEnum(ActivatedHabitatSpawning.Trigger::class.java)
|
||||
this.cancelledNaturalSpawningRange = buffer.readInt()
|
||||
this.spawnRange = buffer.readInt()
|
||||
this.maxSpawns = buffer.readInt()
|
||||
this.maxSpawnsPerActivation = buffer.readInt()
|
||||
this.habitatPool = HabitatPoolSettingsDTO()
|
||||
habitatPool.decode(buffer, buckets)
|
||||
}
|
||||
}
|
||||
+10
-36
@@ -9,30 +9,23 @@
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class HabitatPoolSettingsDTO() {
|
||||
lateinit var id: ResourceLocation
|
||||
var isReference = false
|
||||
var isActivated = false
|
||||
|
||||
// Below properties are only there when it's not a reference, otherwise the handler will have to look them up from the id
|
||||
var name: String? = null
|
||||
var spawns: MutableList<HabitatSpawn>? = null
|
||||
|
||||
constructor(habitatPool: HabitatPool<*>): this() {
|
||||
constructor(habitatPool: HabitatPool): this() {
|
||||
this.id = habitatPool.id
|
||||
this.isReference = !id.path.startsWith("custom__")
|
||||
this.isActivated = habitatPool is ActivatedHabitatPool
|
||||
if (!this.isReference) {
|
||||
this.name = habitatPool.name
|
||||
this.spawns = habitatPool.spawns.toMutableList()
|
||||
@@ -42,7 +35,6 @@ class HabitatPoolSettingsDTO() {
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(id)
|
||||
buffer.writeBoolean(isReference)
|
||||
buffer.writeBoolean(isActivated)
|
||||
if (!isReference) {
|
||||
buffer.writeUtf(name!!)
|
||||
buffer.writeCollection(spawns!!) { _, spawn ->
|
||||
@@ -52,50 +44,32 @@ class HabitatPoolSettingsDTO() {
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
buffer: RegistryFriendlyByteBuf
|
||||
) {
|
||||
this.id = buffer.readResourceLocation()
|
||||
this.isReference = buffer.readBoolean()
|
||||
this.isActivated = buffer.readBoolean()
|
||||
if (!isReference) {
|
||||
this.name = buffer.readUtf()
|
||||
this.spawns = buffer.readList {
|
||||
val spawn = if (isActivated) {
|
||||
ActivatedHabitatSpawn()
|
||||
} else {
|
||||
NaturalHabitatSpawn()
|
||||
}
|
||||
spawn.decode(buffer, buckets)
|
||||
val spawn = HabitatSpawn()
|
||||
spawn.decode(buffer)
|
||||
spawn
|
||||
}.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun toHabitatPool(): HabitatPool<*>? {
|
||||
fun toHabitatPool(): HabitatPool? {
|
||||
if (isReference) {
|
||||
val pool = HabitatPools.habitatPoolsById[id]
|
||||
if (pool == null) {
|
||||
Cobblemon.LOGGER.error("Could not find habitat pool with id $id")
|
||||
} else if (pool is ActivatedHabitatPool && !isActivated) {
|
||||
Cobblemon.LOGGER.error("Pool with id $id is an activated habitat pool but was deserialized as a natural pool")
|
||||
} else if (pool !is ActivatedHabitatPool && isActivated) {
|
||||
Cobblemon.LOGGER.error("Pool with id $id is a natural habitat pool but was deserialized as an activated pool")
|
||||
}
|
||||
return pool
|
||||
}
|
||||
return if (isActivated) {
|
||||
val pool = ActivatedHabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!.map { it as ActivatedHabitatSpawn }
|
||||
pool
|
||||
} else {
|
||||
val pool = NaturalHabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!.map { it as NaturalHabitatSpawn }
|
||||
pool
|
||||
}
|
||||
val pool = HabitatPool()
|
||||
pool.id = id
|
||||
pool.name = name!!
|
||||
pool.spawns = spawns!!
|
||||
return pool
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -9,9 +9,9 @@
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPhaseOrder
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -23,6 +23,7 @@ class HabitatSettingsDTO() {
|
||||
var modifiers: String = ""
|
||||
lateinit var mimicId: ResourceLocation
|
||||
var isActivatedSpawning = false
|
||||
var pool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(HabitatPool.default())
|
||||
var activatedSettings: ActivatedHabitatSettingsDTO? = null
|
||||
var naturalSettings: NaturalHabitatSettingsDTO? = null
|
||||
|
||||
@@ -32,6 +33,7 @@ class HabitatSettingsDTO() {
|
||||
this.levelRange = habitatBlockEntity.levelRange
|
||||
this.modifiers = habitatBlockEntity.modifiers.asString()
|
||||
this.mimicId = habitatBlockEntity.mimicId
|
||||
this.pool = HabitatPoolSettingsDTO(habitatBlockEntity.pool)
|
||||
val style = habitatBlockEntity.spawningStyle
|
||||
if (style is ActivatedHabitatSpawning) {
|
||||
this.isActivatedSpawning = true
|
||||
@@ -48,6 +50,7 @@ class HabitatSettingsDTO() {
|
||||
buffer.writeInt(levelRange.last)
|
||||
buffer.writeUtf(modifiers)
|
||||
buffer.writeResourceLocation(mimicId)
|
||||
pool.encode(buffer)
|
||||
buffer.writeBoolean(isActivatedSpawning)
|
||||
if (isActivatedSpawning) {
|
||||
activatedSettings?.encode(buffer)
|
||||
@@ -56,10 +59,7 @@ class HabitatSettingsDTO() {
|
||||
}
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
this.numberOfPhases = buffer.readInt()
|
||||
this.phaseOrder = buffer.readEnum(HabitatPhaseOrder::class.java)
|
||||
val levelRangeStart = buffer.readInt()
|
||||
@@ -67,16 +67,18 @@ class HabitatSettingsDTO() {
|
||||
this.levelRange = levelRangeStart..levelRangeEnd
|
||||
this.modifiers = buffer.readUtf()
|
||||
this.mimicId = buffer.readResourceLocation()
|
||||
this.pool = HabitatPoolSettingsDTO()
|
||||
this.pool.decode(buffer)
|
||||
if (buffer.readBoolean()) {
|
||||
this.isActivatedSpawning = true
|
||||
this.naturalSettings = null
|
||||
val activatedSettings = ActivatedHabitatSettingsDTO()
|
||||
activatedSettings.decode(buffer, buckets)
|
||||
activatedSettings.decode(buffer)
|
||||
this.activatedSettings = activatedSettings
|
||||
} else {
|
||||
this.activatedSettings = null
|
||||
val naturalSettings = NaturalHabitatSettingsDTO()
|
||||
naturalSettings.decode(buffer, buckets)
|
||||
naturalSettings.decode(buffer)
|
||||
this.naturalSettings = naturalSettings
|
||||
}
|
||||
}
|
||||
|
||||
+1
-11
@@ -8,35 +8,25 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.dto
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class NaturalHabitatSettingsDTO() {
|
||||
var replaceSpawns = false
|
||||
var rangeOfInfluence = 16
|
||||
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(NaturalHabitatPool.default())
|
||||
|
||||
constructor(naturalHabitatSpawning: NaturalHabitatSpawning): this() {
|
||||
this.replaceSpawns = naturalHabitatSpawning.replaceSpawns
|
||||
this.rangeOfInfluence = naturalHabitatSpawning.rangeOfInfluence
|
||||
this.habitatPool = HabitatPoolSettingsDTO(naturalHabitatSpawning.pool)
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeBoolean(replaceSpawns)
|
||||
buffer.writeInt(rangeOfInfluence)
|
||||
habitatPool.encode(buffer)
|
||||
}
|
||||
|
||||
fun decode(
|
||||
buffer: RegistryFriendlyByteBuf,
|
||||
buckets: List<SpawnBucket>
|
||||
) {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf, ) {
|
||||
this.replaceSpawns = buffer.readBoolean()
|
||||
this.rangeOfInfluence = buffer.readInt()
|
||||
this.habitatPool = HabitatPoolSettingsDTO()
|
||||
habitatPool.decode(buffer, buckets)
|
||||
}
|
||||
}
|
||||
+12
-23
@@ -8,17 +8,16 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.habitats.HabitatSpawnActivatedEvent
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -27,7 +26,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
* A habitat spawning style defined by being triggered by something. The activation trigger, [trigger], is either
|
||||
* a redstone pulse, every tick, or every random tick.
|
||||
*
|
||||
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
|
||||
* The spawn details are generated from the pool every time there is some change to the habitat block's settings,
|
||||
* then those spawn details are used for spawning Pokémon when the habitat block is triggered.
|
||||
*
|
||||
* The [chance] property is applied after the trigger logic so that not every activation necessarily results in a spawn
|
||||
@@ -37,6 +36,10 @@ import net.minecraft.server.level.ServerLevel
|
||||
* @since February 14th, 2026
|
||||
*/
|
||||
class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("activated")
|
||||
}
|
||||
|
||||
enum class Trigger {
|
||||
/** Requires a redstone pulse. */
|
||||
REDSTONE,
|
||||
@@ -46,7 +49,8 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
RANDOM_TICK
|
||||
}
|
||||
|
||||
override val type = ActivatedHabitatSpawn.TYPE
|
||||
@Transient
|
||||
override val type = TYPE
|
||||
|
||||
/** The chance that an activation by the [trigger] will result in a spawn attempt. The spawn attempt may still fail. */
|
||||
var chance = 1F
|
||||
@@ -60,11 +64,7 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
var maxSpawns = -1
|
||||
/** The maximum number of Pokémon that can spawn in a single activation. This will still be capped by [maxSpawns]. */
|
||||
var maxSpawnsPerActivation = 1
|
||||
/** The pool of spawns to choose from. */
|
||||
var pool: ActivatedHabitatPool = ActivatedHabitatPool.default()
|
||||
|
||||
// This is cached here so that it doesn't have to be regenerated every time the block is triggered, updated when settings change.
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
lateinit var spawner: FixedAreaSpawner
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
@@ -75,10 +75,6 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE, spawnRange)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS, maxSpawns)
|
||||
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION, maxSpawnsPerActivation)
|
||||
|
||||
val poolNBT = CompoundTag()
|
||||
writePoolToNBT(pool, poolNBT)
|
||||
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
@@ -88,25 +84,18 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
spawnRange = nbt.getInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE)
|
||||
maxSpawns = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS)
|
||||
maxSpawnsPerActivation = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION)
|
||||
|
||||
pool = HabitatSpawningStyle.readPoolFromNBT(
|
||||
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
|
||||
poolInitializer = ::ActivatedHabitatPool,
|
||||
spawnInitializer = ::ActivatedHabitatSpawn,
|
||||
defaultPool = ActivatedHabitatPool::default
|
||||
)
|
||||
}
|
||||
|
||||
fun generateSpawnDetails(world: ServerLevel, pos: BlockPos) {
|
||||
spawnDetails = pool.createSpawnDetails(habitatBlockEntity)
|
||||
spawner = FixedAreaSpawner(
|
||||
name = "habitat_spawner_$pos",
|
||||
world = world,
|
||||
position = pos,
|
||||
spawnPool = SpawnPool("habitat_block").also {
|
||||
it.details.addAll(spawnDetails)
|
||||
it.details.addAll(habitatBlockEntity.spawnDetails)
|
||||
it.precalculate()
|
||||
},
|
||||
buckets = Cobblemon.bestSpawner.config.activatedHabitatBuckets,
|
||||
maxPokemonPerChunk = 16F,
|
||||
verticalRadius = spawnRange,
|
||||
horizontalRadius = spawnRange
|
||||
@@ -118,7 +107,7 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
|
||||
// Check if the spawn attempt should even happen based on chance.
|
||||
if (chance < 1F && world.random.nextFloat() >= chance) {
|
||||
return
|
||||
} else if (spawnDetails.isEmpty()) {
|
||||
} else if (habitatBlockEntity.spawnDetails.isEmpty()) {
|
||||
// If there are no spawn details, then there's no point in trying to spawn anything.
|
||||
return
|
||||
}
|
||||
|
||||
-51
@@ -8,15 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
@@ -33,47 +25,4 @@ sealed interface HabitatSpawningStyle {
|
||||
|
||||
fun writeToNBT(nbt: CompoundTag)
|
||||
fun readFromNBT(nbt: CompoundTag)
|
||||
|
||||
fun writePoolToNBT(pool: HabitatPool<*>, nbt: CompoundTag) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_ID, pool.id.toString())
|
||||
if (pool.id.path.startsWith("custom__")) {
|
||||
nbt.putString(DataKeys.HABITAT_POOL_NAME, pool.name)
|
||||
val spawnsListNBT = ListTag()
|
||||
for (spawn in pool.spawns) {
|
||||
val spawnTag = CompoundTag()
|
||||
spawn.writeToNBT(spawnTag)
|
||||
spawnsListNBT.add(spawnTag)
|
||||
}
|
||||
nbt.put(DataKeys.HABITAT_POOL_SPAWNS, spawnsListNBT)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <T : HabitatSpawn, P : HabitatPool<T>> readPoolFromNBT(nbt: CompoundTag, poolInitializer: () -> P, spawnInitializer: () -> T, defaultPool: () -> P): P {
|
||||
var pool: P
|
||||
val poolId = nbt.getString(DataKeys.HABITAT_POOL_ID).asIdentifierDefaultingNamespace()
|
||||
if (nbt.contains(DataKeys.HABITAT_POOL_NAME)) {
|
||||
// It's one that was stored on the block
|
||||
pool = poolInitializer()
|
||||
pool.id = poolId
|
||||
pool.name = nbt.getString(DataKeys.HABITAT_POOL_NAME)
|
||||
val spawnsListNBT = nbt.getList(DataKeys.HABITAT_POOL_SPAWNS, Tag.TAG_COMPOUND.toInt())
|
||||
val spawns = mutableListOf<T>()
|
||||
spawnsListNBT.forEach { spawnTag ->
|
||||
try {
|
||||
val spawn = spawnInitializer().apply { readFromNBT(spawnTag as CompoundTag) }
|
||||
spawns.add(spawn)
|
||||
} catch (_: Exception) {
|
||||
// If there's an error reading a spawn, skip it and log the error.
|
||||
Cobblemon.LOGGER.error("Error reading activated habitat spawn from NBT for habitat block, skipping.")
|
||||
}
|
||||
}
|
||||
pool.spawns = spawns
|
||||
} else {
|
||||
pool = HabitatPools.habitatPoolsById[poolId] as? P ?: defaultPool()
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-24
@@ -8,21 +8,18 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.habitats.spawningstyle
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.HabitatSpawningStyle.Companion.readPoolFromNBT
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A habitat spawning style defined by tapping into regular spawning methods. This works by adding to the spawn list
|
||||
* for spawning that's occurring nearby. When [replaceSpawns] is set to true, the original spawns will be
|
||||
* removed completely in favour of the spawns provided in this habitat's [pool].
|
||||
* removed completely in favour of the spawns provided in this habitat's pool.
|
||||
*
|
||||
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
|
||||
* The spawn details are generated from the pool every time there is some change to the habitat block's settings,
|
||||
* then those spawn details are used for spawning Pokémon when world spawning is occurring nearby.
|
||||
*
|
||||
* Whether or not this interactions with overworld or fishing spawns is determined by whether the habitat pool has
|
||||
@@ -32,10 +29,13 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @since February 22nd, 2026
|
||||
*/
|
||||
class NaturalHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
|
||||
companion object {
|
||||
val TYPE = cobblemonResource("natural")
|
||||
}
|
||||
|
||||
@Transient
|
||||
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
|
||||
override val type: ResourceLocation = TYPE
|
||||
|
||||
var pool: NaturalHabitatPool = NaturalHabitatPool.default()
|
||||
var replaceSpawns = false
|
||||
var rangeOfInfluence = 16
|
||||
|
||||
@@ -43,33 +43,19 @@ class NaturalHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Habit
|
||||
var affectsFishing = false
|
||||
@Transient
|
||||
var affectsOverworld = false
|
||||
@Transient
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
|
||||
fun generateSpawnDetails() {
|
||||
spawnDetails = pool.createSpawnDetails(habitatBlockEntity = habitatBlockEntity)
|
||||
affectsFishing = spawnDetails.any { it.spawnablePositionType.name == "fishing" }
|
||||
affectsOverworld = spawnDetails.any { it.spawnablePositionType.name != "fishing" }
|
||||
affectsFishing = habitatBlockEntity.spawnDetails.any { it.spawnablePositionType.name == "fishing" }
|
||||
affectsOverworld = habitatBlockEntity.spawnDetails.any { it.spawnablePositionType.name != "fishing" }
|
||||
}
|
||||
|
||||
override fun writeToNBT(nbt: CompoundTag) {
|
||||
nbt.putBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS, replaceSpawns)
|
||||
nbt.putInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE, rangeOfInfluence)
|
||||
|
||||
val poolNBT = CompoundTag()
|
||||
writePoolToNBT(pool, poolNBT)
|
||||
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
|
||||
}
|
||||
|
||||
override fun readFromNBT(nbt: CompoundTag) {
|
||||
replaceSpawns = nbt.getBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS)
|
||||
rangeOfInfluence = nbt.getInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE)
|
||||
|
||||
pool = readPoolFromNBT(
|
||||
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
|
||||
poolInitializer = ::NaturalHabitatPool,
|
||||
spawnInitializer = ::NaturalHabitatSpawn,
|
||||
defaultPool = NaturalHabitatPool::default
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,12 @@ import com.cobblemon.mod.common.api.spawning.condition.SeafloorSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.EmptySpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
|
||||
import com.cobblemon.mod.common.api.spawning.position.FishingSpawnablePosition
|
||||
@@ -121,6 +121,7 @@ object BestSpawner {
|
||||
SpawnDetail.registerSpawnType(name = PokemonSpawnDetail.TYPE, PokemonSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = PokemonHerdSpawnDetail.TYPE, PokemonHerdSpawnDetail::class.java)
|
||||
SpawnDetail.registerSpawnType(name = EmptySpawnDetail.TYPE, EmptySpawnDetail::class.java)
|
||||
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
|
||||
|
||||
loadConfig()
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
/**
|
||||
* A spawn bucket is a simple referenceable object by spawn files. The bucket is used at the beginning of
|
||||
* the selection process to decide which set of spawns will be used for spawning on that attempt. Weights are used to
|
||||
* make entire buckets more or less likely, and then weighted selections will occur within whichever bucket
|
||||
* was chosen.
|
||||
*
|
||||
* Spawn buckets are configured in the main config.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since June 20th, 2022
|
||||
*/
|
||||
class SpawnBucket() {
|
||||
lateinit var name: String
|
||||
var weight = 0F
|
||||
|
||||
constructor(name: String, weight: Float): this() {
|
||||
this.name = name
|
||||
this.weight = weight
|
||||
}
|
||||
|
||||
override fun hashCode() = name.hashCode()
|
||||
override fun equals(other: Any?) = other is SpawnBucket && other.name == name
|
||||
}
|
||||
@@ -49,7 +49,6 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
|
||||
.setPrettyPrinting()
|
||||
.setLenient()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
|
||||
@@ -67,7 +67,6 @@ object SpawnLoader {
|
||||
.registerTypeAdapter(MoonPhaseRange::class.java, IntRangesAdapter(MoonPhaseRange.moonPhaseRanges) { MoonPhaseRange(*it) })
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
|
||||
|
||||
+13
-14
@@ -8,10 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
@@ -22,8 +21,8 @@ import net.minecraft.resources.ResourceLocation
|
||||
* time as sharing that value. The requirement of implementations is only to get that
|
||||
* key from both a [SpawnDetail] and a [SpawnablePosition].
|
||||
*
|
||||
* When a precalculation has been registered with a [Spawner] by adding to [Spawner.precalculators],
|
||||
* the precalculation result will need to be updated by running [Spawner.precalculate]. That
|
||||
* When a precalculation has been registered with a [SpawnPool] by adding to [SpawnPool.precalculators],
|
||||
* the precalculation result will need to be updated by running [SpawnPool.precalculate]. That
|
||||
* might take it a hot minute, but the result will be that there is an extra layer of
|
||||
* shortcutting to the matching algorithm.
|
||||
*
|
||||
@@ -34,7 +33,7 @@ interface SpawningPrecalculation<T : Any> {
|
||||
/** Retrieves the precalculation keys for the given spawn detail. */
|
||||
fun select(detail: SpawnDetail): Collection<T>
|
||||
/** Retrieves the precalculation key for the given spawnable position, if that key exists for this position. */
|
||||
fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): T?
|
||||
fun select(bucket: String, spawnablePosition: SpawnablePosition): T?
|
||||
|
||||
/**
|
||||
* Generates a precalculation result for the list of [SpawnDetail]s and the subsequent precalculations.
|
||||
@@ -74,7 +73,7 @@ interface SpawningPrecalculation<T : Any> {
|
||||
object RootPrecalculation : SpawningPrecalculation<Any> {
|
||||
val list = listOf(Unit)
|
||||
override fun select(detail: SpawnDetail): List<Any> = list
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): Any = Unit
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition): Any = Unit
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,12 +84,12 @@ object RootPrecalculation : SpawningPrecalculation<Any> {
|
||||
*/
|
||||
object SpawnablePositionTypePrecalculation : SpawningPrecalculation<Class<out SpawnablePosition>> {
|
||||
override fun select(detail: SpawnDetail) = listOf(detail.spawnablePositionType.clazz)
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition::class.java
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = spawnablePosition::class.java
|
||||
}
|
||||
|
||||
object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
|
||||
object BucketPrecalculation : SpawningPrecalculation<String> {
|
||||
override fun select(detail: SpawnDetail) = listOf(detail.bucket)
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = bucket
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = bucket
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +98,7 @@ object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
|
||||
*/
|
||||
object BiomePrecalculation : SpawningPrecalculation<ResourceLocation> {
|
||||
override fun select(detail: SpawnDetail) = detail.validBiomes
|
||||
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
|
||||
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = spawnablePosition.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +113,7 @@ sealed class PrecalculationResult<T : Any>(
|
||||
val calculation: SpawningPrecalculation<*>
|
||||
) {
|
||||
/** Gets the list of spawn details that fit this spawnable position. */
|
||||
abstract fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>
|
||||
abstract fun retrieve(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +126,7 @@ class NestedPrecalculationResult<T : Any>(
|
||||
calculation: SpawningPrecalculation<T>,
|
||||
val mapping: Map<T, PrecalculationResult<*>> = mutableMapOf()
|
||||
) : PrecalculationResult<T>(calculation) {
|
||||
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)]?.retrieve(bucket, spawnablePosition) ?: emptyList()
|
||||
override fun retrieve(bucket: String, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)]?.retrieve(bucket, spawnablePosition) ?: emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,5 +140,5 @@ class FinalPrecalculationResult<T : Any>(
|
||||
calculation: SpawningPrecalculation<*>,
|
||||
val mapping: Map<T, List<SpawnDetail>>
|
||||
) : PrecalculationResult<T>(calculation) {
|
||||
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)] ?: emptyList()
|
||||
override fun retrieve(bucket: String, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)] ?: emptyList()
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
|
||||
/**
|
||||
* Does a whole lot of nothing. Spawns nothing.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 16th, 2026
|
||||
*/
|
||||
class EmptySpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: String,
|
||||
detail: EmptySpawnDetail
|
||||
) : SpawnAction<Unit>(spawnablePosition, bucket, detail) {
|
||||
override fun run() {}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
|
||||
/**
|
||||
* A spawn detail for nothing. It is used to take a slot, like to weigh down a spawn attempt,
|
||||
* with something that ultimately doesn't spawn anything.
|
||||
*
|
||||
* The use case for this is very niche. With 1.8, the buckets that can be selected by a spawner
|
||||
* during a spawn attempt will be narrowed down to the buckets that are used by the spawn
|
||||
* pool. That means that if a bucket is never used by the pool, it will never be selected. That
|
||||
* way, a bucket won't be selected when it never had any chance of being selected. This is what
|
||||
* you would want in almost every case.
|
||||
*
|
||||
* However, in some cases you may want spawning to not spawn anything or you may want to preserve
|
||||
* the relative rarity of the other buckets, and in that case you'd put something like this in a
|
||||
* bucket so that it technically exists in the spawn pool without actually resulting in a spawned
|
||||
* entity.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 16th, 2026
|
||||
*/
|
||||
class EmptySpawnDetail : SpawnDetail() {
|
||||
companion object {
|
||||
val TYPE = "empty"
|
||||
}
|
||||
|
||||
override val type: String = TYPE
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
) = EmptySpawnAction(
|
||||
spawnablePosition = spawnablePosition,
|
||||
bucket = bucket,
|
||||
detail = this
|
||||
)
|
||||
}
|
||||
+1
-2
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
@@ -20,7 +19,7 @@ import com.cobblemon.mod.common.util.resolveInt
|
||||
* @author Hiroku
|
||||
* @since October 8th, 2023
|
||||
*/
|
||||
class NPCSpawnAction(spawnablePosition: SpawnablePosition, bucket: SpawnBucket, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(spawnablePosition, bucket, detail) {
|
||||
class NPCSpawnAction(spawnablePosition: SpawnablePosition, bucket: String, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(spawnablePosition, bucket, detail) {
|
||||
override fun createEntity(): NPCEntity {
|
||||
val npc = NPCEntity(spawnablePosition.world)
|
||||
npc.npc = detail.npcClass
|
||||
|
||||
+1
-3
@@ -11,13 +11,11 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
* A [SpawnDetail] describing an [NPCEntity] spawn.
|
||||
@@ -51,7 +49,7 @@ class NPCSpawnDetail : SpawnDetail() {
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
) = NPCSpawnAction(spawnablePosition, bucket, this)
|
||||
}
|
||||
+1
-2
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
@@ -100,7 +99,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): PokemonSpawnAction {
|
||||
val validHerdMembers = getValidHerdMembers(selectionData)
|
||||
|
||||
+1
-2
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
@@ -26,7 +25,7 @@ import net.minecraft.world.item.ItemStack
|
||||
*/
|
||||
class PokemonSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
override val detail: SpawnDetail,
|
||||
/**
|
||||
* The [PokemonProperties] that are about to be used. When using it, a copy will be taken.
|
||||
|
||||
+2
-2
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -104,7 +103,8 @@ class PokemonSpawnDetail : SpawnDetail() {
|
||||
}
|
||||
|
||||
override fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition, bucket: SpawnBucket,
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): SingleEntitySpawnAction<PokemonEntity> {
|
||||
val levelRange = pokemon.deriveLevelRange(levelRange)
|
||||
|
||||
+1
-2
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.world.entity.Entity
|
||||
@@ -26,7 +25,7 @@ import net.minecraft.world.entity.MobSpawnType
|
||||
*/
|
||||
abstract class SingleEntitySpawnAction<T : Entity>(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
detail: SpawnDetail
|
||||
) : SpawnAction<EntitySpawnResult>(spawnablePosition, bucket, detail) {
|
||||
abstract fun createEntity(): T?
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
@@ -21,7 +20,7 @@ import java.util.concurrent.CompletableFuture
|
||||
*/
|
||||
abstract class SpawnAction<R>(
|
||||
val spawnablePosition: SpawnablePosition,
|
||||
val bucket: SpawnBucket,
|
||||
val bucket: String,
|
||||
open val detail: SpawnDetail
|
||||
) {
|
||||
val labels = mutableListOf<String>()
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.ModDependant
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.condition.CompositeSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
|
||||
@@ -54,7 +53,7 @@ abstract class SpawnDetail : ModDependant {
|
||||
var displayName: String? = null
|
||||
@SerializedName("spawnablePositionType", alternate = ["context"])
|
||||
lateinit var spawnablePositionType: SpawnablePositionType<*>
|
||||
var bucket = SpawnBucket("", 0F)
|
||||
var bucket = ""
|
||||
var conditions = mutableListOf<SpawningCondition<*>>()
|
||||
var anticonditions = mutableListOf<SpawningCondition<*>>()
|
||||
var compositeCondition: CompositeSpawningCondition? = null
|
||||
@@ -79,7 +78,7 @@ abstract class SpawnDetail : ModDependant {
|
||||
"weight" to { DoubleValue(weight) },
|
||||
"percentage" to { DoubleValue(percentage) },
|
||||
"id" to { StringValue(id) },
|
||||
"bucket" to { StringValue(bucket.name) },
|
||||
"bucket" to { StringValue(bucket) },
|
||||
"width" to { DoubleValue(width.toDouble()) },
|
||||
"height" to { DoubleValue(height.toDouble()) },
|
||||
"spawnable_position_type" to { StringValue(spawnablePositionType.name) },
|
||||
@@ -136,6 +135,10 @@ abstract class SpawnDetail : ModDependant {
|
||||
containsNullValues = true
|
||||
LOGGER.error("Spawn Detail with id $id is invalid as it contains invalid values in its anticonditions (commonly caused by trailing comma in biomes or other arrays)")
|
||||
}
|
||||
if (bucket.isBlank()) {
|
||||
containsNullValues = true
|
||||
LOGGER.error("Spawn Detail with id $id is invalid as it does not have a bucket assigned")
|
||||
}
|
||||
return super.isModDependencySatisfied() && !containsNullValues
|
||||
}
|
||||
|
||||
@@ -163,7 +166,7 @@ abstract class SpawnDetail : ModDependant {
|
||||
*/
|
||||
fun choose(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): SpawnAction<*> {
|
||||
val action = createSpawnAction(spawnablePosition, bucket, selectionData)
|
||||
@@ -175,10 +178,10 @@ abstract class SpawnDetail : ModDependant {
|
||||
return action
|
||||
}
|
||||
|
||||
/** Instantiates an action at the given [SpawnablePosition] for the given [SpawnBucket]. */
|
||||
/** Instantiates an action at the given [SpawnablePosition] for the given spawn bucket. */
|
||||
protected abstract fun createSpawnAction(
|
||||
spawnablePosition: SpawnablePosition,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnSelectionData
|
||||
): SpawnAction<*>
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnLoader
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnSet
|
||||
import com.cobblemon.mod.common.api.spawning.condition.PrecalculationResult
|
||||
@@ -61,12 +60,13 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
}
|
||||
|
||||
val details = mutableListOf<SpawnDetail>()
|
||||
/** Tracks which buckets are in use by the pool, to prevent spawners for rolling buckets that don't exist. */
|
||||
val buckets = mutableListOf<String>()
|
||||
var precalculation: PrecalculationResult<*> = RootPrecalculation.generate(details, emptyList())
|
||||
val precalculators = mutableListOf<SpawningPrecalculation<*>>()
|
||||
|
||||
override fun iterator() = details.iterator()
|
||||
|
||||
|
||||
fun addPrecalculators(vararg precalculators: SpawningPrecalculation<*>): SpawnPool {
|
||||
this.precalculators.addAll(precalculators)
|
||||
precalculate()
|
||||
@@ -88,6 +88,9 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
* be slow, especially if there are many precalculators and spawns.
|
||||
*/
|
||||
fun precalculate() {
|
||||
buckets.clear()
|
||||
buckets.addAll(details.asSequence().map(SpawnDetail::bucket).distinct())
|
||||
|
||||
if (precalculators.isEmpty()) {
|
||||
precalculation = RootPrecalculation.generate(details, emptyList())
|
||||
} else {
|
||||
@@ -103,7 +106,7 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
|
||||
* to leverage the precalculation to get a smaller list of spawns as quickly
|
||||
* as possible.
|
||||
*/
|
||||
fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
fun retrieve(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
return precalculation.retrieve(bucket, spawnablePosition)
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -8,19 +8,19 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
|
||||
/**
|
||||
* A [SpawningInfluence] that multiplies bucket weights by the provided values
|
||||
* and then normalizes the resulting weights so that their total adds up to 100.
|
||||
*
|
||||
* No longer used by the mod, but kept for external suers.
|
||||
*/
|
||||
class BucketMultiplyingInfluence(
|
||||
val multipliers: Map<String, Float>,
|
||||
) : SpawningInfluence {
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
|
||||
val updates = mutableMapOf<SpawnBucket, Float>()
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {
|
||||
val updates = mutableMapOf<String, Float>()
|
||||
for ((bucket, weight) in bucketWeights) {
|
||||
val multiplier = multipliers[bucket.name]
|
||||
val multiplier = multipliers[bucket]
|
||||
if (multiplier != null) {
|
||||
updates[bucket] = weight * multiplier
|
||||
}
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import kotlin.math.pow
|
||||
|
||||
/**
|
||||
@@ -33,7 +32,7 @@ open class BucketNormalizingInfluence(
|
||||
val gradient: Float = 0.2F,
|
||||
val firstTier: Float = 1.29F
|
||||
) : SpawningInfluence {
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
|
||||
override fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {
|
||||
if (tier == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
+3
-4
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -39,9 +38,9 @@ interface SpawningInfluence {
|
||||
/** Applies some influence over the entity that's been spawned. */
|
||||
fun affectSpawn(action: SpawnAction<*>, entity: Entity) {}
|
||||
/** Applies some influence over the weight of spawn buckets. */
|
||||
fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {}
|
||||
fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {}
|
||||
/** Normalizes all bucket weights to add up to 100 */
|
||||
fun normalizeBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
|
||||
fun normalizeBucketWeights(bucketWeights: MutableMap<String, Float>) {
|
||||
val sum = bucketWeights.values.sum()
|
||||
val to100 = 100F / sum
|
||||
bucketWeights.keys.toList().forEach {
|
||||
@@ -56,5 +55,5 @@ interface SpawningInfluence {
|
||||
*
|
||||
* You should make sure the spawn position satisfies any spawn details that you inject.
|
||||
*/
|
||||
fun injectSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>? = null
|
||||
fun injectSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>? = null
|
||||
}
|
||||
+28
-6
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.preset
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.util.adapters.RegisteredSpawnablePositionAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -39,11 +38,34 @@ class BestSpawnerConfig {
|
||||
"surface" to 0.01F,
|
||||
"seafloor" to 1F
|
||||
)
|
||||
val buckets = mutableListOf(
|
||||
SpawnBucket("common", 94.3F),
|
||||
SpawnBucket("uncommon", 5F),
|
||||
SpawnBucket("rare", 0.5F),
|
||||
SpawnBucket("ultra-rare", 0.2F)
|
||||
|
||||
val worldBuckets = mutableMapOf(
|
||||
"common" to 94F,
|
||||
"uncommon" to 5F,
|
||||
"rare" to 0.5F,
|
||||
"ultra-rare" to 0.2F,
|
||||
"boss" to 0.3F,
|
||||
)
|
||||
|
||||
val fishingBuckets = mutableMapOf(
|
||||
"common" to 83.25F,
|
||||
"uncommon" to 11.25F,
|
||||
"rare" to 4.125F,
|
||||
"ultra-rare" to 1.375F
|
||||
)
|
||||
|
||||
val activatedHabitatBuckets: MutableMap<String, Float> = mutableMapOf(
|
||||
"common" to 83.25F,
|
||||
"uncommon" to 11.25F,
|
||||
"rare" to 4.125F,
|
||||
"ultra-rare" to 1.375F
|
||||
)
|
||||
|
||||
val pokeSnackBuckets = mutableMapOf(
|
||||
"common" to 83.25F,
|
||||
"uncommon" to 11.25F,
|
||||
"rare" to 4.125F,
|
||||
"ultra-rare" to 1.375F
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
+2
-3
@@ -8,13 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.preset
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnLoader
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
|
||||
import com.cobblemon.mod.common.util.MergeMode
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnDetailAdapter
|
||||
import com.google.gson.JsonObject
|
||||
@@ -41,7 +40,7 @@ import com.google.gson.annotations.SerializedName
|
||||
* @since July 8th, 2022
|
||||
*/
|
||||
abstract class SpawnDetailPreset {
|
||||
var bucket: SpawnBucket? = null
|
||||
var bucket: String? = null
|
||||
var spawnDetailType: String? = null
|
||||
@SerializedName("spawnablePositionType", alternate = ["context"])
|
||||
var spawnablePositionType: SpawnablePositionType<*>? = null
|
||||
|
||||
+6
-7
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.selection
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -88,12 +87,12 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
class SpawnablePositionSelectionData(
|
||||
val spawner: Spawner,
|
||||
val spawnablePositions: List<SpawnablePosition>,
|
||||
val selectingSpawnInformation: MutableMap<SpawnBucket, SpawnablePositionBucketSelectionData>,
|
||||
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData>,
|
||||
): SpawnSelectionData {
|
||||
override val spawnActions = mutableListOf<SpawnAction<*>>()
|
||||
override val context = mutableMapOf<String, Any>()
|
||||
|
||||
fun getDataForBucket(bucket: SpawnBucket): SpawnablePositionBucketSelectionData {
|
||||
fun getDataForBucket(bucket: String): SpawnablePositionBucketSelectionData {
|
||||
val existing = selectingSpawnInformation[bucket]
|
||||
if (existing != null) {
|
||||
return existing
|
||||
@@ -130,14 +129,14 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
spawner: Spawner,
|
||||
spawnablePositions: List<SpawnablePosition>
|
||||
): SpawnablePositionSelectionData {
|
||||
val selectingSpawnInformation: MutableMap<SpawnBucket, SpawnablePositionBucketSelectionData> = mutableMapOf()
|
||||
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData> = mutableMapOf()
|
||||
|
||||
return SpawnablePositionSelectionData(spawner, spawnablePositions, selectingSpawnInformation)
|
||||
}
|
||||
|
||||
override fun selectSpawnAction(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SpawnablePositionSelectionData,
|
||||
): SpawnAction<*>? {
|
||||
val selectingSpawnInformation = selectionData.getDataForBucket(bucket) ?: return null
|
||||
@@ -149,7 +148,7 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
if (percentSum > 100) {
|
||||
Cobblemon.LOGGER.warn(
|
||||
"""
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket ${bucket.name}...
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket $bucket...
|
||||
This means you don't understand how this option works.
|
||||
""".trimIndent()
|
||||
)
|
||||
@@ -186,7 +185,7 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
|
||||
|
||||
override fun getTotalWeights(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
spawnablePositions: List<SpawnablePosition>
|
||||
): Map<SpawnDetail, Float> {
|
||||
val selectionData = getSelectionData(spawner, spawnablePositions).getDataForBucket(bucket)
|
||||
|
||||
+5
-6
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.spawning.selection
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -105,7 +104,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
override val spawnActions = mutableListOf<SpawnAction<*>>()
|
||||
override val context = mutableMapOf<String, Any>()
|
||||
|
||||
val bucketData = mutableMapOf<SpawnBucket, MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData>>()
|
||||
val bucketData = mutableMapOf<String, MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData>>()
|
||||
|
||||
override fun removeSpawnDetails(shouldRemove: (SpawnDetail) -> Boolean) {
|
||||
bucketData.values.forEach {
|
||||
@@ -121,7 +120,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
}
|
||||
}
|
||||
|
||||
fun getDataForBucket(bucket: SpawnBucket): MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData> {
|
||||
fun getDataForBucket(bucket: String): MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData> {
|
||||
bucketData[bucket]?.let { return it }
|
||||
|
||||
val spawnablePositionTypeToSpawns = mutableMapOf<SpawnablePositionType<*>, SpawnablePositionSelectionData>()
|
||||
@@ -149,7 +148,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
|
||||
override fun selectSpawnAction(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: SeparatedSelectionData
|
||||
): SpawnAction<*>? {
|
||||
val bucketData = selectionData.getDataForBucket(bucket)
|
||||
@@ -172,7 +171,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
if (percentSum > 100) {
|
||||
LOGGER.warn(
|
||||
"""
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket ${bucket.name}...
|
||||
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket $bucket...
|
||||
This means you don't understand how this option works.
|
||||
""".trimIndent()
|
||||
)
|
||||
@@ -244,7 +243,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
|
||||
|
||||
override fun getTotalWeights(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
spawnablePositions: List<SpawnablePosition>
|
||||
): Map<SpawnDetail, Float> {
|
||||
val selectionData = getSelectionData(spawner, spawnablePositions).getDataForBucket(bucket)
|
||||
|
||||
+4
-5
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.selection
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -33,13 +32,13 @@ interface SpawningSelector<T : SpawnSelectionData> {
|
||||
|
||||
fun selectSpawnAction(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
bucket: String,
|
||||
selectionData: T
|
||||
): SpawnAction<*>?
|
||||
|
||||
fun select(
|
||||
spawner: Spawner,
|
||||
bucketSelector: () -> SpawnBucket,
|
||||
bucketSelector: () -> String,
|
||||
spawnablePositions: List<SpawnablePosition>,
|
||||
maxSpawns: Int
|
||||
): List<SpawnAction<*>> {
|
||||
@@ -65,7 +64,7 @@ interface SpawningSelector<T : SpawnSelectionData> {
|
||||
return spawnActions
|
||||
}
|
||||
|
||||
fun getProbabilities(spawner: Spawner, bucket: SpawnBucket, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float> {
|
||||
fun getProbabilities(spawner: Spawner, bucket: String, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float> {
|
||||
val weights = getTotalWeights(spawner, bucket, spawnablePositions)
|
||||
val totalWeight = weights.values.sum()
|
||||
val percentages = mutableMapOf<SpawnDetail, Float>()
|
||||
@@ -73,5 +72,5 @@ interface SpawningSelector<T : SpawnSelectionData> {
|
||||
return percentages
|
||||
}
|
||||
|
||||
fun getTotalWeights(spawner: Spawner, bucket: SpawnBucket, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float>
|
||||
fun getTotalWeights(spawner: Spawner, bucket: String, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float>
|
||||
}
|
||||
+2
-1
@@ -25,7 +25,8 @@ import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
|
||||
open class BasicSpawner(
|
||||
override val name: String,
|
||||
override var spawnPool: SpawnPool,
|
||||
override var maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk
|
||||
override var maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk,
|
||||
override val buckets: Map<String, Float>
|
||||
) : Spawner {
|
||||
override var selector: SpawningSelector<*> = SpawningSelector.DEFAULT
|
||||
override var generator: SpawningZoneGenerator = Cobblemon.spawningZoneGenerator
|
||||
|
||||
+4
-11
@@ -8,11 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence.Companion.TYPICAL_VARIATION
|
||||
@@ -72,16 +72,9 @@ object FishingSpawnerFactory {
|
||||
fun createSharedSpawner(): BasicSpawner {
|
||||
return BasicSpawner(
|
||||
name = "fishing",
|
||||
spawnPool = spawnPool
|
||||
).also { spawner ->
|
||||
spawner.influences += BucketMultiplyingInfluence(
|
||||
multipliers = mapOf(
|
||||
"uncommon" to 2.25f,
|
||||
"rare" to 5.5f,
|
||||
"ultra-rare" to 5.5f,
|
||||
)
|
||||
)
|
||||
}
|
||||
spawnPool = spawnPool,
|
||||
buckets = Cobblemon.bestSpawner.config.fishingBuckets,
|
||||
)
|
||||
}
|
||||
|
||||
fun findHabitatInfluences(world: ServerLevel, position: BlockPos, spawner: Spawner): Array<HabitatBlockEntity> {
|
||||
|
||||
+3
-2
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.api.spawning.spawner
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
||||
/**
|
||||
* A spawner that works within a fixed area. Instances provide the center point and the radii and then the spawner
|
||||
@@ -24,12 +24,13 @@ import net.minecraft.core.BlockPos
|
||||
open class FixedAreaSpawner(
|
||||
name: String,
|
||||
spawnPool: SpawnPool,
|
||||
buckets: Map<String, Float>,
|
||||
val world: ServerLevel,
|
||||
val position: BlockPos,
|
||||
val horizontalRadius: Int,
|
||||
val verticalRadius: Int,
|
||||
maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk
|
||||
) : BasicSpawner(name, spawnPool, maxPokemonPerChunk) {
|
||||
) : BasicSpawner(name, spawnPool, maxPokemonPerChunk, buckets) {
|
||||
fun run(cause: SpawnCause, maxSpawns: Int? = null): List<Any> {
|
||||
return runForArea(
|
||||
zoneInput = getZoneInput(cause),
|
||||
|
||||
+8
-6
@@ -8,20 +8,21 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.config
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.nextBetween
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.Mth.PI
|
||||
import net.minecraft.util.Mth.ceil
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import kotlin.math.atan
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.util.Mth.PI
|
||||
import net.minecraft.util.Mth.ceil
|
||||
|
||||
/**
|
||||
* A spawner that works around a single player. It will do basic tracking of a player's speed
|
||||
@@ -35,7 +36,8 @@ open class PlayerSpawner(
|
||||
spawnPool: SpawnPool
|
||||
) : BasicSpawner(
|
||||
name = player.name.string,
|
||||
spawnPool = spawnPool
|
||||
spawnPool = spawnPool,
|
||||
buckets = Cobblemon.bestSpawner.config.worldBuckets
|
||||
) {
|
||||
val uuid: UUID = player.uuid
|
||||
|
||||
|
||||
+2
-10
@@ -8,11 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.spawner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
@@ -61,15 +61,6 @@ object PokeSnackSpawnerFactory {
|
||||
firstTier = 1.2F
|
||||
)
|
||||
},
|
||||
{ _ ->
|
||||
BucketMultiplyingInfluence(
|
||||
mapOf(
|
||||
"uncommon" to 2.25f,
|
||||
"rare" to 5.5f,
|
||||
"ultra-rare" to 5.5f,
|
||||
)
|
||||
)
|
||||
},
|
||||
{ ctx ->
|
||||
SpawnBaitInfluence(effects = ctx.baitEffects)
|
||||
},
|
||||
@@ -81,6 +72,7 @@ object PokeSnackSpawnerFactory {
|
||||
return FixedAreaSpawner(
|
||||
name = ctx.name,
|
||||
spawnPool = spawnPool,
|
||||
buckets = Cobblemon.bestSpawner.config.pokeSnackBuckets,
|
||||
world = ctx.world,
|
||||
position = ctx.pos,
|
||||
horizontalRadius = ctx.horizontalRadius,
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.entity.SpawnBucketChosenEvent
|
||||
import com.cobblemon.mod.common.api.spawning.BestSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
|
||||
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
|
||||
@@ -26,6 +25,7 @@ import com.cobblemon.mod.common.api.spawning.position.calculators.SpawnablePosit
|
||||
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.isBoxLoaded
|
||||
import com.cobblemon.mod.common.util.removeIf
|
||||
import com.cobblemon.mod.common.util.squeezeWithinBounds
|
||||
import com.cobblemon.mod.common.util.toVec3f
|
||||
import com.cobblemon.mod.common.util.weightedSelection
|
||||
@@ -52,6 +52,7 @@ import net.minecraft.world.phys.Vec3
|
||||
interface Spawner {
|
||||
val name: String
|
||||
val influences: MutableList<SpawningInfluence>
|
||||
val buckets: Map<String, Float>
|
||||
var spawnPool: SpawnPool
|
||||
var selector: SpawningSelector<*>
|
||||
var generator: SpawningZoneGenerator
|
||||
@@ -65,7 +66,7 @@ interface Spawner {
|
||||
const val ENTITY_LIMIT_CHUNK_RANGE = 3
|
||||
}
|
||||
|
||||
fun getMatchingSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
fun getMatchingSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
|
||||
val spawns = mutableListOf<SpawnDetail>()
|
||||
spawns.addAll(spawnPool.retrieve(bucket, spawnablePosition).filter { it.isSatisfiedBy(spawnablePosition) })
|
||||
spawnablePosition.influences.forEach { influence ->
|
||||
@@ -172,11 +173,12 @@ interface Spawner {
|
||||
|
||||
fun copyInfluences() = influences.filter { !it.isExpired() }.toMutableList()
|
||||
|
||||
fun chooseBucket(cause: SpawnCause, influences: List<SpawningInfluence>): SpawnBucket {
|
||||
val buckets = Cobblemon.bestSpawner.config.buckets
|
||||
val bucketWeights = buckets.associateWith { it.weight }.toMutableMap()
|
||||
fun chooseBucket(cause: SpawnCause, influences: List<SpawningInfluence>): String {
|
||||
val bucketWeights = buckets.toMutableMap()
|
||||
influences.forEach { it.affectBucketWeights(bucketWeights) }
|
||||
val bucket = bucketWeights.entries.weightedSelection { it.value }?.key ?: buckets.first()
|
||||
val usedBuckets = spawnPool.buckets.intersect(bucketWeights.keys)
|
||||
bucketWeights.removeIf { it.key !in usedBuckets }
|
||||
val bucket = bucketWeights.entries.weightedSelection { it.value }?.key ?: buckets.keys.first()
|
||||
val event = SpawnBucketChosenEvent(
|
||||
spawner = this,
|
||||
spawnCause = cause,
|
||||
|
||||
+16
-25
@@ -11,16 +11,13 @@ package com.cobblemon.mod.common.block.habitat
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPhaseOrder
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.HabitatSpawningStyle
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
@@ -69,12 +66,15 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
|
||||
var levelRange: IntRange = 1..Cobblemon.config.maxPokemonLevel
|
||||
var modifiers: PokemonProperties = PokemonProperties()
|
||||
|
||||
var pool: HabitatPool = HabitatPool.default()
|
||||
var mimicId: ResourceLocation = BuiltInRegistries.BLOCK.getKey(Blocks.STONE)
|
||||
val mimickedState: BlockState
|
||||
get() = BuiltInRegistries.BLOCK.get(mimicId).defaultBlockState() ?: Blocks.STONE.defaultBlockState()
|
||||
var displaySpeciesIds: List<ResourceLocation> = emptyList()
|
||||
|
||||
// This is cached here so that it doesn't have to be regenerated every time the block is triggered, updated when settings change.
|
||||
var spawnDetails: List<SpawnDetail> = listOf()
|
||||
|
||||
fun onUse(player: Player, hit: BlockHitResult): InteractionResult {
|
||||
if (player.level().isClientSide) {
|
||||
return InteractionResult.SUCCESS
|
||||
@@ -97,6 +97,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
val spawningStyle = spawningStyle
|
||||
var activatedStyle = false
|
||||
var cancelsRegularSpawns = false
|
||||
spawnDetails = pool.createSpawnDetails(this)
|
||||
if (spawningStyle is ActivatedHabitatSpawning) {
|
||||
spawningStyle.generateSpawnDetails(level as ServerLevel, blockPos)
|
||||
activatedStyle = true
|
||||
@@ -136,21 +137,14 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
|
||||
fun applySettings(habitatSettingsDTO: HabitatSettingsDTO) {
|
||||
val activatedSettings = habitatSettingsDTO.activatedSettings
|
||||
val activatedPool = habitatSettingsDTO.activatedSettings?.habitatPool?.toHabitatPool() as? ActivatedHabitatPool
|
||||
val isActivated = habitatSettingsDTO.isActivatedSpawning
|
||||
if (isActivated && activatedPool == null) {
|
||||
return // Pool was bad, check console logs
|
||||
}
|
||||
val naturalSettings = habitatSettingsDTO.naturalSettings
|
||||
val naturalPool = habitatSettingsDTO.naturalSettings?.habitatPool?.toHabitatPool() as? NaturalHabitatPool
|
||||
if (!isActivated && naturalPool == null) {
|
||||
return // Pool was bad, check console logs
|
||||
}
|
||||
val isActivated = habitatSettingsDTO.isActivatedSpawning
|
||||
this.numberOfPhases = habitatSettingsDTO.numberOfPhases
|
||||
this.phaseOrder = habitatSettingsDTO.phaseOrder
|
||||
this.levelRange = habitatSettingsDTO.levelRange
|
||||
this.modifiers = PokemonProperties.parse(habitatSettingsDTO.modifiers)
|
||||
this.mimicId = habitatSettingsDTO.mimicId
|
||||
this.pool = habitatSettingsDTO.pool.toHabitatPool() ?: HabitatPool.default()
|
||||
if (isActivated && activatedSettings != null) {
|
||||
val activatedHabitatSpawning = ActivatedHabitatSpawning(this)
|
||||
activatedHabitatSpawning.trigger = activatedSettings.trigger
|
||||
@@ -159,13 +153,11 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
activatedHabitatSpawning.spawnRange = activatedSettings.spawnRange
|
||||
activatedHabitatSpawning.maxSpawns = activatedSettings.maxSpawns
|
||||
activatedHabitatSpawning.maxSpawnsPerActivation = activatedSettings.maxSpawnsPerActivation
|
||||
activatedHabitatSpawning.pool = activatedPool!!
|
||||
this.spawningStyle = activatedHabitatSpawning
|
||||
} else if (naturalSettings != null && naturalPool != null) {
|
||||
} else if (naturalSettings != null) {
|
||||
val naturalHabitatSpawning = NaturalHabitatSpawning(this)
|
||||
naturalHabitatSpawning.replaceSpawns = naturalSettings.replaceSpawns
|
||||
naturalHabitatSpawning.rangeOfInfluence = naturalSettings.rangeOfInfluence
|
||||
naturalHabitatSpawning.pool = naturalPool
|
||||
this.spawningStyle = naturalHabitatSpawning
|
||||
}
|
||||
|
||||
@@ -186,6 +178,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
tag.putString(DataKeys.HABITAT_MODIFIERS, modifiers.asString())
|
||||
tag.putString(DataKeys.HABITAT_SPAWNING_STYLE, spawningStyle.type.toString())
|
||||
spawningStyle.writeToNBT(tag)
|
||||
HabitatPool.writePoolToNBT(pool = pool, nbt = tag)
|
||||
val speciesListTag = ListTag()
|
||||
displaySpeciesIds.forEach { speciesListTag.add(StringTag.valueOf(it.toString())) }
|
||||
tag.put(DISPLAY_SPECIES_KEY, speciesListTag)
|
||||
@@ -213,12 +206,13 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
levelRange = tag.getString(DataKeys.HABITAT_LEVEL_RANGE).takeIf { it.isNotBlank() }?.let(IntRangeAdapter::deserialize) ?: 1..Cobblemon.config.maxPokemonLevel
|
||||
modifiers = tag.getString(DataKeys.HABITAT_MODIFIERS).takeIf { it.isNotBlank() }?.let(PokemonProperties::parse) ?: PokemonProperties()
|
||||
val spawningStyleType = tag.getString(DataKeys.HABITAT_SPAWNING_STYLE).asIdentifierDefaultingNamespace()
|
||||
spawningStyle = if (spawningStyleType == ActivatedHabitatSpawn.TYPE) {
|
||||
spawningStyle = if (spawningStyleType == ActivatedHabitatSpawning.TYPE) {
|
||||
ActivatedHabitatSpawning(this)
|
||||
} else {
|
||||
NaturalHabitatSpawning(this)
|
||||
}
|
||||
spawningStyle.readFromNBT(tag)
|
||||
pool = HabitatPool.readPoolFromNBT(tag)
|
||||
refreshDisplaySpeciesIds()
|
||||
}
|
||||
|
||||
@@ -237,10 +231,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
}
|
||||
|
||||
private fun refreshDisplaySpeciesIds() {
|
||||
displaySpeciesIds = when (val style = spawningStyle) {
|
||||
is ActivatedHabitatSpawning -> style.pool.spawns.map { it.species.resourceIdentifier }.distinct()
|
||||
is NaturalHabitatSpawning -> style.pool.spawns.map { it.species.resourceIdentifier }.distinct()
|
||||
}
|
||||
displaySpeciesIds = pool.spawns.map { it.species.resourceIdentifier }.distinct()
|
||||
}
|
||||
|
||||
fun getInfluentialRange(spawner: Spawner): Int {
|
||||
@@ -273,7 +264,7 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
return if (spawningStyle is NaturalHabitatSpawning) {
|
||||
if (spawningStyle.replaceSpawns) {
|
||||
// Replace mode! Permit spawning only if it's from this block
|
||||
detail in spawningStyle.spawnDetails
|
||||
detail in spawnDetails
|
||||
} else {
|
||||
true // We don't mess with other types
|
||||
}
|
||||
@@ -285,10 +276,10 @@ class HabitatBlockEntity(pos: BlockPos, state: BlockState) :
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>? {
|
||||
override fun injectSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>? {
|
||||
val spawningStyle = spawningStyle
|
||||
return if (spawningStyle is NaturalHabitatSpawning) {
|
||||
spawningStyle.spawnDetails.filter { it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
|
||||
spawnDetails.filter { it.spawnablePositionType.clazz == spawnablePosition::class.java && it.isSatisfiedBy(spawnablePosition) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
+22
-67
@@ -8,15 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.habitat
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPhaseOrder
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.dto.ActivatedHabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatPoolSettingsDTO
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.api.habitats.dto.NaturalHabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
@@ -47,12 +45,12 @@ class HabitatEditGUI(
|
||||
val maxPokemonLevel: Int,
|
||||
/** Use this as reference data for what spawnable position types exist. */
|
||||
val spawnablePositionTypes: List<String>,
|
||||
/** Use this as reference data for what buckets can be chosen and how rare they are */
|
||||
val buckets: List<SpawnBucket>,
|
||||
/** Use this as reference data for pre-made activated habitat pools */
|
||||
val activatedHabitatPools: Map<ResourceLocation, ActivatedHabitatPool>,
|
||||
/** Use this as reference data for pre-made natural habitat pools */
|
||||
val naturalHabitatPools: Map<ResourceLocation, NaturalHabitatPool>,
|
||||
/** Use this as reference data for what buckets can be chosen for natural spawns */
|
||||
val worldBuckets: List<String>,
|
||||
/** Use this as reference data for what buckets can be chosen for activated spawns */
|
||||
val activatedHabitatBuckets: List<String>,
|
||||
/** Use this as reference data for pre-made habitat pools */
|
||||
val pools: Map<ResourceLocation, HabitatPool>,
|
||||
/** The current settings - edit the properties in here then send it back to the server. */
|
||||
val habitatSettingsDTO: HabitatSettingsDTO
|
||||
) : Screen(lang("ui.edit.habitat")), CobblemonRenderable {
|
||||
@@ -124,7 +122,7 @@ class HabitatEditGUI(
|
||||
val phases = value.toIntOrNull()
|
||||
val valid = phases != null && phases > 0
|
||||
if (valid) {
|
||||
habitatSettingsDTO.numberOfPhases = phases!!
|
||||
habitatSettingsDTO.numberOfPhases = phases
|
||||
}
|
||||
phasesInput.setTextColor(if (valid) validTextColor else invalidTextColor)
|
||||
setFieldValidity("phases", valid, "ui.edit.habitat.validation.phases")
|
||||
@@ -289,17 +287,12 @@ class HabitatEditGUI(
|
||||
// Pool Selection
|
||||
poolSelectorButton = addOptionWidget(
|
||||
Button.builder(getPoolText()) {
|
||||
val currentPool = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
habitatSettingsDTO.activatedSettings?.habitatPool
|
||||
} else {
|
||||
habitatSettingsDTO.naturalSettings?.habitatPool
|
||||
}
|
||||
val currentPool = habitatSettingsDTO.pool
|
||||
val selectedPoolId = if (currentPool?.isReference == true) currentPool.id else null
|
||||
Minecraft.getInstance().setScreen(
|
||||
HabitatPoolSelectionGUI(
|
||||
isActivated = habitatSettingsDTO.isActivatedSpawning,
|
||||
activatedPools = activatedHabitatPools,
|
||||
naturalPools = naturalHabitatPools,
|
||||
pools = pools,
|
||||
selectedPoolId = selectedPoolId,
|
||||
parentScreen = this
|
||||
) { poolId ->
|
||||
@@ -312,11 +305,8 @@ class HabitatEditGUI(
|
||||
|
||||
viewPoolButton = addOptionWidget(
|
||||
Button.builder(getViewPoolText()) {
|
||||
val pool = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
habitatSettingsDTO.activatedSettings?.habitatPool
|
||||
} else {
|
||||
habitatSettingsDTO.naturalSettings?.habitatPool
|
||||
} ?: return@builder
|
||||
val pool = habitatSettingsDTO.pool
|
||||
val buckets = if (habitatSettingsDTO.isActivatedSpawning) activatedHabitatBuckets else worldBuckets
|
||||
|
||||
if (!pool.isReference) {
|
||||
Minecraft.getInstance().setScreen(
|
||||
@@ -325,16 +315,11 @@ class HabitatEditGUI(
|
||||
return@builder
|
||||
}
|
||||
|
||||
val sourcePool = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
activatedHabitatPools[pool.id]
|
||||
} else {
|
||||
naturalHabitatPools[pool.id]
|
||||
} ?: return@builder
|
||||
val sourcePool = pools[pool.id] ?: return@builder
|
||||
|
||||
val viewPool = HabitatPoolSettingsDTO(sourcePool).apply {
|
||||
isReference = true
|
||||
id = sourcePool.id
|
||||
isActivated = habitatSettingsDTO.isActivatedSpawning
|
||||
name = sourcePool.name
|
||||
spawns = sourcePool.spawns.toMutableList()
|
||||
}
|
||||
@@ -424,29 +409,17 @@ class HabitatEditGUI(
|
||||
private fun getReplaceSpawnsText() = lang("ui.edit.habitat.replace_spawns", habitatSettingsDTO.naturalSettings?.replaceSpawns ?: false)
|
||||
|
||||
private fun getPoolText(): net.minecraft.network.chat.Component {
|
||||
val pool = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
habitatSettingsDTO.activatedSettings?.habitatPool
|
||||
} else {
|
||||
habitatSettingsDTO.naturalSettings?.habitatPool
|
||||
}
|
||||
val pool = habitatSettingsDTO.pool
|
||||
if (pool == null || !pool.isReference) {
|
||||
return lang("ui.edit.habitat.pool", lang("ui.edit.habitat.pool.custom"))
|
||||
}
|
||||
val poolName = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
activatedHabitatPools[pool.id]?.name?.asTranslated()
|
||||
} else {
|
||||
naturalHabitatPools[pool.id]?.name?.asTranslated()
|
||||
}
|
||||
val poolName = pools[pool.id]?.name?.asTranslated()
|
||||
return lang("ui.edit.habitat.pool", poolName ?: pool.id)
|
||||
}
|
||||
|
||||
private fun getViewPoolText(): net.minecraft.network.chat.Component {
|
||||
val pool = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
habitatSettingsDTO.activatedSettings?.habitatPool
|
||||
} else {
|
||||
habitatSettingsDTO.naturalSettings?.habitatPool
|
||||
}
|
||||
return if (pool?.isReference == true) {
|
||||
val pool = habitatSettingsDTO.pool
|
||||
return if (pool.isReference) {
|
||||
lang("ui.edit.habitat.view_pool")
|
||||
} else {
|
||||
lang("ui.edit.habitat.edit_pool")
|
||||
@@ -454,47 +427,29 @@ class HabitatEditGUI(
|
||||
}
|
||||
|
||||
private fun applyPoolSelection(poolId: ResourceLocation?) {
|
||||
val currentPool = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
habitatSettingsDTO.activatedSettings?.habitatPool
|
||||
} else {
|
||||
habitatSettingsDTO.naturalSettings?.habitatPool
|
||||
}
|
||||
val currentPool = habitatSettingsDTO.pool
|
||||
if (poolId == null) {
|
||||
if (currentPool == null || currentPool.isReference) {
|
||||
if (currentPool.isReference) {
|
||||
val newCustomPool = HabitatPoolSettingsDTO().apply {
|
||||
id = cobblemonResource("custom__${System.currentTimeMillis()}")
|
||||
isReference = false
|
||||
isActivated = habitatSettingsDTO.isActivatedSpawning
|
||||
name = "Custom"
|
||||
spawns = mutableListOf()
|
||||
}
|
||||
if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
habitatSettingsDTO.activatedSettings?.habitatPool = newCustomPool
|
||||
} else {
|
||||
habitatSettingsDTO.naturalSettings?.habitatPool = newCustomPool
|
||||
}
|
||||
habitatSettingsDTO.pool = newCustomPool
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val sourcePool = if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
activatedHabitatPools[poolId]
|
||||
} else {
|
||||
naturalHabitatPools[poolId]
|
||||
} ?: return
|
||||
val sourcePool = pools[poolId] ?: return
|
||||
|
||||
val selectedPool = HabitatPoolSettingsDTO(sourcePool).apply {
|
||||
isReference = true
|
||||
id = poolId
|
||||
isActivated = habitatSettingsDTO.isActivatedSpawning
|
||||
name = sourcePool.name
|
||||
}
|
||||
|
||||
if (habitatSettingsDTO.isActivatedSpawning) {
|
||||
habitatSettingsDTO.activatedSettings?.habitatPool = selectedPool
|
||||
} else {
|
||||
habitatSettingsDTO.naturalSettings?.habitatPool = selectedPool
|
||||
}
|
||||
habitatSettingsDTO.pool = selectedPool
|
||||
}
|
||||
|
||||
private fun validateLevelRangeInputs() {
|
||||
|
||||
+11
-19
@@ -8,11 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.habitat
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatPoolSettingsDTO
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
|
||||
@@ -28,7 +26,7 @@ import net.minecraft.network.chat.MutableComponent
|
||||
class HabitatPoolEditorGUI(
|
||||
val pool: HabitatPoolSettingsDTO,
|
||||
val isActivated: Boolean,
|
||||
val buckets: List<SpawnBucket>,
|
||||
val buckets: List<String>,
|
||||
val spawnablePositionTypes: List<String>,
|
||||
val maxPokemonLevel: Int,
|
||||
val parentScreen: Screen,
|
||||
@@ -73,7 +71,7 @@ class HabitatPoolEditorGUI(
|
||||
}
|
||||
|
||||
// Headers
|
||||
val colWidth = if (isActivated) buttonWidth / 5 else buttonWidth / 6
|
||||
val colWidth = buttonWidth / 6
|
||||
var x = 10
|
||||
headers.add(lang("ui.edit.habitat.pool.species") to x)
|
||||
x += colWidth
|
||||
@@ -84,16 +82,12 @@ class HabitatPoolEditorGUI(
|
||||
headers.add(lang("ui.edit.habitat.pool.position") to x)
|
||||
x += colWidth
|
||||
headers.add(lang("ui.edit.habitat.pool.weight") to x)
|
||||
if (!isActivated) {
|
||||
x += colWidth - 10
|
||||
headers.add(lang("ui.edit.habitat.pool.bucket") to x)
|
||||
}
|
||||
x += colWidth - 10
|
||||
headers.add(lang("ui.edit.habitat.pool.bucket") to x)
|
||||
y += 20
|
||||
|
||||
var spawns = pool.spawns!!
|
||||
if (!isActivated) {
|
||||
spawns = spawns.sortedBy { (it as? NaturalHabitatSpawn)?.bucket?.name?.first() }.toMutableList()
|
||||
}
|
||||
spawns = spawns.sortedBy { it.bucket }.toMutableList()
|
||||
|
||||
spawns.forEachIndexed { index, spawn ->
|
||||
val rowY = y
|
||||
@@ -109,14 +103,14 @@ class HabitatPoolEditorGUI(
|
||||
phases = spawn.phases?.let { IntRangesAdapter.basic.serialize(it) } ?: "All",
|
||||
position = spawn.spawnablePositionType,
|
||||
weight = spawn.weight.toString(),
|
||||
bucket = if (!isActivated && spawn is NaturalHabitatSpawn) spawn.bucket.name.first().uppercaseChar().toString() else null,
|
||||
bucket = spawn.bucket.first().uppercaseChar().toString(),
|
||||
y = rowY
|
||||
))
|
||||
|
||||
if (editable) {
|
||||
addOptionWidget(
|
||||
Button.builder("E".text()) {
|
||||
Minecraft.getInstance().setScreen(HabitatSpawnEditorGUI(spawn, isActivated, maxPokemonLevel, buckets, spawnablePositionTypes, this))
|
||||
Minecraft.getInstance().setScreen(HabitatSpawnEditorGUI(spawn, maxPokemonLevel, buckets, spawnablePositionTypes, this))
|
||||
}.bounds(getScaledWidth() - 55, rowY, 20, 20).build()
|
||||
)
|
||||
|
||||
@@ -134,12 +128,10 @@ class HabitatPoolEditorGUI(
|
||||
if (editable) {
|
||||
addOptionWidget(
|
||||
Button.builder(lang("ui.edit.habitat.pool.add_spawn")) {
|
||||
val newSpawn = if (isActivated) ActivatedHabitatSpawn() else NaturalHabitatSpawn()
|
||||
val newSpawn = HabitatSpawn()
|
||||
newSpawn.species = PokemonSpecies.implemented.random()
|
||||
newSpawn.spawnablePositionType = spawnablePositionTypes.firstOrNull() ?: "grounded"
|
||||
if (newSpawn is NaturalHabitatSpawn) {
|
||||
newSpawn.bucket = buckets.firstOrNull() ?: return@builder
|
||||
}
|
||||
newSpawn.bucket = buckets.firstOrNull() ?: return@builder
|
||||
pool.spawns!!.add(newSpawn)
|
||||
rebuildWidgets()
|
||||
}.bounds(10, y, buttonWidth, 20).build()
|
||||
@@ -179,7 +171,7 @@ class HabitatPoolEditorGUI(
|
||||
guiGraphics.drawCenteredString(font, title, width / 2, 8, 0xFFFFFF)
|
||||
|
||||
val buttonWidth = getScaledWidth() - 20
|
||||
val colWidth = if (isActivated) buttonWidth / 5 else buttonWidth / 6
|
||||
val colWidth = buttonWidth / 6
|
||||
val optionsTop = getOptionsTop()
|
||||
val optionsBottom = getOptionsBottom()
|
||||
|
||||
|
||||
+3
-10
@@ -8,8 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.habitat
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
@@ -22,13 +21,11 @@ import net.minecraft.client.gui.components.ObjectSelectionList
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.client.resources.language.I18n
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class HabitatPoolSelectionGUI(
|
||||
private val isActivated: Boolean,
|
||||
private val activatedPools: Map<ResourceLocation, ActivatedHabitatPool>,
|
||||
private val naturalPools: Map<ResourceLocation, NaturalHabitatPool>,
|
||||
private val pools: Map<ResourceLocation, HabitatPool>,
|
||||
private val selectedPoolId: ResourceLocation?,
|
||||
private val parentScreen: Screen,
|
||||
private val onSelect: (ResourceLocation?) -> Unit
|
||||
@@ -85,11 +82,7 @@ class HabitatPoolSelectionGUI(
|
||||
private fun getOptions(): List<Pair<ResourceLocation?, String>> {
|
||||
val options = mutableListOf<Pair<ResourceLocation?, String>>()
|
||||
options.add(null to I18n.get("cobblemon.ui.edit.habitat.pool.custom"))
|
||||
val pools = if (isActivated) {
|
||||
activatedPools.entries.map { it.key to I18n.get(it.value.name) }
|
||||
} else {
|
||||
naturalPools.entries.map { it.key to I18n.get(it.value.name) }
|
||||
}.sortedBy { it.second.lowercase() }
|
||||
val pools = pools.entries.map { it.key to I18n.get(it.value.name) }.sortedBy { it.second.lowercase() }
|
||||
options.addAll(pools)
|
||||
return options
|
||||
}
|
||||
|
||||
+10
-17
@@ -9,11 +9,9 @@
|
||||
package com.cobblemon.mod.common.client.gui.habitat
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.spawning.IntRanges
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
@@ -32,9 +30,8 @@ import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
class HabitatSpawnEditorGUI(
|
||||
val spawn: HabitatSpawn,
|
||||
val isActivated: Boolean,
|
||||
val maxPokemonLevel: Int,
|
||||
val buckets: List<SpawnBucket>,
|
||||
val buckets: List<String>,
|
||||
val spawnablePositionTypes: List<String>,
|
||||
val parentScreen: Screen
|
||||
) : Screen(lang("ui.edit.habitat.spawn")), CobblemonRenderable {
|
||||
@@ -110,16 +107,14 @@ class HabitatSpawnEditorGUI(
|
||||
)
|
||||
y += spacing
|
||||
|
||||
if (spawn is NaturalHabitatSpawn) {
|
||||
bucketButton = addOptionWidget(
|
||||
Button.builder(lang("ui.edit.habitat.spawn.bucket", spawn.bucket.name)) {
|
||||
val currentIndex = buckets.indexOf(spawn.bucket)
|
||||
spawn.bucket = buckets[(currentIndex + 1) % buckets.size]
|
||||
refresh()
|
||||
}.bounds(10, y, buttonWidth, 20).build()
|
||||
)
|
||||
y += spacing
|
||||
}
|
||||
bucketButton = addOptionWidget(
|
||||
Button.builder(lang("ui.edit.habitat.spawn.bucket", spawn.bucket)) {
|
||||
val currentIndex = buckets.indexOf(spawn.bucket)
|
||||
spawn.bucket = buckets[(currentIndex + 1) % buckets.size]
|
||||
refresh()
|
||||
}.bounds(10, y, buttonWidth, 20).build()
|
||||
)
|
||||
y += spacing
|
||||
|
||||
labels.add(lang("ui.edit.habitat.spawn.weight") to (10 to y))
|
||||
y += 12
|
||||
@@ -309,9 +304,7 @@ class HabitatSpawnEditorGUI(
|
||||
|
||||
fun refresh() {
|
||||
positionTypeButton.message = lang("ui.edit.habitat.spawn.position_type", spawn.spawnablePositionType)
|
||||
if (spawn is NaturalHabitatSpawn) {
|
||||
bucketButton.message = lang("ui.edit.habitat.spawn.bucket", spawn.bucket.name)
|
||||
}
|
||||
bucketButton.message = lang("ui.edit.habitat.spawn.bucket", spawn.bucket)
|
||||
}
|
||||
|
||||
// data validation for error handling
|
||||
|
||||
+3
-3
@@ -22,9 +22,9 @@ object OpenHabitatBlockEditorHandler : ClientNetworkPacketHandler<OpenHabitatBlo
|
||||
currentPhase = packet.currentPhase,
|
||||
maxPokemonLevel = packet.maxPokemonLevel,
|
||||
spawnablePositionTypes = packet.spawnablePositionTypes,
|
||||
buckets = packet.buckets,
|
||||
activatedHabitatPools = packet.activatedHabitatPools,
|
||||
naturalHabitatPools = packet.naturalHabitatPools,
|
||||
worldBuckets = packet.worldBuckets,
|
||||
activatedHabitatBuckets = packet.activatedHabitatBuckets,
|
||||
pools = packet.habitatPools,
|
||||
habitatSettingsDTO = packet.habitatSettingsDTO
|
||||
)
|
||||
)
|
||||
|
||||
+8
-9
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.command.argument
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.mojang.brigadier.StringReader
|
||||
import com.mojang.brigadier.arguments.ArgumentType
|
||||
@@ -17,25 +16,25 @@ import com.mojang.brigadier.context.CommandContext
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType
|
||||
import com.mojang.brigadier.suggestion.Suggestions
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder
|
||||
import net.minecraft.commands.SharedSuggestionProvider
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.commands.SharedSuggestionProvider
|
||||
|
||||
class SpawnBucketArgumentType: ArgumentType<SpawnBucket> {
|
||||
class SpawnBucketArgumentType: ArgumentType<String> {
|
||||
|
||||
companion object {
|
||||
val EXAMPLES: List<String> = listOf(Cobblemon.bestSpawner.config.buckets.first().name)
|
||||
val EXAMPLES: List<String> = listOf(Cobblemon.bestSpawner.config.worldBuckets.keys.first())
|
||||
val INVALID_BUCKET = lang("command.checkspawns.invalid-bucket")
|
||||
|
||||
fun spawnBucket() = SpawnBucketArgumentType()
|
||||
|
||||
fun <S> getSpawnBucket(context: CommandContext<S>, name: String): SpawnBucket {
|
||||
return context.getArgument(name, SpawnBucket::class.java)
|
||||
fun <S> getSpawnBucket(context: CommandContext<S>, name: String): String {
|
||||
return context.getArgument(name, String::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
override fun parse(reader: StringReader): SpawnBucket {
|
||||
override fun parse(reader: StringReader): String {
|
||||
val name = reader.readString()
|
||||
return Cobblemon.bestSpawner.config.buckets.find { it.name.equals(name, ignoreCase = true) }
|
||||
return Cobblemon.bestSpawner.config.worldBuckets.entries.find { it.key.equals(name, ignoreCase = true) }?.key
|
||||
?: throw SimpleCommandExceptionType(INVALID_BUCKET).createWithContext(reader)
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ class SpawnBucketArgumentType: ArgumentType<SpawnBucket> {
|
||||
context: CommandContext<S>,
|
||||
builder: SuggestionsBuilder
|
||||
): CompletableFuture<Suggestions> {
|
||||
return SharedSuggestionProvider.suggest(Cobblemon.bestSpawner.config.buckets.map { it.name }, builder)
|
||||
return SharedSuggestionProvider.suggest(Cobblemon.bestSpawner.config.worldBuckets.keys, builder)
|
||||
}
|
||||
|
||||
override fun getExamples() = EXAMPLES
|
||||
|
||||
+1
-9
@@ -39,7 +39,6 @@ import net.minecraft.advancements.CriteriaTriggers
|
||||
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
|
||||
import net.minecraft.network.syncher.EntityDataAccessor
|
||||
@@ -248,13 +247,6 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
INVALID
|
||||
}
|
||||
|
||||
private fun setPlayerFishHook(fishingBobber: FishingHook?) {
|
||||
val playerEntity = this.playerOwner
|
||||
if (playerEntity != null) {
|
||||
playerEntity.fishing = fishingBobber
|
||||
}
|
||||
}
|
||||
|
||||
enum class State {
|
||||
FLYING,
|
||||
HOOKED_IN_ENTITY,
|
||||
@@ -375,7 +367,7 @@ class PokeRodFishingBobberEntity(type: EntityType<out PokeRodFishingBobberEntity
|
||||
.sumOf { it.value }
|
||||
.toInt()
|
||||
planSpawn(player, rodStack, stackedLureTier)
|
||||
val selectedWeight = plannedSpawnAction?.bucket?.weight ?: 50F
|
||||
val selectedWeight = plannedSpawnAction?.bucket?.let { BestSpawner.fishingSpawner.buckets[it] } ?: 50F
|
||||
val reactionMinMax = calculateMinMaxCountdown(selectedWeight)
|
||||
|
||||
// set the hook reaction time to be based off the rarity of the bucket chosen
|
||||
|
||||
+19
-40
@@ -9,12 +9,10 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.habitat
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
|
||||
import com.cobblemon.mod.common.net.IntSize
|
||||
@@ -38,9 +36,9 @@ class OpenHabitatBlockEditorPacket(
|
||||
val currentPhase: Int,
|
||||
val maxPokemonLevel: Int,
|
||||
val spawnablePositionTypes: List<String>,
|
||||
val buckets: List<SpawnBucket>,
|
||||
val activatedHabitatPools: Map<ResourceLocation, ActivatedHabitatPool>,
|
||||
val naturalHabitatPools: Map<ResourceLocation, NaturalHabitatPool>,
|
||||
val worldBuckets: List<String>,
|
||||
val activatedHabitatBuckets: List<String>,
|
||||
val habitatPools: Map<ResourceLocation, HabitatPool>,
|
||||
val habitatSettingsDTO: HabitatSettingsDTO
|
||||
) : NetworkPacket<OpenHabitatBlockEditorPacket> {
|
||||
companion object {
|
||||
@@ -51,40 +49,28 @@ class OpenHabitatBlockEditorPacket(
|
||||
val currentPhase = buffer.readInt()
|
||||
val maxPokemonLevel = buffer.readInt()
|
||||
val spawnablePositionTypes = buffer.readList { buffer.readUtf() }
|
||||
val buckets = buffer.readList {
|
||||
val name = buffer.readUtf()
|
||||
val weight = buffer.readFloat()
|
||||
SpawnBucket(name = name, weight = weight)
|
||||
}
|
||||
val activatedHabitatPools = buffer.readMapK(
|
||||
val worldBuckets = buffer.readList { buffer.readUtf() }
|
||||
val activatedHabitatBuckets = buffer.readList { buffer.readUtf() }
|
||||
val habitatPools = buffer.readMapK(
|
||||
size = IntSize.U_SHORT,
|
||||
mutableMapOf(),
|
||||
entryReader = {
|
||||
val pool = ActivatedHabitatPool()
|
||||
pool.decode(buffer, buckets)
|
||||
pool.id to pool
|
||||
}
|
||||
)
|
||||
val naturalHabitatPools = buffer.readMapK(
|
||||
size = IntSize.U_SHORT,
|
||||
mutableMapOf(),
|
||||
entryReader = {
|
||||
val pool = NaturalHabitatPool()
|
||||
pool.decode(buffer, buckets)
|
||||
val pool = HabitatPool()
|
||||
pool.decode(buffer)
|
||||
pool.id to pool
|
||||
}
|
||||
)
|
||||
val habitatSettingsDTO = HabitatSettingsDTO()
|
||||
habitatSettingsDTO.decode(buffer, buckets)
|
||||
habitatSettingsDTO.decode(buffer)
|
||||
return OpenHabitatBlockEditorPacket(
|
||||
blockPos = blockPos,
|
||||
currentlySpawned = currentlySpawned,
|
||||
currentPhase = currentPhase,
|
||||
maxPokemonLevel = maxPokemonLevel,
|
||||
spawnablePositionTypes = spawnablePositionTypes,
|
||||
buckets = buckets,
|
||||
activatedHabitatPools = activatedHabitatPools,
|
||||
naturalHabitatPools = naturalHabitatPools,
|
||||
worldBuckets = worldBuckets,
|
||||
activatedHabitatBuckets = activatedHabitatBuckets,
|
||||
habitatPools = habitatPools,
|
||||
habitatSettingsDTO = habitatSettingsDTO
|
||||
)
|
||||
}
|
||||
@@ -96,10 +82,10 @@ class OpenHabitatBlockEditorPacket(
|
||||
currentPhase = habitatBlockEntity.currentPhase,
|
||||
maxPokemonLevel = Cobblemon.config.maxPokemonLevel,
|
||||
spawnablePositionTypes = SpawnablePosition.spawnablePositionTypes.map { it.name },
|
||||
buckets = Cobblemon.bestSpawner.config.buckets,
|
||||
worldBuckets = Cobblemon.bestSpawner.config.worldBuckets.keys.toList(),
|
||||
activatedHabitatBuckets = Cobblemon.bestSpawner.config.activatedHabitatBuckets.keys.toList(),
|
||||
habitatSettingsDTO = HabitatSettingsDTO(habitatBlockEntity),
|
||||
activatedHabitatPools = HabitatPools.habitatPoolsById.entries.filter { it.value is ActivatedHabitatPool }.associate { it.key to it.value as ActivatedHabitatPool },
|
||||
naturalHabitatPools = HabitatPools.habitatPoolsById.entries.filter { it.value is NaturalHabitatPool }.associate { it.key to it.value as NaturalHabitatPool }
|
||||
habitatPools = HabitatPools.habitatPoolsById.toMap(),
|
||||
)
|
||||
|
||||
override val id = ID
|
||||
@@ -110,18 +96,11 @@ class OpenHabitatBlockEditorPacket(
|
||||
buffer.writeInt(currentPhase)
|
||||
buffer.writeInt(maxPokemonLevel)
|
||||
buffer.writeCollection(spawnablePositionTypes) { _, it -> buffer.writeUtf(it) }
|
||||
buffer.writeCollection(buckets) { _, bucket ->
|
||||
buffer.writeUtf(bucket.name)
|
||||
buffer.writeFloat(bucket.weight)
|
||||
}
|
||||
buffer.writeCollection(worldBuckets) { _, bucket -> buffer.writeUtf(bucket) }
|
||||
buffer.writeCollection(activatedHabitatBuckets) { _, bucket -> buffer.writeUtf(bucket) }
|
||||
buffer.writeMapK(
|
||||
size = IntSize.U_SHORT,
|
||||
map = activatedHabitatPools,
|
||||
entryWriter = { (_, value) -> value.encode(buffer) },
|
||||
)
|
||||
buffer.writeMapK(
|
||||
size = IntSize.U_SHORT,
|
||||
map = naturalHabitatPools,
|
||||
map = habitatPools,
|
||||
entryWriter = { (_, value) -> value.encode(buffer) },
|
||||
)
|
||||
habitatSettingsDTO.encode(buffer)
|
||||
|
||||
+1
-5
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.net.messages.server.habitat
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.habitats.dto.HabitatSettingsDTO
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
@@ -30,10 +29,7 @@ class SaveHabitatBlockSettingsPacket(
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): SaveHabitatBlockSettingsPacket {
|
||||
val blockPos = buffer.readBlockPos()
|
||||
val settings = HabitatSettingsDTO()
|
||||
settings.decode(
|
||||
buffer = buffer,
|
||||
buckets = Cobblemon.bestSpawner.config.buckets
|
||||
)
|
||||
settings.decode(buffer)
|
||||
return SaveHabitatBlockSettingsPacket(blockPos, settings)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPool
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
* Simple map adapter for which type of HabitatPool is being deserialized.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since February 13th, 2026
|
||||
*/
|
||||
object HabitatPoolAdapter : JsonDeserializer<HabitatPool<*>> {
|
||||
override fun deserialize(json: JsonElement, t: Type, ctx: JsonDeserializationContext): HabitatPool<*>? {
|
||||
json as JsonObject
|
||||
val type = json.get("type").asString.asIdentifierDefaultingNamespace()
|
||||
val clazz = HabitatPool.types[type] ?: throw IllegalArgumentException("Unknown habitat pool type: $type")
|
||||
return ctx.deserialize(json, clazz)
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
* Spawn bucket adapter that searches the config for a matching named element to deserialize. The serialized form
|
||||
* is simply the name of the bucket.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since June 20th, 2022
|
||||
*/
|
||||
object SpawnBucketAdapter : JsonDeserializer<SpawnBucket>, JsonSerializer<SpawnBucket> {
|
||||
override fun serialize(bucket: SpawnBucket, type: Type, ctx: JsonSerializationContext) = JsonPrimitive(bucket.name)
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): SpawnBucket {
|
||||
return Cobblemon.bestSpawner.config.buckets.find { it.name == json.asString }
|
||||
?: throw IllegalStateException("Spawn referred to invalid spawn bucket: ${json.asString}. Is it missing from the config?")
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnLoader
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.singularToPluralList
|
||||
@@ -76,7 +76,7 @@ object SpawnDetailAdapter : JsonDeserializer<SpawnDetail> {
|
||||
?: throw IllegalStateException("There is no spawning condition registered with the name '${ctxType.defaultCondition}'")
|
||||
val detail = registeredSpawnDetail.deserializeDetail(element, ctx)
|
||||
presets.forEach { it.apply(detail) }
|
||||
if (detail.bucket.name.isBlank()) {
|
||||
if (detail.bucket.isBlank()) {
|
||||
throw IllegalStateException("No bucket was specified for spawn: ${detail.id}")
|
||||
}
|
||||
detail.autoLabel()
|
||||
|
||||
@@ -6,22 +6,29 @@
|
||||
"submerged": 1.0,
|
||||
"surface": 1.0
|
||||
},
|
||||
"buckets": [
|
||||
{
|
||||
"name": "common",
|
||||
"weight": 94.3
|
||||
},
|
||||
{
|
||||
"name": "uncommon",
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"name": "rare",
|
||||
"weight": 0.5
|
||||
},
|
||||
{
|
||||
"name": "ultra-rare",
|
||||
"weight": 0.2
|
||||
}
|
||||
]
|
||||
"worldBuckets": {
|
||||
"common": 94.0,
|
||||
"uncommon": 5.0,
|
||||
"rare": 0.5,
|
||||
"ultra-rare": 0.2,
|
||||
"boss": 0.3
|
||||
},
|
||||
"fishingBuckets": {
|
||||
"common": 83.25,
|
||||
"uncommon": 11.25,
|
||||
"rare": 4.125,
|
||||
"ultra-rare": 1.375
|
||||
},
|
||||
"pokeSnackBuckets": {
|
||||
"common": 83.25,
|
||||
"uncommon": 11.25,
|
||||
"rare": 4.125,
|
||||
"ultra-rare": 1.375
|
||||
},
|
||||
"activatedHabitatBuckets": {
|
||||
"common": 83.25,
|
||||
"uncommon": 11.25,
|
||||
"rare": 4.125,
|
||||
"ultra-rare": 1.375
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user