Merge remote-tracking branch 'origin/spawn-rules' into npcs

# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonProperties.kt
This commit is contained in:
Paul
2023-10-08 11:10:16 +10:00
47 changed files with 820 additions and 58 deletions
@@ -0,0 +1,71 @@
/*
* 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.molang
import com.bedrockk.molang.runtime.MoLangEnvironment
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import net.minecraft.block.Block
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.Identifier
import net.minecraft.world.World
import net.minecraft.world.biome.Biome
import net.minecraft.world.dimension.DimensionType
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
*
* @author Hiroku
* @since October 2nd, 2023
*/
object MoLangFunctions {
val generalFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val dimensionTypeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val blockFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
fun RegistryEntry<Biome>.asBiomeMoLangValue() = asMoLangValue(RegistryKeys.BIOME).addFunctions(biomeFunctions)
fun RegistryEntry<World>.asWorldMoLangValue() = asMoLangValue(RegistryKeys.WORLD).addFunctions(worldFunctions)
fun RegistryEntry<Block>.asBlockMoLangValue() = asMoLangValue(RegistryKeys.BLOCK).addFunctions(blockFunctions)
fun RegistryEntry<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(RegistryKeys.DIMENSION_TYPE).addFunctions(dimensionTypeFunctions)
fun <T> RegistryEntry<T>.asMoLangValue(key: RegistryKey<Registry<T>>): ObjectValue<RegistryEntry<T>> {
val value = ObjectValue(
obj = this,
stringify = { it.key.get().value.toString() }
)
value.functions.put("is_in") {
val tag = TagKey.of(key, Identifier(it.getString(0).replace("#", "")))
return@put DoubleValue(if (value.obj.isIn(tag)) 1.0 else 0.0)
}
value.functions.put("is_of") {
val identifier = Identifier(it.getString(0))
return@put DoubleValue(if (value.obj.matchesId(identifier)) 1.0 else 0.0)
}
return value
}
fun QueryStruct.addStandardFunctions(): QueryStruct {
functions.putAll(generalFunctions)
return this
}
fun <T : QueryStruct> T.addFunctions(functions: Map<String, java.util.function.Function<MoParams, Any>>): T {
this.functions.putAll(functions)
return this
}
fun MoLangEnvironment.getQueryStruct(name: String = "query") = structs.getOrPut(name) { QueryStruct(hashMapOf()).addStandardFunctions() } as QueryStruct
}
@@ -0,0 +1,28 @@
/*
* 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.molang
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.MoValue
/**
* A [MoValue] that just wraps around some other object value. It's for good and valid reasons.
*
* @author Hiroku
* @since October 2nd, 2023
*/
class ObjectValue<T>(
var obj: T,
val stringify: (T) -> String = { it.toString() },
val doublify: (T) -> Double = { throw NotImplementedError("Doublify not implemented for this object value, are you doing weird molang") }
) : QueryStruct(hashMapOf()) {
override fun value() = this
override fun asDouble() = doublify(obj)
override fun asString() = stringify(obj)
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.pokemon
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.pokeball.PokeBalls
@@ -631,6 +633,13 @@ open class PokemonProperties {
return pieces.joinToString(separator)
}
fun asStruct(): VariableStruct {
val struct = VariableStruct()
species?.let { struct.setDirectly("species", StringValue(it)) }
// todo a bunch more
return struct
}
fun updateAspects() {
val aspects = mutableSetOf<String>()
AspectProvider.providers.forEach { aspects.addAll(it.provide(this)) }
@@ -0,0 +1,84 @@
/*
* 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
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
import com.cobblemon.mod.common.api.spawning.rules.component.FilterRuleComponent
import com.cobblemon.mod.common.api.spawning.rules.component.LocationRuleCalculator
import com.cobblemon.mod.common.api.spawning.rules.component.SpawnRuleComponent
import com.cobblemon.mod.common.api.spawning.rules.component.WeightTweakRuleComponent
import com.cobblemon.mod.common.api.spawning.rules.selector.ConditionalSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.ExpressionSpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.ExpressionSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.SpawnDetailSelectorAdapter
import com.cobblemon.mod.common.util.adapters.SpawnRuleComponentAdapter
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
import com.cobblemon.mod.common.util.adapters.SpawningContextSelectorAdapter
import com.cobblemon.mod.common.util.adapters.TextAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* All of the [SpawnRule]s that will be applied as global [SpawningInfluence]s.
*
* @author Hiroku
* @since September 30th, 2023
*/
object CobblemonSpawnRules : JsonDataRegistry<SpawnRule> {
override val gson = GsonBuilder()
.registerTypeAdapter(SpawnRuleComponent::class.java, SpawnRuleComponentAdapter)
.registerTypeAdapter(SpawnDetailSelector::class.java, SpawnDetailSelectorAdapter)
.registerTypeAdapter(SpawningContextSelector::class.java, SpawningContextSelectorAdapter)
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(Text::class.java, TextAdapter)
.create()
override val typeToken = TypeToken.get(SpawnRule::class.java)
override val resourcePath = "spawn_rules"
init {
SpawnRuleComponent.register<WeightTweakRuleComponent>("weight")
SpawnRuleComponent.register<FilterRuleComponent>("filter")
SpawnRuleComponent.register<LocationRuleCalculator>("location")
SpawnDetailSelector.register<ExpressionSpawnDetailSelector>("expression")
SpawningContextSelector.register<ExpressionSpawningContextSelector>("expression")
SpawningContextSelector.register<ConditionalSpawningContextSelector>("conditional")
}
val rules = mutableMapOf<Identifier, SpawnRule>()
override fun reload(data: Map<Identifier, SpawnRule>) {
rules.clear()
rules.putAll(data)
data.forEach { (id, value) -> value.id = id }
observable.emit(this)
}
override val id: Identifier = cobblemonResource("spawn_rules")
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable = SimpleObservable<CobblemonSpawnRules>()
override fun sync(player: ServerPlayerEntity) {}
}
@@ -55,5 +55,5 @@ interface AppendageCondition {
fun getAppendages(spawningCondition: SpawningCondition<*>) = appendages.filter { it.spawningConditionFits(spawningCondition) }.map { it.clazz }
}
fun fits(ctx: SpawningContext, detail: SpawnDetail): Boolean
fun fits(ctx: SpawningContext): Boolean
}
@@ -26,8 +26,8 @@ abstract class AreaTypeSpawningCondition<T : AreaSpawningContext> : SpawningCond
var maxHeight: Int? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
if (!super.fits(ctx)) {
return false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
@@ -30,11 +30,11 @@ class CompositeSpawningCondition {
var conditions = mutableListOf<SpawningCondition<*>>()
var anticonditions = mutableListOf<SpawningCondition<*>>()
fun satisfiedBy(ctx: SpawningContext, detail: SpawnDetail): Boolean {
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx, detail) }) {
fun satisfiedBy(ctx: SpawningContext): Boolean {
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx) }) {
false
} else {
!(anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx, detail) })
!(anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx) })
}
}
}
@@ -24,8 +24,8 @@ import net.minecraft.block.Block
abstract class GroundedTypeSpawningCondition<T : GroundedSpawningContext> : AreaTypeSpawningCondition<T>() {
var neededBaseBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.MoonPhaseRange
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.condition.ListCheckMode.ALL
import com.cobblemon.mod.common.api.spawning.condition.ListCheckMode.ANY
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.Merger
@@ -55,8 +53,6 @@ abstract class SpawningCondition<T : SpawningContext> {
var isRaining: Boolean? = null
var isThundering: Boolean? = null
var timeRange: TimeRange? = null
var labels: MutableList<String>? = null
var labelMode = ANY
var structures: MutableList<Either<Identifier, TagKey<Structure>>>? = null
@Transient
@@ -65,15 +61,15 @@ abstract class SpawningCondition<T : SpawningContext> {
abstract fun contextClass(): Class<out T>
fun contextMatches(ctx: SpawningContext) = contextClass().isAssignableFrom(ctx::class.java)
fun isSatisfiedBy(ctx: SpawningContext, detail: SpawnDetail): Boolean {
fun isSatisfiedBy(ctx: SpawningContext): Boolean {
return if (contextMatches(ctx)) {
fits(ctx as T, detail)
fits(ctx as T)
} else {
false
}
}
protected open fun fits(ctx: T, detail: SpawnDetail): Boolean {
protected open fun fits(ctx: T): Boolean {
if (ctx.position.x < minX.orMin() || ctx.position.x > maxX.orMax()) {
return false
} else if (ctx.position.y < minY.orMin() || ctx.position.y > maxY.orMax()) {
@@ -96,14 +92,7 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
} else if (isThundering != null && ctx.world.isThundering != isThundering!!) {
return false
} else if (labels != null && labels!!.isNotEmpty() &&
(
(labelMode == ANY && labels!!.none { it in detail.labels }) ||
(labelMode == ALL && labels!!.any { it !in detail.labels })
)
) {
return false
} else if (appendages.any { !it.fits(ctx, detail) }) {
} else if (appendages.any { !it.fits(ctx) }) {
return false
} else if (structures != null && structures!!.isNotEmpty() &&
structures!!.let { structures ->
@@ -123,7 +112,6 @@ abstract class SpawningCondition<T : SpawningContext> {
open fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
dimensions = merger.merge(dimensions, other.dimensions)?.toMutableList()
biomes = merger.merge(biomes, other.biomes)?.toMutableSet()
labels = merger.merge(labels, other.labels)?.toMutableList()
moonPhase = merger.mergeSingle(moonPhase, other.moonPhase)
canSeeSky = merger.mergeSingle(canSeeSky, other.canSeeSky)
minX = merger.mergeSingle(minX, other.minX)
@@ -27,8 +27,8 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawningContext> : Ar
var fluidIsSource: Boolean? = null
var fluid: RegistryLikeCondition<Fluid>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
@@ -26,8 +26,8 @@ abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawningContext> : AreaTy
var maxDepth: Int? = null
var fluid: RegistryLikeCondition<Fluid>? = null
override fun fits(ctx: T, detail: SpawnDetail): Boolean {
return if (!super.fits(ctx, detail)) {
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minDepth != null && ctx.depth < minDepth!!) {
false
@@ -49,7 +49,8 @@ interface AreaContextResolver {
pos.set(x, y, z)
val vec = pos.toVec3d()
if (slice.nearbyEntityPositions.none { it.isInRange(vec, config.minimumDistanceBetweenEntities) && it != slice.cause.entity }) {
val fittedContextCalculator = contextCalculators.firstOrNull { it.fits(input) }
val fittedContextCalculator = contextCalculators
.firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
if (fittedContextCalculator != null) {
val context = fittedContextCalculator.calculate(input)
if (context != null) {
@@ -8,6 +8,9 @@
package com.cobblemon.mod.common.api.spawning.context
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
@@ -80,6 +83,9 @@ abstract class SpawningContext {
val biomeName: Identifier
get() = this.biomeRegistry.getId(biome)!!
private val struct = VariableStruct()
private var structCompiled = false
class StructureChunkCache {
val missingTags = mutableSetOf<TagKey<Structure>>()
val foundTags = mutableSetOf<TagKey<Structure>>()
@@ -159,4 +165,21 @@ abstract class SpawningContext {
}
return weight
}
fun getOrSetupStruct(): VariableStruct {
if (structCompiled) {
return struct
}
struct.setDirectly("light", DoubleValue(light.toDouble()))
struct.setDirectly("x", DoubleValue(position.x.toDouble()))
struct.setDirectly("y", DoubleValue(position.y.toDouble()))
struct.setDirectly("z", DoubleValue(position.z.toDouble()))
struct.setDirectly("moon_phase", DoubleValue(moonPhase.toDouble()))
struct.setDirectly("world", ObjectValue(world.registryManager.get(RegistryKeys.WORLD).getEntry(world)))
struct.setDirectly("biome", ObjectValue(biomeRegistry.getEntry(biome)))
structCompiled = true
return struct
}
}
@@ -50,6 +50,7 @@ interface FlooredSpawningContextCalculator<T : FlooredSpawningContext> : AreaSpa
*/
object GroundedSpawningContextCalculator : FlooredSpawningContextCalculator<GroundedSpawningContext> {
// TODO expand base condition
override val name = "grounded"
override val baseCondition: (BlockState) -> Boolean = isSolidCondition
override val surroundingCondition: (BlockState) -> Boolean = isAirCondition
@@ -76,6 +77,7 @@ object GroundedSpawningContextCalculator : FlooredSpawningContextCalculator<Grou
* @since February 7th, 2022
*/
object SeafloorSpawningContextCalculator : FlooredSpawningContextCalculator<SeafloorSpawningContext> {
override val name = "seafloor"
override val baseCondition: (BlockState) -> Boolean = isSolidCondition
override val surroundingCondition: (BlockState) -> Boolean = isWaterCondition
@@ -102,6 +104,7 @@ object SeafloorSpawningContextCalculator : FlooredSpawningContextCalculator<Seaf
* @since February 7th, 2022
*/
object LavafloorSpawningContextCalculator : FlooredSpawningContextCalculator<LavafloorSpawningContext> {
override val name = "lavafloor"
override val baseCondition: (BlockState) -> Boolean = isSolidCondition
override val surroundingCondition: (BlockState) -> Boolean = isLavaCondition
@@ -129,6 +132,7 @@ object LavafloorSpawningContextCalculator : FlooredSpawningContextCalculator<Lav
* @since December 15th, 2022
*/
object SurfaceSpawningContextCalculator : FlooredSpawningContextCalculator<SurfaceSpawningContext> {
override val name = "surface"
override val baseCondition: (BlockState) -> Boolean = { !it.fluidState.isEmpty }
override val surroundingCondition: (BlockState) -> Boolean = isAirCondition
@@ -48,6 +48,8 @@ interface SpawningContextCalculator<I : SpawningContextInput, O : SpawningContex
}
}
val name: String
/** Tries creating a [SpawningContext] from the given input. Returning null should be a last resort. */
fun calculate(input: I): O?
}
@@ -23,6 +23,7 @@ import net.minecraft.util.math.MathHelper.ceil
* @since February 7th, 2022
*/
object SubmergedSpawningContextCalculator : AreaSpawningContextCalculator<SubmergedSpawningContext> {
override val name = "submerged"
val fluidConditions = mutableListOf(
isWaterCondition,
isLavaCondition
@@ -8,8 +8,8 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -50,6 +50,9 @@ class PokemonSpawnAction(
entity.pokemon.swapHeldItem(heldItem)
}
entity.drops = detail.drops
// Useful debug code in situations where you want to find spawns
// val fireworkRocketEntity = FireworkRocketEntity(ctx.world, ctx.position.x.toDouble(), ctx.position.y.toDouble() + 2, ctx.position.z.toDouble(), ItemStack(Items.FIREWORK_ROCKET))
// ctx.world.spawnEntity(fireworkRocketEntity)
return entity
}
}
@@ -69,7 +69,7 @@ class PokemonSpawnDetail : SpawnDetail() {
}
override fun autoLabel() {
super.autoLabel()
val pokemonStruct = pokemon.asStruct()
if (pokemon.species != null) {
val species = PokemonSpecies.getByIdentifier(pokemon.species!!.asIdentifierDefaultingNamespace())
if (species != null) {
@@ -87,6 +87,9 @@ class PokemonSpawnDetail : SpawnDetail() {
}
}
}
struct.setDirectly("pokemon", pokemonStruct)
super.autoLabel()
}
fun getDerivedLevelRange() = levelRange.let { levelRange ->
@@ -8,6 +8,10 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.bedrockk.molang.runtime.struct.ArrayStruct
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.ModDependant
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.condition.CompositeSpawningCondition
@@ -50,20 +54,32 @@ abstract class SpawnDetail : ModDependant {
var labels = mutableListOf<String>()
val struct: VariableStruct = VariableStruct()
override var neededInstalledMods = listOf<String>()
override var neededUninstalledMods = listOf<String>()
open fun autoLabel() {}
open fun autoLabel() {
struct.setDirectly("weight", DoubleValue(weight.toDouble()))
struct.setDirectly("percentage", DoubleValue(percentage.toDouble()))
struct.setDirectly("id", StringValue(id))
struct.setDirectly("bucket", StringValue(bucket.name))
struct.setDirectly("width", DoubleValue(width.toDouble()))
struct.setDirectly("height", DoubleValue(height.toDouble()))
struct.setDirectly("context", StringValue(context.name))
struct.setDirectly("labels", ArrayStruct(labels.mapIndexed { index, s -> "$index" to StringValue(s) }.toMap()))
}
open fun getName() = displayName?.asTranslated() ?: id.text()
open fun isSatisfiedBy(ctx: SpawningContext): Boolean {
if (!ctx.preFilter(this)) {
return false
} else if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx, this) }) {
} else if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx) }) {
return false
} else if (anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx, this) }) {
} else if (anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx) }) {
return false
} else if (compositeCondition?.satisfiedBy(ctx, this) == false) {
} else if (compositeCondition?.satisfiedBy(ctx) == false) {
return false
} else if (!ctx.postFilter(this)) {
return false
@@ -11,9 +11,12 @@ 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.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import net.minecraft.entity.Entity
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* An influence over spawning that can affect various parts of the spawning process. These can be attached to
@@ -35,4 +38,6 @@ interface SpawningInfluence {
fun affectSpawn(entity: Entity) {}
/** Applies some influence over the weight of spawn buckets. */
fun affectBucketWeight(bucket: SpawnBucket, weight: Float): Float = weight
/** Filters positions that could be usable for a spawning context calculator */
fun isAllowedPosition(world: ServerWorld, pos: BlockPos, contextCalculator: SpawningContextCalculator<*, *>): Boolean = true
}
@@ -31,8 +31,8 @@ class WeightMultiplier : SpawningInfluence {
var multiplier = 1F
override fun affectWeight(detail: SpawnDetail, ctx: SpawningContext, weight: Float): Float {
val meetsConditions = (conditions.isEmpty() || conditions.any { it.isSatisfiedBy(ctx, detail) })
&& (anticonditions.isEmpty() || anticonditions.none { it.isSatisfiedBy(ctx, detail) })
val meetsConditions = (conditions.isEmpty() || conditions.any { it.isSatisfiedBy(ctx) })
&& (anticonditions.isEmpty() || anticonditions.none { it.isSatisfiedBy(ctx) })
return if (meetsConditions) multiplier * weight else weight
}
}
@@ -0,0 +1,29 @@
/*
* 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.rules
import com.cobblemon.mod.common.api.spawning.rules.component.SpawnRuleComponent
import com.cobblemon.mod.common.api.text.text
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* A bundling of [SpawnRuleComponent]s.
*
* @author Hiroku
* @since September 30th, 2023
*/
class SpawnRule {
lateinit var id: Identifier
val displayName: Text = "Spawn Rule".text()
// val pool: String? = null Kinda difficult to see how this would get used in practice.
// var enabled = true One would expect there to be some kind of persistence if this could be enabled and disabled willy nilly.
val components: MutableList<SpawnRuleComponent> = mutableListOf()
}
@@ -0,0 +1,46 @@
/*
* 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.rules.component
import com.bedrockk.molang.Expression
import com.bedrockk.molang.ast.BooleanExpression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.rules.selector.AllSpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.AllSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.resolveBoolean
/**
* A rule component that targets specific spawns and contexts and judges whether it
* is allowed to spawn. This runs prior to the regular SpawnDetail conditions.
*
* @author Hiroku
* @since October 1st, 2023
*/
class FilterRuleComponent : SpawnRuleComponent {
@Transient
val runtime = MoLangRuntime()
val spawnSelector: SpawnDetailSelector = AllSpawnDetailSelector
val contextSelector: SpawningContextSelector = AllSpawningContextSelector
val allow: Expression = BooleanExpression(true)
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
return if (spawnSelector.selects(detail) && contextSelector.selects(ctx)) {
runtime.environment.setSimpleVariable("spawn", detail.struct)
runtime.environment.setSimpleVariable("context", ctx.getOrSetupStruct())
runtime.resolveBoolean(allow)
} else {
true
}
}
}
@@ -0,0 +1,83 @@
/*
* 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.rules.component
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asDimensionTypeMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asWorldMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.SpawningContextCalculator
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import net.minecraft.world.dimension.DimensionType
/**
* An early rule that filters possible locations that would go into a [SpawningContext].
*
* @author Hiroku
* @since October 2nd, 2023
*/
class LocationRuleCalculator : SpawnRuleComponent {
@Transient
val runtime = MoLangRuntime().also { it.environment.getQueryStruct() }
@Transient
private val reusableX = DoubleValue(0.0)
@Transient
private val reusableY = DoubleValue(0.0)
@Transient
private val reusableZ = DoubleValue(0.0)
@Transient
private val reusableContext = StringValue("")
@Transient
private lateinit var reusableWorldValue: ObjectValue<RegistryEntry<World>>
@Transient
private lateinit var reusableDimensionTypeValue: ObjectValue<RegistryEntry<DimensionType>>
val allow = "true".asExpression()
override fun isAllowedPosition(
world: ServerWorld,
pos: BlockPos,
contextCalculator: SpawningContextCalculator<*, *>
): Boolean {
reusableX.value = pos.x.toDouble()
reusableY.value = pos.y.toDouble()
reusableZ.value = pos.z.toDouble()
reusableContext.value = contextCalculator.name
if (!this::reusableWorldValue.isInitialized) {
reusableWorldValue = world.registryManager.get(RegistryKeys.WORLD).getEntry(world).asWorldMoLangValue()
} else {
reusableWorldValue.obj = world.registryManager.get(RegistryKeys.WORLD).getEntry(world)
}
if (!this::reusableDimensionTypeValue.isInitialized) {
reusableDimensionTypeValue = world.dimensionEntry.asDimensionTypeMoLangValue()
} else {
reusableDimensionTypeValue.obj = world.dimensionEntry
}
runtime.environment.setSimpleVariable("x", reusableX)
runtime.environment.setSimpleVariable("y", reusableY)
runtime.environment.setSimpleVariable("z", reusableZ)
runtime.environment.setSimpleVariable("context", reusableContext)
runtime.environment.setSimpleVariable("world", reusableWorldValue)
runtime.environment.setSimpleVariable("dimension_type", reusableDimensionTypeValue)
return runtime.resolveBoolean(allow)
}
}
@@ -0,0 +1,27 @@
/*
* 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.rules.component
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
/**
* Responsible for changing a thing about spawns. A spawn rule may have many components like this.
*
* @author Hiroku
* @since September 30th, 2023
*/
interface SpawnRuleComponent : SpawningInfluence {
companion object {
val types = mutableMapOf<String, Class<out SpawnRuleComponent>>()
inline fun <reified T : SpawnRuleComponent> register(type: String) {
types[type] = T::class.java
}
}
}
@@ -0,0 +1,46 @@
/*
* 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.rules.component
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.rules.selector.AllSpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.AllSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveFloat
/**
* A rule component that alters the weight of spawns at specific contexts.
*
* @author Hiroku
* @since October 1st, 2023
*/
class WeightTweakRuleComponent : SpawnRuleComponent {
val spawnSelector: SpawnDetailSelector = AllSpawnDetailSelector
val contextSelector: SpawningContextSelector = AllSpawningContextSelector
val weight: Expression = "v.weight".asExpression()
@Transient
val runtime = MoLangRuntime()
override fun affectWeight(detail: SpawnDetail, ctx: SpawningContext, weight: Float): Float {
return if (spawnSelector.selects(detail) && contextSelector.selects(ctx)) {
runtime.environment.setSimpleVariable("spawn", detail.struct)
runtime.environment.setSimpleVariable("weight", DoubleValue(weight.toDouble()))
runtime.resolveFloat(this.weight)
} else {
weight
}
}
}
@@ -0,0 +1,15 @@
/*
* 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.rules.selector
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
object AllSpawnDetailSelector : SpawnDetailSelector {
override fun selects(spawnDetail: SpawnDetail) = true
}
@@ -0,0 +1,15 @@
/*
* 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.rules.selector
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
object AllSpawningContextSelector : SpawningContextSelector {
override fun selects(ctx: SpawningContext) = true
}
@@ -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.rules.selector
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
class ConditionalSpawningContextSelector : SpawningContextSelector {
var conditions = mutableListOf<SpawningCondition<*>>()
var anticonditions = mutableListOf<SpawningCondition<*>>()
override fun selects(ctx: SpawningContext): Boolean {
return if (conditions.isNotEmpty() && conditions.none { it.isSatisfiedBy(ctx) }) {
false
} else {
!(anticonditions.isNotEmpty() && anticonditions.any { it.isSatisfiedBy(ctx) })
}
}
}
@@ -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.rules.selector
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
class ExpressionSpawnDetailSelector : SpawnDetailSelector {
@Transient
val runtime = MoLangRuntime()
var expression = "true".asExpression()
override fun selects(spawnDetail: SpawnDetail): Boolean {
runtime.environment.setSimpleVariable("spawn", spawnDetail.struct)
return runtime.resolveBoolean(expression)
}
}
@@ -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.rules.selector
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
class ExpressionSpawningContextSelector : SpawningContextSelector {
@Transient
val runtime = MoLangRuntime()
var expression = "true".asExpression()
override fun selects(ctx: SpawningContext): Boolean {
runtime.environment.setSimpleVariable("context", ctx.getOrSetupStruct())
return runtime.resolveBoolean(expression)
}
}
@@ -0,0 +1,22 @@
/*
* 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.rules.selector
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
interface SpawnDetailSelector {
companion object {
val types = mutableMapOf<String, Class<out SpawnDetailSelector>>()
inline fun <reified T : SpawnDetailSelector> register(type: String) {
types[type] = T::class.java
}
}
fun selects(spawnDetail: SpawnDetail): Boolean
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.rules.selector
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
interface SpawningContextSelector {
companion object {
val types = mutableMapOf<String, Class<out SpawningContextSelector>>()
inline fun <reified T : SpawningContextSelector> register(type: String) {
types[type] = T::class.java
}
}
fun selects(ctx: SpawningContext): Boolean
}
@@ -26,8 +26,13 @@ import kotlin.random.Random
* [FlatContextWeightedSelector] because here a spawning context type only appearing once
* will not make it any less likely to be selected.
*
* A spawn detail's weight when selecting between all possible spawn details will be the
* highest of its contextual weights. In other words, if a spawn is possible at both A and
* B contexts but is weighed more highly at A due to a weight multiplier, it will use the
* weight of the spawn at A when choosing which spawn will run.
*
* When choosing which context a specific spawn detail will spawn at once that spawn detail
* has been chosen, it does a flat random across the possible contexts.
* has been chosen, it does a context-weighted random selection across those contexts.
*
* At a glance:
* - Spawn detail selection is flat across context quantity
@@ -37,10 +42,22 @@ import kotlin.random.Random
* @since July 10th, 2022
*/
open class FlatSelector : SpawningSelector {
open fun getWeight(contextType: RegisteredSpawningContext<*>) = contextType.getWeight()
protected class SelectingSpawnInformation {
val spawningContexts = mutableMapOf<SpawningContext, Float>()
var highestWeight = 0F
fun add(spawningContext: SpawningContext, contextWeight: Float) {
spawningContexts[spawningContext] = contextWeight
if (contextWeight > highestWeight) {
highestWeight = contextWeight
}
}
fun chooseContext() = spawningContexts.entries.weightedSelection { it.value }!!.key
}
protected class ContextSelectionData(
val spawnsToContexts: MutableMap<SpawnDetail, MutableList<SpawningContext>>,
val spawnsToContexts: MutableMap<SpawnDetail, SelectingSpawnInformation>,
var percentSum: Float
) {
val size: Int
@@ -51,7 +68,7 @@ open class FlatSelector : SpawningSelector {
spawner: Spawner,
contexts: List<SpawningContext>
): ContextSelectionData {
val spawnsToContexts: MutableMap<SpawnDetail, MutableList<SpawningContext>> = mutableMapOf()
val spawnsToContexts: MutableMap<SpawnDetail, SelectingSpawnInformation> = mutableMapOf()
var percentSum = 0F
contexts.forEach { ctx ->
@@ -65,9 +82,9 @@ open class FlatSelector : SpawningSelector {
val selectingSpawnInformation = spawnsToContexts.getOrPut(
it,
::mutableListOf
::SelectingSpawnInformation
)
selectingSpawnInformation.add(ctx)
selectingSpawnInformation.add(ctx, ctx.getWeight(it))
}
}
@@ -87,7 +104,6 @@ open class FlatSelector : SpawningSelector {
val spawnsToContexts = selectionData.spawnsToContexts
var percentSum = selectionData.percentSum
// First pass is doing percentage checks.
if (percentSum > 0) {
@@ -111,15 +127,14 @@ open class FlatSelector : SpawningSelector {
if (spawnDetail.percentage > 0) {
percentSum += spawnDetail.percentage
if (percentSum >= selectedPercentage) {
return info.random() to spawnDetail
return info.chooseContext() to spawnDetail
}
}
}
}
val selectedSpawn = spawnsToContexts.entries.toList().weightedSelection { it.key.weight }!!
return selectedSpawn.value.random() to selectedSpawn.key
val selectedSpawn = spawnsToContexts.entries.toList().weightedSelection { it.value.highestWeight }!!
return selectedSpawn.value.chooseContext() to selectedSpawn.key
}
override fun getTotalWeights(
@@ -129,13 +144,13 @@ open class FlatSelector : SpawningSelector {
val selectionData = getSelectionData(spawner, contexts)
if (selectionData.size == 0) {
return mapOf()
return emptyMap()
}
val totalWeights = mutableMapOf<SpawnDetail, Float>()
for (spawnDetail in selectionData.spawnsToContexts.keys) {
totalWeights[spawnDetail] = spawnDetail.weight
for ((spawnDetail, info) in selectionData.spawnsToContexts.entries) {
totalWeights[spawnDetail] = info.highestWeight
}
return totalWeights
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* A spawner that works within a fixed area, and ticks on its own. The location
@@ -9,10 +9,12 @@
package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
import net.minecraft.server.network.ServerPlayerEntity
/**
@@ -31,6 +33,7 @@ object PlayerSpawnerFactory {
val influences = influenceBuilders.mapNotNull { it(player) }
return PlayerSpawner(player, spawns, spawnerManager).also {
it.influences.addAll(influences)
it.influences.addAll(CobblemonSpawnRules.rules.values.flatMap(SpawnRule::components))
}
}
}
@@ -132,7 +132,7 @@ data class BedrockAnimation(
val boneTimelines: Map<String, BedrockBoneTimeline>
) {
companion object {
val functionMappings = mutableMapOf<String, java.util.function.Function<MoParams, Any>>()
val functionMappings = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val sharedRuntime = MoLangRuntime().also {
it.environment.structs["query"] = it.environment.structs["variable"]
it.environment.structs["script"] = QueryStruct(functionMappings)
@@ -24,12 +24,12 @@ import com.cobblemon.mod.common.api.pokemon.feature.GlobalSpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatureAssignments
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
@@ -62,6 +62,7 @@ object CobblemonDataProvider : DataProvider {
this.register(PokeBalls)
this.register(PropertiesCompletionProvider)
this.register(SpawnDetailPresets)
this.register(CobblemonSpawnRules)
this.register(CobblemonMechanics)
this.register(BagItems)
this.register(NPCClasses)
@@ -0,0 +1,32 @@
/*
* 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.spawning.rules.selector.ExpressionSpawnDetailSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawnDetailSelector
import com.cobblemon.mod.common.util.asExpression
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
object SpawnDetailSelectorAdapter : JsonDeserializer<SpawnDetailSelector> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): SpawnDetailSelector {
return if (json.isJsonPrimitive) {
val expression = json.asString.asExpression()
ExpressionSpawnDetailSelector().also { it.expression = expression }
} else {
json as JsonObject
val type = json.get("type").asString
val clazz = SpawnDetailSelector.types[type] ?: throw IllegalArgumentException("Unknown spawn detail selector type: $type")
ctx.deserialize(json, clazz)
}
}
}
@@ -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.util.adapters
import com.cobblemon.mod.common.api.spawning.rules.component.SpawnRuleComponent
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
object SpawnRuleComponentAdapter : JsonDeserializer<SpawnRuleComponent> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): SpawnRuleComponent {
json as JsonObject
val type = json.get("type").asString
val clazz = SpawnRuleComponent.types[type] ?: throw IllegalArgumentException("Unknown spawn rule component type: $type")
return ctx.deserialize(json, clazz)
}
}
@@ -0,0 +1,33 @@
/*
* 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.spawning.rules.selector.ConditionalSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.ExpressionSpawningContextSelector
import com.cobblemon.mod.common.api.spawning.rules.selector.SpawningContextSelector
import com.cobblemon.mod.common.util.asExpression
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
object SpawningContextSelectorAdapter : JsonDeserializer<SpawningContextSelector> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): SpawningContextSelector {
return if (json.isJsonPrimitive) {
val expression = json.asString.asExpression()
ExpressionSpawningContextSelector().also { it.expression = expression }
} else {
json as JsonObject
val type = json.get("type")?.asString ?: return ctx.deserialize(json, ConditionalSpawningContextSelector::class.java)
val clazz = SpawningContextSelector.types[type] ?: throw IllegalArgumentException("Unknown spawn detail selector type: $type")
ctx.deserialize(json, clazz)
}
}
}
@@ -0,0 +1,20 @@
/*
* 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.text.text
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import java.lang.reflect.Type
import net.minecraft.text.Text
object TextAdapter : JsonDeserializer<Text> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext) = json.asString.text()
}
@@ -0,0 +1,7 @@
{
"displayName": "Pikachu Daylight Multiplier",
"components": [
]
}
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>local.com.bedrockk</groupId>
<artifactId>molang</artifactId>
<version>1.1.8</version>
</project>
+3 -3
View File
@@ -3,10 +3,10 @@
<groupId>local.com.bedrockk</groupId>
<artifactId>molang</artifactId>
<versioning>
<release>1.1.5</release>
<release>1.1.8</release>
<versions>
<version>1.1.5</version>
<version>1.1.8</version>
</versions>
<lastUpdated>20221939127999</lastUpdated>
<lastUpdated>20221949138999</lastUpdated>
</versioning>
</metadata>
+1 -1
View File
@@ -25,7 +25,7 @@ versions:
# common
graal: 22.3.0
molang: 1.1.5
molang: 1.1.8
# test
junit: 5.9.0