mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-29 21:38:53 -04:00
Compare commits
31
Commits
v1.1.0
...
form-registry
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0ffa4c565 | ||
|
|
6bf09dbd2a | ||
|
|
010f9b16f9 | ||
|
|
470f0d37a4 | ||
|
|
f7bd3af13b | ||
|
|
f4f7c0f19a | ||
|
|
7ff0a66aae | ||
|
|
0487d431e1 | ||
|
|
5429f70a26 | ||
|
|
aa8d0d80f1 | ||
|
|
9547be150b | ||
|
|
346e3a3047 | ||
|
|
594bf59496 | ||
|
|
7f6fa2f009 | ||
|
|
b7720248ad | ||
|
|
60593c7cdf | ||
|
|
61e545ace3 | ||
|
|
045398d204 | ||
|
|
c4e318cb1c | ||
|
|
048bae7585 | ||
|
|
fee858e5dc | ||
|
|
f938e99036 | ||
|
|
02ecc87608 | ||
|
|
5a0d46410a | ||
|
|
e516b6dea2 | ||
|
|
3e3a2a7ec0 | ||
|
|
bd1f7c59c3 | ||
|
|
1fdf928b7b | ||
|
|
ee863a4e04 | ||
|
|
24f6915cc9 | ||
|
|
b4207b11ab |
No files matched your search
+4
-1
@@ -28,4 +28,7 @@ forge*changelog.txt
|
||||
/logs/
|
||||
.vscode/launch.json
|
||||
.vscode
|
||||
common/src/main/resources/assets/cobblemon/showdown
|
||||
common/src/main/resources/assets/cobblemon/showdown
|
||||
|
||||
# Generated files
|
||||
*/generated/resources/*
|
||||
+22
-1
@@ -1,6 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## 1.1.0 - The Swim and Sleep Update
|
||||
## 1.1.2
|
||||
### Additions
|
||||
- Reworked customisation of models, textures, and animations to make fakemon very easy to create.
|
||||
- Added 'nature' and 'pokeball' options to commands such as /spawnpokemon and /givepokemon.
|
||||
- Added support for datapack spawn files and spawn file presets.
|
||||
|
||||
### Changes
|
||||
- Changed the healer advancements to make the healing machine's mechanics more obvious.
|
||||
|
||||
### Fixes
|
||||
- Fixed an incompatibility with [Porting Lib](https://github.com/Fabricators-of-Create/Porting-Lib) used by Fabric ports of mods such as Create or Twilight Forest.
|
||||
- Fixed HP and max HP values in the battle GUI not being correct.
|
||||
- Fixed some animations on snake-type Pokémon being mega slow.
|
||||
- Fixed a typo in the English name for Calcium. Calcuim.
|
||||
- Fixed Pokémon gradually becoming rarer around you if you move long distances.
|
||||
|
||||
## 1.1.1 (November 27th, 2022)
|
||||
|
||||
### Fixes
|
||||
- Fixed a critical issue with servers where Pokémon data didn't properly synchronize and so you couldn't see any.
|
||||
|
||||
## 1.1.0 - The Swim and Sleep Update (November 27th, 2022)
|
||||
#### "Ideally not at the same time."
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ architectury {
|
||||
|
||||
repositories {
|
||||
maven(url = "https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/")
|
||||
maven(url = "https://maven.impactdev.net/repository/development/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -38,6 +39,8 @@ dependencies {
|
||||
// modCompileOnly 'com.caoccao.javet:javet:1.0.6' // Linux or Windows
|
||||
// modCompileOnly 'com.caoccao.javet:javet-macos:1.0.6' // Mac OS (x86_64 Only)
|
||||
// modCompileOnly group: 'commons-io', name: 'commons-io', version: '2.6'
|
||||
|
||||
implementation("net.impactdev:json:1.0.0")
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
||||
@@ -35,9 +35,10 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.StandardExperienceCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.EnumSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.GlobalSpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Generation8EvCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.StatProvider
|
||||
@@ -79,21 +80,12 @@ import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacke
|
||||
import com.cobblemon.mod.common.net.serverhandling.ServerPacketRegistrar
|
||||
import com.cobblemon.mod.common.permission.LaxPermissionValidator
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.aspects.FishStripesAspect
|
||||
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SeasonAspect
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SnakePatternAspect
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.BattleCriticalHitsFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.feature.DamageTakenFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.FISH_STRIPES
|
||||
import com.cobblemon.mod.common.pokemon.feature.FishStripesFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.SEASON
|
||||
import com.cobblemon.mod.common.pokemon.feature.SNAKE_PATTERN
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.SnakePatternFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.UntradeableProperty
|
||||
@@ -101,7 +93,6 @@ import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.registry.CompletableRegistry
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.getServer
|
||||
import com.cobblemon.mod.common.util.ifDedicatedServer
|
||||
@@ -132,7 +123,7 @@ import org.apache.logging.log4j.LogManager
|
||||
|
||||
object Cobblemon {
|
||||
const val MODID = "cobblemon"
|
||||
const val VERSION = "1.1.0"
|
||||
const val VERSION = "1.2.0"
|
||||
const val CONFIG_PATH = "config/$MODID/main.json"
|
||||
val LOGGER = LogManager.getLogger()
|
||||
|
||||
@@ -162,7 +153,6 @@ object Cobblemon {
|
||||
DropEntry.register("item", ItemDropEntry::class.java, isDefault = true)
|
||||
|
||||
ExperienceGroups.registerDefaults()
|
||||
PokemonSpecies.observable.subscribe { CobblemonSpawnPools.load() }
|
||||
|
||||
this.loadConfig()
|
||||
this.implementation = implementation
|
||||
@@ -225,21 +215,12 @@ object Cobblemon {
|
||||
|
||||
SHINY_ASPECT.register()
|
||||
GENDER_ASPECT.register()
|
||||
SnakePatternAspect.register()
|
||||
FishStripesAspect.register()
|
||||
SeasonAspect.register()
|
||||
|
||||
config.flagSpeciesFeatures.forEach(FlagSpeciesFeature::registerWithPropertyAndAspect)
|
||||
config.globalFlagSpeciesFeatures.forEach(FlagSpeciesFeature::registerWithPropertyAndAspect)
|
||||
FlagSpeciesFeature.registerWithPropertyAndAspect(DataKeys.ALOLAN)
|
||||
FlagSpeciesFeature.registerWithPropertyAndAspect(DataKeys.GALARIAN)
|
||||
FlagSpeciesFeature.registerWithPropertyAndAspect(DataKeys.HISUIAN)
|
||||
FlagSpeciesFeature.registerWithPropertyAndAspect(DataKeys.VALENCIAN)
|
||||
SpeciesFeature.registerGlobalFeature(DamageTakenFeature.ID) { DamageTakenFeature() }
|
||||
SpeciesFeature.registerGlobalFeature(BattleCriticalHitsFeature.ID) { BattleCriticalHitsFeature() }
|
||||
EnumSpeciesFeature.registerWithProperty(SNAKE_PATTERN, SnakePatternFeature::class.java)
|
||||
EnumSpeciesFeature.registerWithProperty(SEASON, SeasonFeature::class.java)
|
||||
EnumSpeciesFeature.registerWithProperty(FISH_STRIPES, FishStripesFeature::class.java)
|
||||
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
|
||||
|
||||
GlobalSpeciesFeatures.register(DamageTakenFeature.ID) { DamageTakenFeature() }
|
||||
GlobalSpeciesFeatures.register(BattleCriticalHitsFeature.ID) { BattleCriticalHitsFeature() }
|
||||
|
||||
CustomPokemonProperty.register(UntradeableProperty)
|
||||
CustomPokemonProperty.register(UncatchableProperty)
|
||||
|
||||
@@ -32,15 +32,8 @@ object CobblemonEntities : CompletableRegistry<EntityType<*>>(Registry.ENTITY_TY
|
||||
EntityAttributeRegistry.register(
|
||||
{ POKEMON.get() },
|
||||
{
|
||||
DefaultAttributeContainer
|
||||
.builder()
|
||||
LivingEntity.createLivingAttributes()
|
||||
.add(EntityAttributes.GENERIC_FOLLOW_RANGE)
|
||||
.add(EntityAttributes.GENERIC_MAX_HEALTH)
|
||||
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED)
|
||||
.add(EntityAttributes.HORSE_JUMP_STRENGTH)
|
||||
.add(EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE)
|
||||
.add(EntityAttributes.GENERIC_ARMOR)
|
||||
.add(EntityAttributes.GENERIC_ARMOR_TOUGHNESS)
|
||||
.also { CobblemonEvents.ENTITY_ATTRIBUTE.post(EntityAttributeEvent(POKEMON.get(), it)) }
|
||||
}
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ interface DataProvider {
|
||||
*
|
||||
* @param registry The [DataRegistry] being registered.
|
||||
*/
|
||||
fun register(registry: DataRegistry)
|
||||
fun <T : DataRegistry> register(registry: T): T
|
||||
|
||||
/**
|
||||
* Attempts to find a [DataRegistry] with the given [Identifier].
|
||||
|
||||
@@ -174,4 +174,7 @@ object Natures {
|
||||
fun getRandomNature(): Nature {
|
||||
return allNatures.random()
|
||||
}
|
||||
|
||||
fun all(): Collection<Nature> = this.allNatures.toList()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.cobblemon.mod.common.api.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityPool
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
|
||||
import com.cobblemon.mod.common.api.ai.SleepDepth
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropMethod
|
||||
import com.cobblemon.mod.common.api.entity.EntityDimensionsAdapter
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
|
||||
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroupAdapter
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.condition.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
|
||||
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.AbilityPoolAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.AbilityTemplateAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BiomeLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BlockLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BoxAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.DropEntryAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.EggGroupAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ItemLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.LazySetAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.LearnsetAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.NbtCompoundAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TimeRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.entity.EntityDimensions
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
object PokemonForms : JsonDataRegistry<FormData> {
|
||||
|
||||
override val id = cobblemonResource("forms")
|
||||
override val type = ResourceType.SERVER_DATA
|
||||
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(Stat::class.java, Cobblemon.statProvider.typeAdapter)
|
||||
.registerTypeAdapter(ElementalType::class.java, ElementalTypeAdapter)
|
||||
.registerTypeAdapter(AbilityTemplate::class.java, AbilityTemplateAdapter)
|
||||
.registerTypeAdapter(ShoulderEffect::class.java, ShoulderEffectAdapter)
|
||||
.registerTypeAdapter(MoveTemplate::class.java, MoveTemplateAdapter)
|
||||
.registerTypeAdapter(ExperienceGroup::class.java, ExperienceGroupAdapter)
|
||||
.registerTypeAdapter(EntityDimensions::class.java, EntityDimensionsAdapter)
|
||||
.registerTypeAdapter(Learnset::class.java, LearnsetAdapter)
|
||||
.registerTypeAdapter(Evolution::class.java, CobblemonEvolutionAdapter)
|
||||
.registerTypeAdapter(Box::class.java, BoxAdapter)
|
||||
.registerTypeAdapter(AbilityPool::class.java, AbilityPoolAdapter)
|
||||
.registerTypeAdapter(EvolutionRequirement::class.java, CobblemonRequirementAdapter)
|
||||
.registerTypeAdapter(PreEvolution::class.java, CobblemonPreEvolutionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(Set::class.java, Evolution::class.java).type, LazySetAdapter(Evolution::class))
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRangeAdapter)
|
||||
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
|
||||
.registerTypeAdapter(SleepDepth::class.java, SleepDepth.adapter)
|
||||
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
|
||||
.registerTypeAdapter(NbtCompound::class.java, NbtCompoundAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.disableHtmlEscaping()
|
||||
.enableComplexMapKeySerialization()
|
||||
.create()
|
||||
|
||||
override val typeToken: TypeToken<FormData> = TypeToken.get(FormData::class.java)
|
||||
override val resourcePath: String = "forms"
|
||||
|
||||
override val observable = SimpleObservable<PokemonForms>()
|
||||
|
||||
override fun reload(data: Map<Identifier, FormData>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -25,6 +26,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtList
|
||||
import net.minecraft.nbt.NbtString
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.InvalidIdentifierException
|
||||
import net.minecraft.world.World
|
||||
|
||||
@@ -74,6 +76,8 @@ open class PokemonProperties {
|
||||
props.species = parseSpeciesIdentifier(keyPairs)
|
||||
props.form = parseForm(keyPairs)
|
||||
props.friendship = parseIntProperty(keyPairs, listOf("friendship"))?.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
|
||||
props.pokeball = parseIdentifierOfRegistry(keyPairs, listOf("pokeball")) { identifier -> PokeBalls.getPokeBall(identifier)?.name?.toString() }
|
||||
props.nature = parseIdentifierOfRegistry(keyPairs, listOf("nature")) { identifier -> Natures.getNature(identifier)?.name?.toString() }
|
||||
props.updateAspects()
|
||||
return props
|
||||
}
|
||||
@@ -92,6 +96,17 @@ open class PokemonProperties {
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseIdentifierOfRegistry(keyPairs: MutableList<Pair<String, String?>>, validKeys: List<String>, valueFetcher: (Identifier) -> String?): String? {
|
||||
val matched = getMatchedKeyPair(keyPairs, validKeys) ?: return null
|
||||
val value = matched.second?.lowercase() ?: return null
|
||||
return try {
|
||||
val identifier = value.asIdentifierDefaultingNamespace()
|
||||
valueFetcher(identifier)
|
||||
} catch (_: InvalidIdentifierException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseSpeciesIdentifier(keyPairs: MutableList<Pair<String, String?>>): String? {
|
||||
val matched = getMatchedKeyPair(keyPairs, listOf("species"))
|
||||
if (matched != null) {
|
||||
@@ -186,6 +201,8 @@ open class PokemonProperties {
|
||||
var gender: Gender? = null
|
||||
var level: Int? = null
|
||||
var friendship: Int? = null
|
||||
var pokeball: String? = null
|
||||
var nature: String? = null
|
||||
var aspects: Set<String> = emptySet()
|
||||
|
||||
var customProperties = mutableListOf<CustomPokemonProperty>()
|
||||
@@ -218,6 +235,8 @@ open class PokemonProperties {
|
||||
gender?.let { pokemon.gender = it }
|
||||
level?.let { pokemon.level = it }
|
||||
friendship?.let { pokemon.setFriendship(it) }
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
|
||||
customProperties.forEach { it.apply(pokemon) }
|
||||
}
|
||||
|
||||
@@ -238,6 +257,8 @@ open class PokemonProperties {
|
||||
shiny?.let { pokemonEntity.pokemon.shiny = it }
|
||||
gender?.let { pokemonEntity.pokemon.gender = it }
|
||||
friendship?.let { pokemonEntity.pokemon.setFriendship(it) }
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemonEntity.pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemonEntity.pokemon.nature = nature } }
|
||||
customProperties.forEach { it.apply(pokemonEntity) }
|
||||
}
|
||||
|
||||
@@ -261,6 +282,8 @@ open class PokemonProperties {
|
||||
}
|
||||
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
|
||||
return customProperties.none { !it.matches(pokemon) }
|
||||
}
|
||||
|
||||
@@ -282,6 +305,8 @@ open class PokemonProperties {
|
||||
}
|
||||
form?.takeIf { !it.equals(pokemonEntity.pokemon.form.name, true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemonEntity.pokemon.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemonEntity.pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
nature?.takeIf { it != pokemonEntity.pokemon.nature.name.toString() }?.let { return false }
|
||||
return customProperties.none { !it.matches(pokemonEntity) }
|
||||
}
|
||||
|
||||
@@ -302,6 +327,8 @@ open class PokemonProperties {
|
||||
species?.let { nbt.putString(DataKeys.POKEMON_SPECIES_TEXT, it) }
|
||||
form?.let { nbt.putString(DataKeys.POKEMON_FORM_ID, it) }
|
||||
friendship?.let { nbt.putInt(DataKeys.POKEMON_FRIENDSHIP, it) }
|
||||
pokeball?.let { nbt.putString(DataKeys.POKEMON_CAUGHT_BALL, it) }
|
||||
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
|
||||
val custom = NbtList()
|
||||
customProperties.map { NbtString.of(it.asString()) }.forEach { custom.add(it) }
|
||||
nbt.put(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
|
||||
@@ -316,6 +343,8 @@ open class PokemonProperties {
|
||||
species = if (tag.contains(DataKeys.POKEMON_SPECIES_TEXT)) tag.getString(DataKeys.POKEMON_SPECIES_TEXT) else null
|
||||
form = if (tag.contains(DataKeys.POKEMON_FORM_ID)) tag.getString(DataKeys.POKEMON_FORM_ID) else null
|
||||
friendship = if (tag.contains(DataKeys.POKEMON_FRIENDSHIP)) tag.getInt(DataKeys.POKEMON_FRIENDSHIP) else null
|
||||
pokeball = if (tag.contains(DataKeys.POKEMON_CAUGHT_BALL)) tag.getString(DataKeys.POKEMON_CAUGHT_BALL) else null
|
||||
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
|
||||
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, NbtElement.STRING_TYPE.toInt())
|
||||
// This is kinda gross
|
||||
custom.forEach { customProperties.addAll(parse(it.asString()).customProperties) }
|
||||
@@ -332,6 +361,8 @@ open class PokemonProperties {
|
||||
species?.let { json.addProperty(DataKeys.POKEMON_SPECIES_TEXT, it) }
|
||||
form?.let { json.addProperty(DataKeys.POKEMON_FORM_ID, it) }
|
||||
friendship?.let { json.addProperty(DataKeys.POKEMON_FRIENDSHIP, it) }
|
||||
pokeball?.let { json.addProperty(DataKeys.POKEMON_CAUGHT_BALL, it) }
|
||||
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
|
||||
val custom = JsonArray()
|
||||
customProperties.map { it.asString() }.forEach { custom.add(it) }
|
||||
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
|
||||
@@ -346,6 +377,8 @@ open class PokemonProperties {
|
||||
species = json.get(DataKeys.POKEMON_SPECIES_TEXT)?.asString
|
||||
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
|
||||
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
|
||||
pokeball = json.get(DataKeys.POKEMON_CAUGHT_BALL)?.asString
|
||||
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
|
||||
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
|
||||
// This is still kinda gross
|
||||
custom?.forEach { customProperties.addAll(parse(it.asString).customProperties) }
|
||||
@@ -361,6 +394,8 @@ open class PokemonProperties {
|
||||
shiny?.let { pieces.add("shiny=$it") }
|
||||
gender?.let { pieces.add("gender=$it")}
|
||||
friendship?.let { pieces.add("friendship=$it") }
|
||||
pokeball?.let { pieces.add("pokeball=$it") }
|
||||
nature?.let { pieces.add("nature=$it") }
|
||||
customProperties.forEach { pieces.add(it.asString()) }
|
||||
return pieces.joinToString(separator)
|
||||
}
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.substitute
|
||||
|
||||
/**
|
||||
* A [SpeciesFeatureProvider] which is a string value selected from a fixed list of choices. Parameters exist
|
||||
* to change default behaviour, aspects, and the available choices. Choices must be lowercase.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 30th, 2022
|
||||
*/
|
||||
open class ChoiceSpeciesFeatureProvider(
|
||||
override val keys: List<String>,
|
||||
var default: String? = null,
|
||||
var choices: List<String> = listOf(),
|
||||
var isAspect: Boolean = true,
|
||||
var aspectFormat: String = "{{choice}}"
|
||||
) : SpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
|
||||
override val needsKey = true
|
||||
fun getAspect(feature: StringSpeciesFeature) = aspectFormat.substitute("choice", feature.value)
|
||||
|
||||
override fun examples() = choices
|
||||
|
||||
internal constructor(): this(emptyList())
|
||||
|
||||
fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): StringSpeciesFeature? {
|
||||
val existing = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
return if (existing != null && existing.value in choices) {
|
||||
existing
|
||||
} else {
|
||||
val value = if (default in choices) {
|
||||
default!!
|
||||
} else if (default == "random") {
|
||||
// If it's mandatory, but they provided no value and no default, give it a random value.
|
||||
choices.randomOrNull() ?: throw IllegalStateException("The 'choices' list is empty for species feature provider: ${keys.joinToString()}")
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
fromString(value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun fromString(value: String?): StringSpeciesFeature? {
|
||||
val lower = value?.lowercase()
|
||||
if (lower == null || lower !in choices) {
|
||||
return null
|
||||
}
|
||||
|
||||
return StringSpeciesFeature(keys.first(), lower)
|
||||
}
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return if (isAspect) {
|
||||
pokemon.getFeature<StringSpeciesFeature>(keys.first())?.let { setOf(getAspect(it)) } ?: emptySet()
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return if (isAspect) {
|
||||
val feature = properties.customProperties.filterIsInstance<StringSpeciesFeature>().find { it.name == keys.first() }
|
||||
if (feature != null) {
|
||||
setOf(getAspect(feature))
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.EnumSet
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
|
||||
/**
|
||||
* A [SpeciesFeature] which has a single value from an enum. Extensions of this class
|
||||
* just need to provide the set of all entries of that enum.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 13th, 2022
|
||||
*/
|
||||
abstract class EnumSpeciesFeature<T : Enum<T>> : SpeciesFeature, CustomPokemonProperty {
|
||||
companion object {
|
||||
fun <E : Enum<E>, T : EnumSpeciesFeature<E>> registerWithProperty(name: String, clazz: Class<T>) {
|
||||
SpeciesFeature.register(name, clazz)
|
||||
CustomPokemonProperty.properties.add(EnumSpeciesFeatureCustomPropertyType<E>(name))
|
||||
}
|
||||
}
|
||||
|
||||
abstract override val name: String
|
||||
abstract fun getValues(): EnumSet<T>
|
||||
open var enumValue = getValues().random()
|
||||
|
||||
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
|
||||
pokemonNBT.putString(name, enumValue.name)
|
||||
return pokemonNBT
|
||||
}
|
||||
|
||||
override fun loadFromNBT(pokemonNBT: NbtCompound): SpeciesFeature {
|
||||
val valueName = pokemonNBT.getString(name)?.takeIf { it.isNotBlank() } ?: return this
|
||||
enumValue = getValues().first { it.name == valueName }
|
||||
return this
|
||||
}
|
||||
|
||||
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
|
||||
pokemonJSON.addProperty(name, enumValue.name)
|
||||
return pokemonJSON
|
||||
}
|
||||
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
|
||||
val valueName = pokemonJSON.get(name)?.asString ?: return this
|
||||
enumValue = getValues().first { it.name == valueName }
|
||||
return this
|
||||
}
|
||||
|
||||
override fun asString() = "$name=${enumValue.name}"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
pokemon.getFeature<EnumSpeciesFeature<T>>(name)?.enumValue = enumValue
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<EnumSpeciesFeature<T>>(name)?.enumValue == enumValue
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
|
||||
/**
|
||||
* An implementation of [CustomPokemonPropertyType] that interprets values for some [EnumSpeciesFeature]. The key of the
|
||||
* property is the name of the enum species feature.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 13th, 2022
|
||||
*/
|
||||
open class EnumSpeciesFeatureCustomPropertyType<T : Enum<T>>(val name: String) : CustomPokemonPropertyType<EnumSpeciesFeature<T>> {
|
||||
override val keys = setOf(name)
|
||||
override val needsKey = true
|
||||
|
||||
override fun fromString(value: String?): EnumSpeciesFeature<T>? {
|
||||
value ?: return null
|
||||
val feature = SpeciesFeature.get(name)?.invoke() as? EnumSpeciesFeature<T> ?: return null
|
||||
val enumValue = feature.getValues().find { it.name.equals(value, ignoreCase = true) } ?: return null
|
||||
feature.enumValue = enumValue
|
||||
return feature
|
||||
}
|
||||
|
||||
override fun examples(): Collection<String> {
|
||||
val feature = SpeciesFeature.get(name)?.invoke() as? EnumSpeciesFeature<T> ?: return emptySet()
|
||||
return feature.getValues().map { it.name.lowercase() }
|
||||
}
|
||||
}
|
||||
+76
-19
@@ -8,18 +8,19 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.SingleConditionalAspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.substitute
|
||||
import com.google.gson.JsonObject
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
|
||||
/**
|
||||
* A simple [SpeciesFeature] that is a true/false flag value. It implements [CustomPokemonProperty]
|
||||
* to provide a convenient means of registering it with a [CustomPokemonPropertyType]. That can be done
|
||||
* smoothly using [FlagSpeciesFeature.registerWithProperty].
|
||||
* A simple [SpeciesFeature] that is a true/false flag value. It implements [CustomPokemonProperty] for use in
|
||||
* [PokemonProperties]. [FlagSpeciesFeatureProvider]s must be registered within [SpeciesFeatures].
|
||||
*
|
||||
* Implementations of this class don't need to implement anything.
|
||||
*
|
||||
@@ -27,21 +28,11 @@ import net.minecraft.nbt.NbtCompound
|
||||
* @since May 13th, 2022
|
||||
*/
|
||||
open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, CustomPokemonProperty {
|
||||
companion object {
|
||||
fun <T : FlagSpeciesFeature> registerWithProperty(name: String, clazz: Class<T>) {
|
||||
SpeciesFeature.register(name, clazz)
|
||||
CustomPokemonProperty.properties.add(FlagSpeciesFeatureCustomPropertyType(name))
|
||||
}
|
||||
|
||||
fun registerWithPropertyAndAspect(name: String) {
|
||||
SpeciesFeature.register(name) { FlagSpeciesFeature(name) }
|
||||
CustomPokemonProperty.properties.add(FlagSpeciesFeatureCustomPropertyType(name))
|
||||
AspectProvider.register(SingleConditionalAspectProvider.getForFeature(name))
|
||||
}
|
||||
constructor(name: String, enabled: Boolean): this(name) {
|
||||
this.enabled = enabled
|
||||
}
|
||||
|
||||
open var enabled = false
|
||||
|
||||
var enabled = false
|
||||
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
|
||||
pokemonNBT.putBoolean(name, enabled)
|
||||
return pokemonNBT
|
||||
@@ -66,9 +57,75 @@ open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, Custo
|
||||
override fun asString() = "$name=$enabled"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled = enabled
|
||||
pokemon.updateAspects()
|
||||
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
|
||||
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
|
||||
val existingFeature = pokemon.getFeature<FlagSpeciesFeature>(name)
|
||||
if (existingFeature != null) {
|
||||
existingFeature.enabled = enabled
|
||||
} else {
|
||||
pokemon.features.add(FlagSpeciesFeature(name, enabled))
|
||||
}
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == enabled
|
||||
}
|
||||
|
||||
class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
|
||||
override val keys: List<String>
|
||||
override val needsKey get() = true
|
||||
val default: String? = null
|
||||
val isAspect = true
|
||||
|
||||
override fun examples() = setOf("true", "false")
|
||||
|
||||
internal constructor() {
|
||||
this.keys = emptyList()
|
||||
}
|
||||
|
||||
constructor(keys: List<String>) {
|
||||
this.keys = keys
|
||||
}
|
||||
|
||||
constructor(vararg keys: String) : this(keys.toList())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): FlagSpeciesFeature? {
|
||||
return pokemon.getFeature(keys.first())
|
||||
?: when (default) {
|
||||
"random" -> FlagSpeciesFeature(keys.first(), Random.Default.nextBoolean())
|
||||
in setOf("true", "false") -> FlagSpeciesFeature(keys.first(), default.toBoolean())
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun fromString(value: String?): FlagSpeciesFeature? {
|
||||
val isWeirdValue = value != null && value !in examples()
|
||||
|
||||
if (isWeirdValue) {
|
||||
return null
|
||||
}
|
||||
|
||||
return if (value == null) {
|
||||
FlagSpeciesFeature(keys.first(), true)
|
||||
} else {
|
||||
FlagSpeciesFeature(keys.first(), value.toBoolean())
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return if (isAspect && pokemon.getFeature<FlagSpeciesFeature>(keys.first())?.enabled == true) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return if (isAspect && properties.customProperties.filterIsInstance<FlagSpeciesFeature>().find { it.name == keys.first() }?.enabled == true) {
|
||||
setOf(keys.first())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
|
||||
/**
|
||||
* An implementation of [CustomPokemonPropertyType] that is a simple true/false value for a specific
|
||||
* [FlagSpeciesFeature]. The key of the property is the name of the flag.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 13th, 2022
|
||||
*/
|
||||
open class FlagSpeciesFeatureCustomPropertyType(val name: String) : CustomPokemonPropertyType<FlagSpeciesFeature> {
|
||||
override val keys = setOf(name)
|
||||
override val needsKey = true
|
||||
|
||||
override fun fromString(value: String?): FlagSpeciesFeature? {
|
||||
var enabled = true
|
||||
if (value != null) {
|
||||
try {
|
||||
enabled = value.toBoolean()
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
val feature = SpeciesFeature.get(name)?.invoke() as? FlagSpeciesFeature ?: return null
|
||||
feature.enabled = enabled
|
||||
return feature
|
||||
}
|
||||
|
||||
override fun examples() = setOf("true", "false")
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A registry very similar to [SpeciesFeatures] but the [SpeciesFeatureProvider] contained within it
|
||||
* will attempt to be applied to all Pokémon, even those that have not elected to have the feature.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 30th, 2022
|
||||
*/
|
||||
object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
override val id = cobblemonResource("global_species_features")
|
||||
override val type = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<SpeciesFeatures>()
|
||||
|
||||
private val codeFeatures = mutableMapOf<String, SpeciesFeatureProvider<*>>()
|
||||
private val resourceFeatures = mutableMapOf<String, SpeciesFeatureProvider<*>>()
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
|
||||
override val resourcePath: String = "global_species_features"
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
|
||||
resourceFeatures.keys.toList().forEach(this::unregister)
|
||||
data.forEach(this::registerFromAssets)
|
||||
}
|
||||
|
||||
fun getCodeFeature(name: String) = resourceFeatures[name]
|
||||
fun getResourceFeature(name: String) = codeFeatures[name]
|
||||
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
|
||||
|
||||
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
|
||||
|
||||
private fun register(name: String, provider: SpeciesFeatureProvider<*>, isCoded: Boolean) {
|
||||
val mapping = if (isCoded) codeFeatures else resourceFeatures
|
||||
if (provider is AspectProvider) {
|
||||
AspectProvider.register(provider)
|
||||
}
|
||||
if (provider is CustomPokemonPropertyType<*>) {
|
||||
CustomPokemonProperty.register(provider)
|
||||
}
|
||||
mapping[name] = provider
|
||||
}
|
||||
|
||||
fun register(name: String, provider: SpeciesFeatureProvider<*>) = register(name, provider, isCoded = true)
|
||||
private fun registerFromAssets(identifier: Identifier, provider: SpeciesFeatureProvider<*>) = register(identifier.path, provider, isCoded = false)
|
||||
|
||||
fun unregister(name: String) {
|
||||
var coded = true
|
||||
val value = getResourceFeature(name)?.also { coded = false } ?: getCodeFeature(name) ?: return
|
||||
if (value is AspectProvider) {
|
||||
AspectProvider.unregister(value)
|
||||
}
|
||||
if (value is CustomPokemonPropertyType<*>) {
|
||||
CustomPokemonProperty.unregister(value)
|
||||
}
|
||||
val mapping = if (coded) codeFeatures else resourceFeatures
|
||||
mapping.remove(name)
|
||||
}
|
||||
}
|
||||
+3
-32
@@ -16,8 +16,9 @@ import net.minecraft.nbt.NbtCompound
|
||||
|
||||
/**
|
||||
* A piece of state that can be added to some species of Pokémon. Registering an implementing class
|
||||
* using [SpeciesFeature.register] adds it as a usable value in the [Species.features] list. All Pokémon
|
||||
* are given a default-constructed instance of all SpeciesFeatures mentioned in its species definition.
|
||||
* using [SpeciesFeatures.register] adds it as a usable value in the [SpeciesFeatures] list. All Pokémon
|
||||
* are given the opportunity to be assigned features depending on whether it's a global feature or they
|
||||
* have opted into it in their species JSON.
|
||||
*
|
||||
* The role of this is to allow species-specific data to be attached to individual Pokémon, such as an alolan
|
||||
* flag or a Vivillon pattern variety. This is powerful when combined with [AspectProvider]s.
|
||||
@@ -27,36 +28,6 @@ import net.minecraft.nbt.NbtCompound
|
||||
*/
|
||||
interface SpeciesFeature {
|
||||
val name: String
|
||||
companion object {
|
||||
private val speciesFeatures = HashBiMap.create<String, () -> SpeciesFeature>()
|
||||
private val globalSpeciesFeatures = HashBiMap.create<String, () -> SpeciesFeature>()
|
||||
fun <T : SpeciesFeature> register(name: String, clazz: Class<T>, global: Boolean = false) {
|
||||
try {
|
||||
clazz.getDeclaredConstructor().newInstance()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
throw IllegalArgumentException("The given species feature class for $name could not be constructed with a default constructor call. You need a default constructor to use species features.", e)
|
||||
}
|
||||
val instanceJob = { clazz.getDeclaredConstructor().newInstance() }
|
||||
speciesFeatures[name] = instanceJob
|
||||
if (global) {
|
||||
globalSpeciesFeatures[name] = instanceJob
|
||||
}
|
||||
}
|
||||
fun <T : SpeciesFeature> register(name: String, instantiator: () -> T) {
|
||||
speciesFeatures[name] = instantiator
|
||||
}
|
||||
fun <T : SpeciesFeature> registerGlobalFeature(name: String, instantiator: () -> T) {
|
||||
this.register(name, instantiator)
|
||||
globalSpeciesFeatures[name] = instantiator
|
||||
}
|
||||
fun unregister(name: String) {
|
||||
speciesFeatures.remove(name)
|
||||
}
|
||||
fun get(name: String) = speciesFeatures[name]
|
||||
fun globalFeatures() = this.globalSpeciesFeatures.toMap()
|
||||
}
|
||||
|
||||
fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound
|
||||
fun loadFromNBT(pokemonNBT: NbtCompound): SpeciesFeature
|
||||
fun saveToJSON(pokemonJSON: JsonObject): JsonObject
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
|
||||
/**
|
||||
* An assignment of some number of [Species] to some number of [SpeciesFeatureProvider]s. In most cases
|
||||
* this will be a single Pokémon and a single feature, but it can be used to attach many to many.
|
||||
*
|
||||
* These assignments are registered in [SpeciesFeatureAssignments].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 1st, 2022
|
||||
*/
|
||||
class SpeciesFeatureAssignment {
|
||||
val pokemon: List<String> = emptyList()
|
||||
val features: List<String> = emptyList()
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A registry of assignments combining [SpeciesFeatures] and [PokemonSpecies]. This is a way around the issue
|
||||
* of when multiple data packs want to add their own [SpeciesFeature]s to the same species. The correct way,
|
||||
* with this registry, is to add a new JSON that joins together a list of species with a list of species feature
|
||||
* keys.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 1st, 2022
|
||||
*/
|
||||
object SpeciesFeatureAssignments : JsonDataRegistry<SpeciesFeatureAssignment> {
|
||||
override val id: Identifier = cobblemonResource("species_feature_assignments")
|
||||
override val type: ResourceType = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<SpeciesFeatureAssignments>()
|
||||
|
||||
override val gson: Gson = GsonBuilder().setPrettyPrinting().create()
|
||||
override val typeToken = TypeToken.get(SpeciesFeatureAssignment::class.java)
|
||||
override val resourcePath = "species_feature_assignments"
|
||||
|
||||
private val assignments = mutableMapOf<String, MutableSet<String>>()
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureAssignment>) {
|
||||
data.values.forEach {
|
||||
it.pokemon.forEach { pokemon ->
|
||||
assignments.getOrPut(pokemon) { mutableSetOf() }.addAll(it.features)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFeatures(species: Species) = assignments[species.name] ?: emptySet()
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
/**
|
||||
* A provider of [SpeciesFeature]s. These must be registered via [SpeciesFeatures] either programmatically or
|
||||
* using the datapack structure. Note that for datapacks to be able to use an implementation of this interface,
|
||||
* it must be registered in [SpeciesFeatures.types] and then the JSON must include a 'type' field that references
|
||||
* the key used for that mapping.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 29th, 2022
|
||||
*/
|
||||
fun interface SpeciesFeatureProvider<T : SpeciesFeature> {
|
||||
/**
|
||||
* Attempts to create a species feature for the given [Pokemon]. It is appropriate to check for an existing feature
|
||||
* that is compatible with this type and transfer / retain that feature instead of constructing a new one. For example
|
||||
* a feature for an Ekan's snake pattern should be retained when evolving into Arbok, and species feature invocation
|
||||
* occurs whenever a species changes.
|
||||
*/
|
||||
operator fun invoke(pokemon: Pokemon): T?
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A registry for [SpeciesFeatureProvider]s. This is the backbone of species-specific data such as
|
||||
* variation information and counters used for some evolution types.
|
||||
*
|
||||
* Providers are loaded from JSON but can also be added programmatically. Species feature providers
|
||||
* that are registered via code will replace same-name features that were drawn from assets.
|
||||
*
|
||||
* If a species feature provider implements [CustomPokemonPropertyType] then it will be automatically
|
||||
* registered appropriately, as it will with aspects if it implements [AspectProvider].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 30th, 2022
|
||||
*/
|
||||
object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
override val id = cobblemonResource("species_features")
|
||||
override val type = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<SpeciesFeatures>()
|
||||
|
||||
val types = mutableMapOf<String, Class<out SpeciesFeatureProvider<*>>>()
|
||||
|
||||
private val codeFeatures = mutableMapOf<String, SpeciesFeatureProvider<*>>()
|
||||
private val resourceFeatures = mutableMapOf<String, SpeciesFeatureProvider<*>>()
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
|
||||
override val resourcePath: String = "species_features"
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
|
||||
resourceFeatures.keys.toList().forEach(this::unregister)
|
||||
data.forEach(this::registerFromAssets)
|
||||
}
|
||||
|
||||
fun getCodeFeature(name: String) = resourceFeatures[name]
|
||||
fun getResourceFeature(name: String) = codeFeatures[name]
|
||||
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
|
||||
|
||||
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
|
||||
fun getFeaturesFor(species: Species): List<SpeciesFeatureProvider<*>> {
|
||||
val mentionedFeatures = species.features.mapNotNull(this::getFeature)
|
||||
val globalFeatures = GlobalSpeciesFeatures.getFeatures()
|
||||
val assignedFeatures = SpeciesFeatureAssignments.getFeatures(species).mapNotNull(this::getFeature)
|
||||
|
||||
return (mentionedFeatures + globalFeatures + assignedFeatures).distinct()
|
||||
}
|
||||
|
||||
private fun register(name: String, provider: SpeciesFeatureProvider<*>, isCoded: Boolean) {
|
||||
val mapping = if (isCoded) codeFeatures else resourceFeatures
|
||||
if (provider is AspectProvider) {
|
||||
AspectProvider.register(provider)
|
||||
}
|
||||
if (provider is CustomPokemonPropertyType<*>) {
|
||||
CustomPokemonProperty.register(provider)
|
||||
}
|
||||
mapping[name] = provider
|
||||
}
|
||||
|
||||
fun register(name: String, provider: SpeciesFeatureProvider<*>) = register(name, provider, isCoded = true)
|
||||
private fun registerFromAssets(identifier: Identifier, provider: SpeciesFeatureProvider<*>) = register(identifier.path, provider, isCoded = false)
|
||||
|
||||
fun unregister(name: String) {
|
||||
var coded = true
|
||||
val value = getResourceFeature(name)?.also { coded = false } ?: getCodeFeature(name) ?: return
|
||||
if (value is AspectProvider) {
|
||||
AspectProvider.unregister(value)
|
||||
}
|
||||
if (value is CustomPokemonPropertyType<*>) {
|
||||
CustomPokemonProperty.unregister(value)
|
||||
}
|
||||
val mapping = if (coded) codeFeatures else resourceFeatures
|
||||
mapping.remove(name)
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
|
||||
/**
|
||||
* A species feature value that is a string value.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 30th, 2022
|
||||
*/
|
||||
class StringSpeciesFeature(
|
||||
override val name: String,
|
||||
var value: String
|
||||
) : SpeciesFeature, CustomPokemonProperty {
|
||||
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
|
||||
pokemonNBT.putString(name, value)
|
||||
return pokemonNBT
|
||||
}
|
||||
|
||||
override fun loadFromNBT(pokemonNBT: NbtCompound): SpeciesFeature {
|
||||
value = pokemonNBT.getString(name)?.takeIf { it.isNotBlank() }?.lowercase() ?: return this
|
||||
return this
|
||||
}
|
||||
|
||||
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
|
||||
pokemonJSON.addProperty(name, value)
|
||||
return pokemonJSON
|
||||
}
|
||||
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
|
||||
value = pokemonJSON.get(name)?.asString?.lowercase() ?: return this
|
||||
return this
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$value"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
|
||||
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
|
||||
val existingFeature = pokemon.getFeature<StringSpeciesFeature>(name)
|
||||
if (existingFeature != null) {
|
||||
existingFeature.value = value
|
||||
} else {
|
||||
pokemon.features.add(StringSpeciesFeature(name, value))
|
||||
}
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(name)?.value == value
|
||||
}
|
||||
+4
@@ -48,6 +48,10 @@ interface CustomPokemonProperty {
|
||||
this.triggerSyncAttempt()
|
||||
}
|
||||
|
||||
fun unregister(property: CustomPokemonPropertyType<*>) {
|
||||
properties.remove(property)
|
||||
}
|
||||
|
||||
// We do this every time a new property is registered if the server is running in order to synchronize all players with the new property for tab completion purposes
|
||||
private fun triggerSyncAttempt() {
|
||||
val server = getServer() ?: return
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
|
||||
import com.cobblemon.mod.common.entity.pokemon.CobblemonAgingDespawner
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A grouping of all the overarching behaviours of the Best Spawner system. This is a convenient accessor to
|
||||
@@ -79,7 +80,6 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
*/
|
||||
object BestSpawner {
|
||||
var config = BestSpawnerConfig()
|
||||
var presets = mutableMapOf<String, SpawnDetailPreset>()
|
||||
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
|
||||
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
|
||||
|
||||
@@ -108,10 +108,8 @@ object BestSpawner {
|
||||
|
||||
config = BestSpawnerConfig.load()
|
||||
|
||||
SpawnDetailPreset.registerPresetType(BasicSpawnDetailPreset.NAME, BasicSpawnDetailPreset::class.java)
|
||||
SpawnDetailPreset.registerPresetType(PokemonSpawnDetailPreset.NAME, PokemonSpawnDetailPreset::class.java)
|
||||
presets = SpawnDetailPreset.load()
|
||||
LOGGER.info("Loaded ${presets.size} spawn detail presets.")
|
||||
SpawnDetailPresets.registerPresetType(BasicSpawnDetailPreset.NAME, BasicSpawnDetailPreset::class.java)
|
||||
SpawnDetailPresets.registerPresetType(PokemonSpawnDetailPreset.NAME, PokemonSpawnDetailPreset::class.java)
|
||||
}
|
||||
|
||||
fun onServerStarted() {
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.spawning
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BucketPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.ContextPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
|
||||
/**
|
||||
* A collection of all of Cobblemon's general-purpose [SpawnPool]s. These
|
||||
@@ -25,6 +26,6 @@ object CobblemonSpawnPools {
|
||||
lateinit var WORLD_SPAWN_POOL: SpawnPool
|
||||
|
||||
fun load() {
|
||||
WORLD_SPAWN_POOL = SpawnLoader.load("world").addPrecalculators(ContextPrecalculation, BucketPrecalculation)
|
||||
WORLD_SPAWN_POOL = CobblemonDataProvider.register(SpawnPool("world").addPrecalculators(ContextPrecalculation, BucketPrecalculation))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
|
||||
import com.cobblemon.mod.common.util.adapters.BiomeLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BlockLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RegisteredSpawningContextAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnDetailPresetAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TimeRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
/**
|
||||
* Data registry for [SpawnDetailPreset]s. These help the maintainability of spawn files by allowing common presets
|
||||
* to be defined separately to the spawns that obey it. You can register custom presets either programmatically or
|
||||
* by adding preset JSONs to the spawn_detail_presets data folder.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 9th, 2022
|
||||
*/
|
||||
object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
|
||||
val GSON = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.setLenient()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(RegisteredSpawningContext::class.java, RegisteredSpawningContextAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(SpawnDetailPreset::class.java, SpawnDetailPresetAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRangeAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.create()
|
||||
|
||||
val presetTypes = mutableMapOf<String, Class<out SpawnDetailPreset>>()
|
||||
fun <T : SpawnDetailPreset> registerPresetType(name: String, detailClass: Class<T>) {
|
||||
presetTypes[name] = detailClass
|
||||
}
|
||||
|
||||
override val gson: Gson = GSON
|
||||
override val typeToken = TypeToken.get(SpawnDetailPreset::class.java)
|
||||
override val resourcePath = "spawn_detail_presets"
|
||||
override val id = cobblemonResource(resourcePath)
|
||||
override val type = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<SpawnDetailPresets>()
|
||||
|
||||
var presets = mutableMapOf<Identifier, SpawnDetailPreset>()
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun reload(data: Map<Identifier, SpawnDetailPreset>) {
|
||||
this.presets = data.toMutableMap()
|
||||
Cobblemon.LOGGER.info("Loaded ${presets.size} spawn detail presets.")
|
||||
}
|
||||
}
|
||||
@@ -81,145 +81,145 @@ object SpawnLoader {
|
||||
|
||||
var deserializingConditionClass: Class<out SpawningCondition<*>>? = null
|
||||
|
||||
fun load(folder: String): SpawnPool {
|
||||
val pool = SpawnPool()
|
||||
|
||||
val external = getExternalSets(folder)
|
||||
val internal = loadInternalFolder(folder)
|
||||
|
||||
external.forEach { set ->
|
||||
val matchingInternal = internal.find { it.id == set.id }
|
||||
if (set.preventOverwrite) {
|
||||
if (matchingInternal != null) {
|
||||
internal.remove(matchingInternal)
|
||||
}
|
||||
if (set.isEnabled()) {
|
||||
pool.details.addAll(set.filter { it.isModDependencySatisfied() })
|
||||
}
|
||||
} else {
|
||||
if (matchingInternal != null && matchingInternal.version.isHigherVersion(set.version)) {
|
||||
if (matchingInternal.isEnabled()) {
|
||||
pool.details.addAll(matchingInternal.filter { it.isModDependencySatisfied() })
|
||||
}
|
||||
internal.remove(matchingInternal)
|
||||
exportSet(folder, matchingInternal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal.forEach { set ->
|
||||
if (set.isEnabled()) {
|
||||
pool.details.addAll(set.filter { it.isModDependencySatisfied() })
|
||||
}
|
||||
if (config.exportSpawnsToConfig) {
|
||||
exportSet(folder, set)
|
||||
}
|
||||
}
|
||||
|
||||
pool.precalculate()
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
fun exportSet(folder: String, set: SpawnSet) {
|
||||
val file = File("./config/cobblemon/spawning/spawns/$folder/${set.id.lowercase()}.set.json")
|
||||
file.parentFile.mkdirs()
|
||||
file.createNewFile()
|
||||
|
||||
val internalStream = Cobblemon::class.java.getResourceAsStream(set.path.toAbsolutePath().toString())!!
|
||||
val bytes = internalStream.readAllBytes()
|
||||
internalStream.close()
|
||||
|
||||
val externalStream = FileOutputStream(file)
|
||||
externalStream.write(bytes)
|
||||
externalStream.close()
|
||||
}
|
||||
|
||||
fun getExternalSets(folder: String): MutableList<SpawnSet> {
|
||||
val sets = mutableListOf<SpawnSet>()
|
||||
val files = mutableListOf<File>()
|
||||
AssetLoading.searchFor(
|
||||
dir = "config/cobblemon/spawning/spawns/$folder",
|
||||
suffix = ".json",
|
||||
list = files
|
||||
)
|
||||
val paths = files.map { it.toPath() }
|
||||
|
||||
sets.addAll(
|
||||
paths.mapNotNull {
|
||||
try {
|
||||
val file = it.toFile()
|
||||
val reader = FileReader(file)
|
||||
val set = gson.fromJson<SpawnSet>(reader)
|
||||
reader.close()
|
||||
return@mapNotNull set
|
||||
} catch (exception : Exception) {
|
||||
LOGGER.error("Unable to load spawn set from ${it.pathString}")
|
||||
exception.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return sets
|
||||
}
|
||||
|
||||
fun loadSetFromPath(path: Path): SpawnSet {
|
||||
val pathString = if (path.toString().startsWith("/")) path.toString() else "/$path"
|
||||
val inputStream = Cobblemon.javaClass.getResourceAsStream(pathString)
|
||||
?: throw IllegalArgumentException("Unable to open resource stream for: $pathString")
|
||||
val reader = InputStreamReader(inputStream)
|
||||
val set = gson.fromJson<SpawnSet>(reader)
|
||||
reader.close()
|
||||
return set
|
||||
}
|
||||
|
||||
fun loadInternalFolder(folder: String): MutableList<SpawnSet> {
|
||||
val internalFolder = cobblemonResource("spawning/spawns/$folder")
|
||||
val path = internalFolder.toPath() ?: throw IllegalArgumentException("There is no valid, internal path for $internalFolder")
|
||||
val internalPaths = AssetLoading.fileSearch(path, { it.toString().endsWith(".json") }, recursive = true)
|
||||
val sets = mutableListOf<SpawnSet>()
|
||||
sets.addAll(internalPaths.flatMap { filePath -> loadInternalFile(filePath) })
|
||||
return sets
|
||||
}
|
||||
|
||||
fun loadInternalFile(path: Path): MutableList<SpawnSet> {
|
||||
if (path.toString().endsWith(".packed.json")) {
|
||||
return loadPackedSets(path).onEach { it.path = path }
|
||||
} else if (path.toString().endsWith(".json")) {
|
||||
return mutableListOf(loadSetFromPath(path)).onEach { it.path = path }
|
||||
}
|
||||
|
||||
return mutableListOf()
|
||||
}
|
||||
|
||||
fun loadPackedSets(path: Path): MutableList<SpawnSet> {
|
||||
val fileStream = Cobblemon::class.java.getResourceAsStream(path.toAbsolutePath().toString())
|
||||
?: throw IllegalArgumentException("Unable to create input stream for $path")
|
||||
|
||||
return try {
|
||||
loadPackedSetsFromStream(fileStream)
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error(e.message)
|
||||
return mutableListOf()
|
||||
} finally {
|
||||
fileStream.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun loadPackedSetsFromStream(inputStream: InputStream): MutableList<SpawnSet> {
|
||||
val sets = mutableListOf<SpawnSet>()
|
||||
val stream = InputStreamReader(inputStream)
|
||||
val jsonArray = gson.fromJson(stream, JsonArray::class.java)
|
||||
stream.close()
|
||||
jsonArray.forEach { jsonElement ->
|
||||
try {
|
||||
sets.add(gson.fromJson<SpawnSet>(jsonElement))
|
||||
} catch (exception: Exception) {
|
||||
exception.printStackTrace()
|
||||
}
|
||||
}
|
||||
return sets
|
||||
}
|
||||
// fun load(folder: String): SpawnPool {
|
||||
// val pool = SpawnPool()
|
||||
//
|
||||
// val external = getExternalSets(folder)
|
||||
// val internal = loadInternalFolder(folder)
|
||||
//
|
||||
// external.forEach { set ->
|
||||
// val matchingInternal = internal.find { it.id == set.id }
|
||||
// if (set.preventOverwrite) {
|
||||
// if (matchingInternal != null) {
|
||||
// internal.remove(matchingInternal)
|
||||
// }
|
||||
// if (set.isEnabled()) {
|
||||
// pool.details.addAll(set.filter { it.isModDependencySatisfied() })
|
||||
// }
|
||||
// } else {
|
||||
// if (matchingInternal != null && matchingInternal.version.isHigherVersion(set.version)) {
|
||||
// if (matchingInternal.isEnabled()) {
|
||||
// pool.details.addAll(matchingInternal.filter { it.isModDependencySatisfied() })
|
||||
// }
|
||||
// internal.remove(matchingInternal)
|
||||
// exportSet(folder, matchingInternal)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// internal.forEach { set ->
|
||||
// if (set.isEnabled()) {
|
||||
// pool.details.addAll(set.filter { it.isModDependencySatisfied() })
|
||||
// }
|
||||
// if (config.exportSpawnsToConfig) {
|
||||
// exportSet(folder, set)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pool.precalculate()
|
||||
//
|
||||
// return pool
|
||||
// }
|
||||
//
|
||||
// fun exportSet(folder: String, set: SpawnSet) {
|
||||
// val file = File("./config/cobblemon/spawning/spawns/$folder/${set.id.lowercase()}.set.json")
|
||||
// file.parentFile.mkdirs()
|
||||
// file.createNewFile()
|
||||
//
|
||||
// val internalStream = Cobblemon::class.java.getResourceAsStream(set.path.toAbsolutePath().toString())!!
|
||||
// val bytes = internalStream.readAllBytes()
|
||||
// internalStream.close()
|
||||
//
|
||||
// val externalStream = FileOutputStream(file)
|
||||
// externalStream.write(bytes)
|
||||
// externalStream.close()
|
||||
// }
|
||||
//
|
||||
// fun getExternalSets(folder: String): MutableList<SpawnSet> {
|
||||
// val sets = mutableListOf<SpawnSet>()
|
||||
// val files = mutableListOf<File>()
|
||||
// AssetLoading.searchFor(
|
||||
// dir = "config/cobblemon/spawning/spawns/$folder",
|
||||
// suffix = ".json",
|
||||
// list = files
|
||||
// )
|
||||
// val paths = files.map { it.toPath() }
|
||||
//
|
||||
// sets.addAll(
|
||||
// paths.mapNotNull {
|
||||
// try {
|
||||
// val file = it.toFile()
|
||||
// val reader = FileReader(file)
|
||||
// val set = gson.fromJson<SpawnSet>(reader)
|
||||
// reader.close()
|
||||
// return@mapNotNull set
|
||||
// } catch (exception : Exception) {
|
||||
// LOGGER.error("Unable to load spawn set from ${it.pathString}")
|
||||
// exception.printStackTrace()
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
//
|
||||
// return sets
|
||||
// }
|
||||
//
|
||||
// fun loadSetFromPath(path: Path): SpawnSet {
|
||||
// val pathString = if (path.toString().startsWith("/")) path.toString() else "/$path"
|
||||
// val inputStream = Cobblemon.javaClass.getResourceAsStream(pathString)
|
||||
// ?: throw IllegalArgumentException("Unable to open resource stream for: $pathString")
|
||||
// val reader = InputStreamReader(inputStream)
|
||||
// val set = gson.fromJson<SpawnSet>(reader)
|
||||
// reader.close()
|
||||
// return set
|
||||
// }
|
||||
//
|
||||
// fun loadInternalFolder(folder: String): MutableList<SpawnSet> {
|
||||
// val internalFolder = cobblemonResource("spawning/spawns/$folder")
|
||||
// val path = internalFolder.toPath() ?: throw IllegalArgumentException("There is no valid, internal path for $internalFolder")
|
||||
// val internalPaths = AssetLoading.fileSearch(path, { it.toString().endsWith(".json") }, recursive = true)
|
||||
// val sets = mutableListOf<SpawnSet>()
|
||||
// sets.addAll(internalPaths.flatMap { filePath -> loadInternalFile(filePath) })
|
||||
// return sets
|
||||
// }
|
||||
//
|
||||
// fun loadInternalFile(path: Path): MutableList<SpawnSet> {
|
||||
// if (path.toString().endsWith(".packed.json")) {
|
||||
// return loadPackedSets(path).onEach { it.path = path }
|
||||
// } else if (path.toString().endsWith(".json")) {
|
||||
// return mutableListOf(loadSetFromPath(path)).onEach { it.path = path }
|
||||
// }
|
||||
//
|
||||
// return mutableListOf()
|
||||
// }
|
||||
//
|
||||
// fun loadPackedSets(path: Path): MutableList<SpawnSet> {
|
||||
// val fileStream = Cobblemon::class.java.getResourceAsStream(path.toAbsolutePath().toString())
|
||||
// ?: throw IllegalArgumentException("Unable to create input stream for $path")
|
||||
//
|
||||
// return try {
|
||||
// loadPackedSetsFromStream(fileStream)
|
||||
// } catch (e: Exception) {
|
||||
// LOGGER.error(e.message)
|
||||
// return mutableListOf()
|
||||
// } finally {
|
||||
// fileStream.close()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// fun loadPackedSetsFromStream(inputStream: InputStream): MutableList<SpawnSet> {
|
||||
// val sets = mutableListOf<SpawnSet>()
|
||||
// val stream = InputStreamReader(inputStream)
|
||||
// val jsonArray = gson.fromJson(stream, JsonArray::class.java)
|
||||
// stream.close()
|
||||
// jsonArray.forEach { jsonElement ->
|
||||
// try {
|
||||
// sets.add(gson.fromJson<SpawnSet>(jsonElement))
|
||||
// } catch (exception: Exception) {
|
||||
// exception.printStackTrace()
|
||||
// }
|
||||
// }
|
||||
// return sets
|
||||
// }
|
||||
}
|
||||
@@ -20,14 +20,11 @@ import java.nio.file.Path
|
||||
* @since January 27th, 2022
|
||||
*/
|
||||
class SpawnSet : Iterable<SpawnDetail>, ModDependant {
|
||||
var id = ""
|
||||
|
||||
var enabled = true
|
||||
var version = "1"
|
||||
var preventOverwrite = false
|
||||
override var neededInstalledMods = listOf<String>()
|
||||
override var neededUninstalledMods = listOf<String>()
|
||||
var spawns = mutableListOf<SpawnDetail>()
|
||||
@Transient
|
||||
lateinit var path: Path
|
||||
|
||||
fun isEnabled(): Boolean = enabled && isModDependencySatisfied()
|
||||
|
||||
+2
-3
@@ -12,8 +12,7 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
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
|
||||
import com.cobblemon.mod.common.pokemon.feature.SEASON
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
@@ -37,7 +36,7 @@ class PokemonSpawnAction(
|
||||
props.shiny = Cobblemon.config.shinyRate.takeIf { it >= 1 }?.let { Random.Default.nextFloat() < 1 / it }
|
||||
}
|
||||
val entity = props.createEntity(ctx.world)
|
||||
entity.pokemon.getFeature<SeasonFeature>(SEASON)?.update(entity.pokemon, ctx.world, ctx.position)
|
||||
SeasonFeatureHandler.updateSeason(entity.pokemon, ctx.world, ctx.position)
|
||||
entity.drops = detail.drops
|
||||
return entity
|
||||
}
|
||||
|
||||
@@ -8,11 +8,23 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.detail
|
||||
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnLoader
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnSet
|
||||
import com.cobblemon.mod.common.api.spawning.condition.FinalPrecalculationResult
|
||||
import com.cobblemon.mod.common.api.spawning.condition.PrecalculationResult
|
||||
import com.cobblemon.mod.common.api.spawning.condition.RootPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningPrecalculation
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* A collection of [SpawnDetail]s with precalculation logic for optimization of searches.
|
||||
@@ -25,7 +37,22 @@ import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
* @author Hiroku
|
||||
* @since February 9th, 2022
|
||||
*/
|
||||
class SpawnPool : Iterable<SpawnDetail> {
|
||||
class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDetail> {
|
||||
override val id = cobblemonResource("spawn_pool_$name")
|
||||
override val type = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<SpawnPool>()
|
||||
override val gson: Gson = SpawnLoader.gson
|
||||
override val typeToken = TypeToken.get(SpawnSet::class.java)
|
||||
override val resourcePath = id.path
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun reload(data: Map<Identifier, SpawnSet>) {
|
||||
details.clear()
|
||||
for (set in data.values) {
|
||||
details.addAll(set.filter { it.isModDependencySatisfied() })
|
||||
}
|
||||
precalculate()
|
||||
}
|
||||
|
||||
val details = mutableListOf<SpawnDetail>()
|
||||
var precalculation: PrecalculationResult<*> = RootPrecalculation.generate(details, emptyList())
|
||||
val precalculators = mutableListOf<SpawningPrecalculation<*>>()
|
||||
@@ -75,8 +102,8 @@ class SpawnPool : Iterable<SpawnDetail> {
|
||||
* Creates a de-referenced copy of the pool which can be modified safely without
|
||||
* this pool being changed.
|
||||
*/
|
||||
fun copy(): SpawnPool {
|
||||
val copy = SpawnPool()
|
||||
fun copy(newName: String): SpawnPool {
|
||||
val copy = SpawnPool(newName)
|
||||
copy.details.addAll(details)
|
||||
copy.precalculators.addAll(precalculators)
|
||||
copy.precalculation = precalculation
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ class BestSpawnerConfig {
|
||||
|
||||
fun load(): BestSpawnerConfig {
|
||||
val internal = loadInternal()
|
||||
if (Cobblemon.config.exportSpawnConfigToConfig) {
|
||||
if (Cobblemon.config.exportSpawnConfig) {
|
||||
val external = loadExternal()
|
||||
return if (external == null) {
|
||||
saveExternal()
|
||||
|
||||
+2
-94
@@ -8,39 +8,16 @@
|
||||
|
||||
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.asset.JsonManifestWalker
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
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.condition.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.context.RegisteredSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
|
||||
import com.cobblemon.mod.common.util.AssetLoading
|
||||
import com.cobblemon.mod.common.util.MergeMode
|
||||
import com.cobblemon.mod.common.util.adapters.BiomeLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.BlockLikeConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.RegisteredSpawningContextAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnDetailAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawnDetailPresetAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpawningConditionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.TimeRangeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.world.biome.Biome
|
||||
|
||||
/**
|
||||
* Base class for spawn detail presets. Presets are a spawn loading mechanism that allows various properties to be
|
||||
@@ -48,7 +25,7 @@ import net.minecraft.world.biome.Biome
|
||||
* to shortcut the process of commonly used conditions and other [SpawnDetail] properties as well as make those
|
||||
* commonly used properties very easy to maintain.
|
||||
*
|
||||
* A subclass of this base must be registered using [SpawnDetailPreset.registerPresetType].
|
||||
* A subclass of this base must be registered using [SpawnDetailPresets.registerPresetType].
|
||||
*
|
||||
* Preset loading occurs during initialization and first will load the internal presets. Then the external
|
||||
* config/cobblemon/spawning/presets directory and its child directories will be searched for presets.
|
||||
@@ -63,75 +40,6 @@ import net.minecraft.world.biome.Biome
|
||||
* @since July 8th, 2022
|
||||
*/
|
||||
abstract class SpawnDetailPreset {
|
||||
companion object {
|
||||
val GSON = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.setLenient()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
|
||||
.registerTypeAdapter(RegisteredSpawningContext::class.java, RegisteredSpawningContextAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(SpawnDetailPreset::class.java, SpawnDetailPresetAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(SpawningCondition::class.java, SpawningConditionAdapter)
|
||||
.registerTypeAdapter(TimeRange::class.java, TimeRangeAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.create()
|
||||
|
||||
val presetTypes = mutableMapOf<String, Class<out SpawnDetailPreset>>()
|
||||
fun <T : SpawnDetailPreset> registerPresetType(name: String, detailClass: Class<T>) {
|
||||
presetTypes[name] = detailClass
|
||||
}
|
||||
|
||||
fun load(): MutableMap<String, SpawnDetailPreset> {
|
||||
val map = mutableMapOf<String, SpawnDetailPreset>()
|
||||
map.putAll(loadInternal())
|
||||
map.putAll(loadExternal())
|
||||
return map
|
||||
}
|
||||
|
||||
fun loadInternal(): MutableMap<String, SpawnDetailPreset> {
|
||||
val map = mutableMapOf<String, SpawnDetailPreset>()
|
||||
try {
|
||||
val presets = JsonManifestWalker.load(
|
||||
SpawnDetailPreset::class.java,
|
||||
"spawning/presets",
|
||||
GSON
|
||||
)
|
||||
for (template in presets) {
|
||||
map[template.name] = template
|
||||
}
|
||||
return map
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Error loading internal spawn detail presets", e)
|
||||
}
|
||||
return mutableMapOf()
|
||||
}
|
||||
|
||||
fun loadExternal(): MutableMap<String, SpawnDetailPreset> {
|
||||
val files = mutableListOf<File>()
|
||||
val map = mutableMapOf<String, SpawnDetailPreset>()
|
||||
AssetLoading.searchFor(
|
||||
dir = "config/${Cobblemon.MODID}/spawning/presets",
|
||||
suffix = ".json",
|
||||
list = files
|
||||
)
|
||||
files.forEach {
|
||||
try {
|
||||
val reader = FileReader(it)
|
||||
val preset = GSON.fromJson<SpawnDetailPreset>(reader)
|
||||
reader.close()
|
||||
map[preset.name] = preset
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Unable to load preset from file: ${it.name}", e)
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
}
|
||||
|
||||
var name = ""
|
||||
var bucket: SpawnBucket? = null
|
||||
var spawnDetailType: String? = null
|
||||
var context: RegisteredSpawningContext<*>? = null
|
||||
|
||||
+1
-2
@@ -46,7 +46,6 @@ abstract class TickingSpawner(
|
||||
|
||||
var active = true
|
||||
val spawnedEntities = mutableListOf<Entity>()
|
||||
var maximumSpawned = Cobblemon.config.maximumSpawnedPokemonPerPlayer
|
||||
|
||||
var lastSpawnTime = 0L
|
||||
var ticksUntilNextSpawn = 100F
|
||||
@@ -66,7 +65,7 @@ abstract class TickingSpawner(
|
||||
removalCheckTicks = 0
|
||||
}
|
||||
|
||||
if (!active || spawnedEntities.size >= maximumSpawned) {
|
||||
if (!active) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ class BattleOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.g
|
||||
colour = Triple(r, g, b),
|
||||
opacity = opacity.toFloat(),
|
||||
ballState = activeBattlePokemon.ballCapturing,
|
||||
trueHealth = truePokemon?.let { (it.currentHealth * battlePokemon.hpRatio).roundToInt() to (it.hp * battlePokemon.hpRatio).roundToInt() }
|
||||
trueHealth = truePokemon?.let { (it.hp * battlePokemon.hpRatio).roundToInt() to it.hp }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -8,10 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.client.model.*
|
||||
|
||||
class TexturedModel {
|
||||
@SerializedName("format_version")
|
||||
val formatVersion: String = "0"
|
||||
@@ -142,11 +142,11 @@ class TexturedModel {
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
fun from(path: String) : TexturedModel {
|
||||
fun from(json: String) : TexturedModel {
|
||||
try {
|
||||
return GSON.fromJson(Cobblemon::class.java.getResourceAsStream("/assets/cobblemon/$path")!!.reader(), TexturedModel::class.java)
|
||||
return GSON.fromJson(json, TexturedModel::class.java)
|
||||
} catch (exception: Exception) {
|
||||
throw IllegalStateException("Issue loading pokemon geo: $path", exception)
|
||||
throw IllegalStateException("Issue loading pokemon geo: $json", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ class WaveAnimation<T : Entity>(
|
||||
|
||||
override fun setAngles(entity: T?, model: PoseableEntityModel<T>, state: PoseableEntityState<T>?, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, headYaw: Float, headPitch: Float) {
|
||||
val t = if (basedOnLimbSwing) {
|
||||
limbSwing / 20F
|
||||
limbSwing
|
||||
} else {
|
||||
entity?.let { model.getState(it).animationSeconds } ?: 0F
|
||||
}
|
||||
|
||||
+27
-8
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.repository
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.TexturedModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.JsonPokemonPoseableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1.AbraModel
|
||||
@@ -244,13 +245,14 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen8.Kle
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen8.SizzlipedeModel
|
||||
import com.cobblemon.mod.common.client.render.pokemon.ModelLayer
|
||||
import com.cobblemon.mod.common.client.render.pokemon.RegisteredSpeciesRendering
|
||||
import com.cobblemon.mod.common.client.render.pokemon.SpeciesAssetResolver
|
||||
import com.cobblemon.mod.common.client.render.pokemon.SpeciesVariationSet
|
||||
import com.cobblemon.mod.common.client.util.exists
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import java.io.File
|
||||
import java.nio.charset.StandardCharsets
|
||||
import net.minecraft.client.model.ModelPart
|
||||
@@ -507,6 +509,8 @@ object PokemonModelRepository : ModelRepository<PokemonEntity>() {
|
||||
posers[cobblemonResource(name)] = model
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun registerJsonPosers(resourceManager: ResourceManager) {
|
||||
resourceManager.findResources("bedrock/posers") { path -> path.endsWith(".json") }.forEach { identifier, resource ->
|
||||
resource.inputStream.use { stream ->
|
||||
@@ -521,32 +525,47 @@ object PokemonModelRepository : ModelRepository<PokemonEntity>() {
|
||||
}
|
||||
|
||||
fun registerSpeciesAssetResolvers(resourceManager: ResourceManager) {
|
||||
val speciesToSpeciesVariationSets = mutableMapOf<Identifier, MutableList<SpeciesVariationSet>>()
|
||||
resourceManager.findResources("bedrock/species") { path -> path.endsWith(".json") }.forEach { identifier, resource ->
|
||||
resource.inputStream.use { stream ->
|
||||
val json = String(stream.readAllBytes(), StandardCharsets.UTF_8)
|
||||
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
renders[resolvedIdentifier] = RegisteredSpeciesRendering(
|
||||
resolvedIdentifier,
|
||||
SpeciesAssetResolver.GSON.fromJson(json, SpeciesAssetResolver::class.java)
|
||||
)
|
||||
val speciesVariationSet = RegisteredSpeciesRendering.GSON.fromJson<SpeciesVariationSet>(json)
|
||||
speciesToSpeciesVariationSets.getOrPut(speciesVariationSet.species) { mutableListOf() }.add(speciesVariationSet)
|
||||
}
|
||||
}
|
||||
|
||||
for ((species, speciesVariationSets) in speciesToSpeciesVariationSets) {
|
||||
val variations = speciesVariationSets.sortedBy { it.order }.flatMap { it.variations }.toMutableList()
|
||||
renders[species] = RegisteredSpeciesRendering(species, variations)
|
||||
}
|
||||
|
||||
renders.values.forEach(RegisteredSpeciesRendering::initialize)
|
||||
}
|
||||
|
||||
override fun registerAll() {
|
||||
}
|
||||
|
||||
val texturedModels = mutableMapOf<Identifier, TexturedModel>()
|
||||
fun registerModels(resourceManager: ResourceManager) {
|
||||
resourceManager.findResources("bedrock/models") { path -> path.endsWith(".geo.json") }.forEach { identifier, resource ->
|
||||
resource.inputStream.use { stream ->
|
||||
val json = String(stream.readAllBytes(), StandardCharsets.UTF_8)
|
||||
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
texturedModels[resolvedIdentifier] = TexturedModel.from(json)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun reload(resourceManager: ResourceManager) {
|
||||
Cobblemon.LOGGER.info("Initializing Pokémon models")
|
||||
this.renders.clear()
|
||||
this.posers.clear()
|
||||
registerPosers(resourceManager)
|
||||
registerModels(resourceManager)
|
||||
registerSpeciesAssetResolvers(resourceManager)
|
||||
initializeModelLayers()
|
||||
}
|
||||
|
||||
var brokenOnes = mutableListOf<Species>()
|
||||
|
||||
fun getPoser(species: Species, aspects: Set<String>): PokemonPoseableModel {
|
||||
try {
|
||||
val poser = this.renders[species.resourceIdentifier]?.getPoser(aspects)
|
||||
|
||||
+72
-68
@@ -8,14 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.render.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.TexturedModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.Vec3fAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.Vector4fAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.util.Identifier
|
||||
@@ -30,74 +29,30 @@ import net.minecraft.util.math.Vector4f
|
||||
*/
|
||||
class RegisteredSpeciesRendering(
|
||||
val species: Identifier,
|
||||
private val assetResolver: SpeciesAssetResolver
|
||||
val variations: MutableList<ModelAssetVariation>
|
||||
) {
|
||||
val posers = mutableMapOf<Pair<Identifier, Identifier>, PokemonPoseableModel>()
|
||||
val models = mutableMapOf<Identifier, ModelPart>()
|
||||
|
||||
init {
|
||||
posers.clear()
|
||||
assetResolver.getAllModels().forEach { identifier ->
|
||||
models[identifier] = TexturedModel.from(identifier.path).create().createModel()
|
||||
}
|
||||
fun getResolvedPoser(aspects: Set<String>): Identifier {
|
||||
return getVariationValue(aspects) { poser }
|
||||
?: throw IllegalStateException("Unable to find a poser for $species with aspects ${aspects.joinToString()}. This shouldn't be possible if you've defined the fallback variation.")
|
||||
}
|
||||
|
||||
fun getPoser(aspects: Set<String>): PokemonPoseableModel {
|
||||
val poserName = assetResolver.getPoser(aspects)
|
||||
val poserSupplier = PokemonModelRepository.posers[poserName] ?: throw IllegalStateException("No poser found for name: $poserName")
|
||||
val modelName = assetResolver.getModel(aspects)
|
||||
val existingEntityModel = posers[poserName to modelName]
|
||||
return if (existingEntityModel != null) {
|
||||
existingEntityModel
|
||||
} else {
|
||||
val entityModel = poserSupplier(models[modelName]!!)
|
||||
entityModel.registerPoses()
|
||||
posers[poserName to modelName] = entityModel
|
||||
entityModel
|
||||
}
|
||||
fun getResolvedModel(aspects: Set<String>): Identifier {
|
||||
return getVariationValue(aspects) { model }
|
||||
?: throw IllegalStateException("Unable to find a model for $species with aspects ${aspects.joinToString()}. This shouldn't be possible if you've defined the fallback variation.")
|
||||
}
|
||||
|
||||
fun getTexture(aspects: Set<String>): Identifier {
|
||||
PokemonModelRepository.posers[assetResolver.getPoser(aspects)] ?: throw IllegalStateException("No poser for $species")
|
||||
return assetResolver.getTexture(aspects)
|
||||
fun getResolvedTexture(aspects: Set<String>): Identifier {
|
||||
return getVariationValue(aspects) { texture }
|
||||
?: throw IllegalStateException("Unable to find a texture for $species with aspects ${aspects.joinToString()}. This shouldn't be possible if you've defined the fallback variation.")
|
||||
}
|
||||
|
||||
fun getLayers(aspects: Set<String>): List<ModelLayer> {
|
||||
PokemonModelRepository.posers[assetResolver.getPoser(aspects)] ?: throw IllegalStateException("No poser for $species")
|
||||
return assetResolver.getLayers(aspects)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A resolver of species assets. This takes a set of assets supplied by the registered [AspectProvider]s and
|
||||
* is used to produce the most appropriate animator class, model, and texture by searching for the last matching
|
||||
* [ModelAssetVariation]. It will fall back to the top-level properties if none match the aspect conditions.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 14th, 2022
|
||||
*/
|
||||
class SpeciesAssetResolver {
|
||||
val poser = Identifier("")
|
||||
val model = Identifier("")
|
||||
val texture = Identifier("")
|
||||
val layers: List<ModelLayer>? = null
|
||||
val variations = mutableListOf<ModelAssetVariation>()
|
||||
|
||||
fun getPoser(aspects: Set<String>): Identifier {
|
||||
return variations.lastOrNull { it.aspects.all { it in aspects } && it.poser != null }?.poser ?: poser
|
||||
private fun <T> getVariationValue(aspects: Set<String>, selector: ModelAssetVariation.() -> T?): T? {
|
||||
return variations.lastOrNull { it.aspects.all { it in aspects } && selector(it) != null }?.let(selector)
|
||||
}
|
||||
|
||||
fun getModel(aspects: Set<String>): Identifier {
|
||||
return variations.lastOrNull { it.aspects.all { it in aspects } && it.model != null }?.model ?: model
|
||||
}
|
||||
|
||||
fun getTexture(aspects: Set<String>): Identifier {
|
||||
return variations.lastOrNull { it.aspects.all { it in aspects } && it.texture != null }?.texture ?: texture
|
||||
}
|
||||
|
||||
fun getLayers(aspects: Set<String>): List<ModelLayer> {
|
||||
fun getResolvedLayers(aspects: Set<String>): List<ModelLayer> {
|
||||
val allLayers = mutableListOf<ModelLayer>()
|
||||
layers?.let(allLayers::addAll)
|
||||
variations[0].layers?.let(allLayers::addAll)
|
||||
|
||||
val variationLayers = variations.lastOrNull { it.aspects.all { it in aspects } && it.layers != null }?.layers ?: emptyList()
|
||||
variationLayers.forEach { layer ->
|
||||
@@ -112,7 +67,6 @@ class SpeciesAssetResolver {
|
||||
|
||||
fun getAllModels(): Set<Identifier> {
|
||||
val models = mutableSetOf<Identifier>()
|
||||
models.add(model)
|
||||
for (variation in variations) {
|
||||
if (variation.model != null) {
|
||||
models.add(variation.model)
|
||||
@@ -131,10 +85,59 @@ class SpeciesAssetResolver {
|
||||
.setLenient()
|
||||
.create()
|
||||
}
|
||||
|
||||
val posers = mutableMapOf<Pair<Identifier, Identifier>, PokemonPoseableModel>()
|
||||
val models = mutableMapOf<Identifier, ModelPart>()
|
||||
|
||||
fun initialize() {
|
||||
posers.clear()
|
||||
getAllModels().forEach { identifier ->
|
||||
models[identifier] = PokemonModelRepository.texturedModels[identifier]!!.create().createModel()
|
||||
}
|
||||
}
|
||||
|
||||
fun getPoser(aspects: Set<String>): PokemonPoseableModel {
|
||||
val poserName = getResolvedPoser(aspects)
|
||||
val poserSupplier = PokemonModelRepository.posers[poserName] ?: throw IllegalStateException("No poser found for name: $poserName")
|
||||
val modelName = getResolvedModel(aspects)
|
||||
val existingEntityModel = posers[poserName to modelName]
|
||||
return if (existingEntityModel != null) {
|
||||
existingEntityModel
|
||||
} else {
|
||||
val entityModel = poserSupplier(models[modelName]!!)
|
||||
entityModel.registerPoses()
|
||||
posers[poserName to modelName] = entityModel
|
||||
entityModel
|
||||
}
|
||||
}
|
||||
|
||||
fun getTexture(aspects: Set<String>): Identifier {
|
||||
PokemonModelRepository.posers[getResolvedPoser(aspects)] ?: throw IllegalStateException("No poser for $species")
|
||||
return getResolvedTexture(aspects)
|
||||
}
|
||||
|
||||
fun getLayers(aspects: Set<String>): List<ModelLayer> {
|
||||
PokemonModelRepository.posers[getResolvedPoser(aspects)] ?: throw IllegalStateException("No poser for $species")
|
||||
return getResolvedLayers(aspects)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A variation to the base species, which can overwrite the animator, model, texture, or any combination.
|
||||
* A set of species variations. This is essentially a prioritized list of [ModelAssetVariation]s for a species, with
|
||||
* an [order] property to control the priority of this set compared to other sets.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since December 4th, 2022
|
||||
*/
|
||||
class SpeciesVariationSet(
|
||||
val species: Identifier = cobblemonResource("pokemon"),
|
||||
val order: Int = 0,
|
||||
val variations: MutableList<ModelAssetVariation> = mutableListOf()
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* A variation to the base species, which can overwrite the poser, model, texture, or any combination of the above.
|
||||
* It contains a set of aspects that must ALL be present on a renderable for this variation to be considered.
|
||||
* If a later variation also matches, but provides different properties, both this and the other variation will
|
||||
* be used for their respective non-null properties.
|
||||
@@ -142,13 +145,14 @@ class SpeciesAssetResolver {
|
||||
* @author Hiroku
|
||||
* @since May 14th, 2022
|
||||
*/
|
||||
class ModelAssetVariation {
|
||||
val aspects = mutableSetOf<String>()
|
||||
val poser: Identifier? = null
|
||||
val model: Identifier? = null
|
||||
val texture: Identifier? = null
|
||||
class ModelAssetVariation(
|
||||
val aspects: MutableSet<String> = mutableSetOf(),
|
||||
val poser: Identifier? = null,
|
||||
val model: Identifier? = null,
|
||||
val texture: Identifier? = null,
|
||||
val layers: List<ModelLayer>? = null
|
||||
}
|
||||
)
|
||||
|
||||
class ModelLayer {
|
||||
val name: String = ""
|
||||
val scale: Vec3f = Vec3f(1F, 1F, 1F)
|
||||
|
||||
@@ -39,14 +39,18 @@ object SpawnPokemon {
|
||||
private fun execute(context: CommandContext<ServerCommandSource>) : Int {
|
||||
val entity = context.source.entity
|
||||
if (entity is ServerPlayerEntity && !entity.world.isClient) {
|
||||
val pkm = PokemonPropertiesArgumentType.getPokemonProperties(context, PROPERTIES)
|
||||
if (pkm.species == null) {
|
||||
entity.sendMessage(commandLang("${NAME}.nospecies").red())
|
||||
return Command.SINGLE_SUCCESS
|
||||
try {
|
||||
val pkm = PokemonPropertiesArgumentType.getPokemonProperties(context, PROPERTIES)
|
||||
if (pkm.species == null) {
|
||||
entity.sendMessage(commandLang("${NAME}.nospecies").red())
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
val pokemonEntity = pkm.createEntity(entity.world)
|
||||
entity.world.spawnEntity(pokemonEntity)
|
||||
pokemonEntity.setPosition(entity.pos)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
val pokemonEntity = pkm.createEntity(entity.world)
|
||||
entity.world.spawnEntity(pokemonEntity)
|
||||
pokemonEntity.setPosition(entity.pos)
|
||||
}
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
|
||||
@@ -90,13 +90,7 @@ class CobblemonConfig {
|
||||
var maximumSliceDistanceFromPlayer = 16 * 4F
|
||||
|
||||
@NodeCategory(Category.Spawning)
|
||||
var maximumSpawnedPokemonPerPlayer = 30
|
||||
|
||||
@NodeCategory(Category.Spawning)
|
||||
var exportSpawnsToConfig = false
|
||||
|
||||
@NodeCategory(Category.Spawning)
|
||||
var exportSpawnConfigToConfig = false
|
||||
var exportSpawnConfig = false
|
||||
|
||||
@NodeCategory(Category.Battles)
|
||||
var autoUpdateShowdown = true
|
||||
@@ -144,12 +138,6 @@ class CobblemonConfig {
|
||||
@NodeCategory(Category.Spawning)
|
||||
var baseApricornTreeGenerationChance = 0.1F
|
||||
|
||||
@NodeCategory(Category.Pokemon)
|
||||
var globalFlagSpeciesFeatures = mutableListOf<String>()
|
||||
|
||||
@NodeCategory(Category.Pokemon)
|
||||
var flagSpeciesFeatures = mutableListOf("sunglasses")
|
||||
|
||||
@NodeCategory(Category.Pokemon)
|
||||
var ninjaskCreatesShedinja = true
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
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.SpawnDetailPresets
|
||||
import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
|
||||
import dev.architectury.registry.ReloadListenerRegistry
|
||||
import java.util.UUID
|
||||
@@ -24,7 +29,7 @@ import net.minecraft.resource.SynchronousResourceReloader
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
internal object CobblemonDataProvider : DataProvider {
|
||||
object CobblemonDataProvider : DataProvider {
|
||||
|
||||
// Both Forge n Fabric keep insertion order so if a registry depends on another simply register it after
|
||||
var canReload = true
|
||||
@@ -38,12 +43,18 @@ internal object CobblemonDataProvider : DataProvider {
|
||||
this.register(Abilities)
|
||||
this.register(PokemonSpecies)
|
||||
this.register(PokeBalls)
|
||||
this.register(SpeciesFeatures)
|
||||
this.register(GlobalSpeciesFeatures)
|
||||
this.register(PropertiesCompletionProvider)
|
||||
this.register(SpeciesFeatureAssignments)
|
||||
this.register(SpawnDetailPresets)
|
||||
|
||||
CobblemonSpawnPools.load()
|
||||
|
||||
CobblemonEvents.PLAYER_QUIT.subscribe { synchronizedPlayerIds.remove(it.uuid) }
|
||||
}
|
||||
|
||||
override fun register(registry: DataRegistry) {
|
||||
override fun <T : DataRegistry> register(registry: T): T {
|
||||
// Only send message once
|
||||
if (this.registries.isEmpty()) {
|
||||
LOGGER.info("Note: Cobblemon data registries are only loaded once per server instance as Pokémon species are not safe to reload.")
|
||||
@@ -52,6 +63,7 @@ internal object CobblemonDataProvider : DataProvider {
|
||||
this.registries.add(registry)
|
||||
LOGGER.info("Registered the {} registry", registry.id.toString())
|
||||
LOGGER.debug("Registered the {} registry of class {}", registry.id.toString(), registry::class.qualifiedName)
|
||||
return registry
|
||||
}
|
||||
|
||||
override fun fromIdentifier(registryIdentifier: Identifier): DataRegistry? = this.registries.find { it.id == registryIdentifier }
|
||||
|
||||
@@ -84,6 +84,8 @@ class FormData(
|
||||
private var _dynamaxBlocked: Boolean? = null,
|
||||
@SerializedName("eggGroups")
|
||||
private val _eggGroups: Set<EggGroup>? = null,
|
||||
@SerializedName("eggCycles")
|
||||
private val _eggCycles: Int? = null,
|
||||
@SerializedName("height")
|
||||
private var _height: Float? = null,
|
||||
@SerializedName("weight")
|
||||
@@ -156,6 +158,9 @@ class FormData(
|
||||
val eggGroups: Set<EggGroup>
|
||||
get() = _eggGroups ?: species.eggGroups
|
||||
|
||||
val eggCycles: Int
|
||||
get() = _eggCycles ?: species.eggCycles
|
||||
|
||||
/**
|
||||
* The height in decimeters
|
||||
*/
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceSource
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.friendship.FriendshipMutationCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.labels.CobblemonPokemonLabels
|
||||
import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
|
||||
@@ -81,8 +82,7 @@ import com.cobblemon.mod.common.pokemon.activestate.PokemonState
|
||||
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
|
||||
import com.cobblemon.mod.common.pokemon.evolution.CobblemonEvolutionProxy
|
||||
import com.cobblemon.mod.common.pokemon.feature.DamageTakenFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.SEASON
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
@@ -125,13 +125,10 @@ open class Pokemon {
|
||||
throw IllegalArgumentException("Cannot set a species that isn't registered")
|
||||
}
|
||||
val quotient = clamp(currentHealth / hp.toFloat(), 0F, 1F)
|
||||
val previousFeatureKeys = features.map { it.name }.toSet()
|
||||
field = value
|
||||
val newFeatureKeys = species.features + Cobblemon.config.globalFlagSpeciesFeatures + SpeciesFeature.globalFeatures().keys
|
||||
val addedFeatures = newFeatureKeys - previousFeatureKeys
|
||||
val removedFeatures = previousFeatureKeys - newFeatureKeys
|
||||
features.addAll(addedFeatures.mapNotNull { SpeciesFeature.get(it)?.invoke() })
|
||||
features.removeAll { it.name in removedFeatures }
|
||||
val newFeatures = SpeciesFeatures.getFeaturesFor(species).mapNotNull { it.invoke(this) }
|
||||
features.clear()
|
||||
features.addAll(newFeatures)
|
||||
this.evolutionProxy.current().clear()
|
||||
updateAspects()
|
||||
updateForm()
|
||||
@@ -364,7 +361,7 @@ open class Pokemon {
|
||||
open fun getStat(stat: Stat) = Cobblemon.statProvider.getStatForPokemon(this, stat)
|
||||
|
||||
fun sendOut(level: ServerWorld, position: Vec3d, mutation: (PokemonEntity) -> Unit = {}): PokemonEntity {
|
||||
getFeature<SeasonFeature>(SEASON)?.update(this, level, position.toBlockPos())
|
||||
SeasonFeatureHandler.updateSeason(this, level, position.toBlockPos())
|
||||
val entity = PokemonEntity(level, this)
|
||||
entity.setPositionSafely(position)
|
||||
mutation(entity)
|
||||
|
||||
@@ -137,6 +137,7 @@ class Species : ClientDataSynchronizer<Species> {
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeIdentifier(this.resourceIdentifier)
|
||||
buffer.writeBoolean(this.implemented)
|
||||
buffer.writeString(this.name)
|
||||
buffer.writeInt(this.nationalPokedexNumber)
|
||||
buffer.writeMap(this.baseStats,
|
||||
@@ -159,6 +160,7 @@ class Species : ClientDataSynchronizer<Species> {
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
this.implemented = buffer.readBoolean()
|
||||
// identifier is decoded in the sync packet for easier debug log
|
||||
this.name = buffer.readString()
|
||||
this.nationalPokedexNumber = buffer.readInt()
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.aspects
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.feature.FISH_STRIPES
|
||||
import com.cobblemon.mod.common.pokemon.feature.FishStripesFeature
|
||||
|
||||
object FishStripesAspect : AspectProvider {
|
||||
fun provideForFeature(feature: FishStripesFeature) = setOf("${feature.enumValue.name.lowercase()}striped")
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return provideForFeature(pokemon.getFeature(FISH_STRIPES) ?: return emptySet())
|
||||
}
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return provideForFeature(properties.customProperties.filterIsInstance<FishStripesFeature>().firstOrNull() ?: return emptySet())
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.aspects
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.feature.SEASON
|
||||
import com.cobblemon.mod.common.pokemon.feature.SNAKE_PATTERN
|
||||
import com.cobblemon.mod.common.pokemon.feature.SeasonFeature
|
||||
import com.cobblemon.mod.common.pokemon.feature.SnakePatternFeature
|
||||
|
||||
object SeasonAspect : AspectProvider {
|
||||
fun provideForFeature(feature: SeasonFeature) = setOf(feature.enumValue.name.lowercase())
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return provideForFeature(pokemon.getFeature(SEASON) ?: return emptySet())
|
||||
}
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return provideForFeature(properties.customProperties.filterIsInstance<SeasonFeature>().firstOrNull() ?: return emptySet())
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.aspects
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.feature.SNAKE_PATTERN
|
||||
import com.cobblemon.mod.common.pokemon.feature.SnakePatternFeature
|
||||
|
||||
object SnakePatternAspect : AspectProvider {
|
||||
fun provideForFeature(feature: SnakePatternFeature) = setOf("$SNAKE_PATTERN-${feature.enumValue.name.lowercase()}")
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return provideForFeature(pokemon.getFeature(SNAKE_PATTERN) ?: return emptySet())
|
||||
}
|
||||
|
||||
override fun provide(properties: PokemonProperties): Set<String> {
|
||||
return provideForFeature(properties.customProperties.filterIsInstance<SnakePatternFeature>().firstOrNull() ?: return emptySet())
|
||||
}
|
||||
}
|
||||
@@ -62,10 +62,10 @@ class SlowFallEffect : ShoulderEffect {
|
||||
|
||||
override fun update(entity: LivingEntity, overwriteCallback: Runnable?): Boolean {
|
||||
entity as ServerPlayerEntity
|
||||
if (isShoulderedPokemon(entity.shoulderEntityLeft) || isShoulderedPokemon(entity.shoulderEntityRight)) {
|
||||
duration = 10
|
||||
duration = if (isShoulderedPokemon(entity.shoulderEntityLeft) || isShoulderedPokemon(entity.shoulderEntityRight)) {
|
||||
10
|
||||
} else {
|
||||
duration = 0
|
||||
0
|
||||
}
|
||||
return super.update(entity, overwriteCallback)
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.feature.FishStripes.Companion.ALL_VALUES
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.EnumSpeciesFeature
|
||||
import java.util.EnumSet
|
||||
|
||||
/**
|
||||
* Fish stripes, Basculin.
|
||||
*/
|
||||
enum class FishStripes {
|
||||
BLUE,
|
||||
RED;
|
||||
|
||||
companion object {
|
||||
val ALL_VALUES = EnumSet.allOf(FishStripes::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
const val FISH_STRIPES = "striped"
|
||||
class FishStripesFeature : EnumSpeciesFeature<FishStripes>() {
|
||||
override val name: String = FISH_STRIPES
|
||||
override fun getValues() = ALL_VALUES
|
||||
}
|
||||
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.SeasonResolver
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.EnumSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.StringSpeciesFeature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.tags.CobblemonBiomeTags
|
||||
import java.util.EnumSet
|
||||
@@ -18,7 +18,7 @@ import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
/**
|
||||
* A season variation of a Pokémon.
|
||||
* A season. You know the ones.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 25th, 2022
|
||||
@@ -35,17 +35,16 @@ enum class CobblemonSeason {
|
||||
}
|
||||
|
||||
const val SEASON = "season"
|
||||
class SeasonFeature : EnumSpeciesFeature<CobblemonSeason>() {
|
||||
override val name: String = SEASON
|
||||
override fun getValues() = CobblemonSeason.ALL_VALUES
|
||||
|
||||
fun update(pokemon: Pokemon, world: WorldAccess, pos: BlockPos) {
|
||||
val currentSeason = enumValue
|
||||
val newSeason = Cobblemon.seasonResolver(world, pos)
|
||||
object SeasonFeatureHandler {
|
||||
fun updateSeason(pokemon: Pokemon, world: WorldAccess, pos: BlockPos) {
|
||||
val feature = pokemon.getFeature<StringSpeciesFeature>(SEASON) ?: return
|
||||
val currentSeason = feature.value
|
||||
val newSeason = Cobblemon.seasonResolver(world, pos)?.name?.lowercase()
|
||||
if (currentSeason != newSeason && newSeason != null) {
|
||||
enumValue = newSeason
|
||||
feature.value = newSeason
|
||||
pokemon.updateAspects()
|
||||
pokemon.markFeatureDirty(this)
|
||||
pokemon.markFeatureDirty(feature)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.EnumSpeciesFeature
|
||||
import java.util.EnumSet
|
||||
|
||||
/**
|
||||
* A pattern on a snake Pokémon, typically Arbok.
|
||||
*/
|
||||
enum class SnakePattern {
|
||||
CLASSIC,
|
||||
LEGACY,
|
||||
ATTACK,
|
||||
DARK,
|
||||
ELUSIVE,
|
||||
HEART,
|
||||
SPEED,
|
||||
SOUND;
|
||||
|
||||
companion object {
|
||||
val ALL_VALUES = EnumSet.allOf(SnakePattern::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
const val SNAKE_PATTERN = "snake-pattern"
|
||||
class SnakePatternFeature : EnumSpeciesFeature<SnakePattern>() {
|
||||
override val name: String = SNAKE_PATTERN
|
||||
override fun getValues() = SnakePattern.ALL_VALUES
|
||||
}
|
||||
+4
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.pokemon.properties
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
|
||||
@@ -125,6 +127,8 @@ internal object PropertiesCompletionProvider : DataRegistry {
|
||||
this.inject(setOf("shiny", "s"), setOf("yes", "no"))
|
||||
this.inject(setOf("gender"), Gender.values().map { it.name.lowercase() })
|
||||
this.inject(setOf("friendship"), setOf("0", Cobblemon.config.maxPokemonLevel.toString()))
|
||||
this.inject(setOf("pokeball"), PokeBalls.all().map { if (it.name.namespace == Cobblemon.MODID) it.name.path else it.name.toString() })
|
||||
this.inject(setOf("nature"), Natures.all().map { if (it.name.namespace == Cobblemon.MODID) it.name.path else it.name.toString() })
|
||||
}
|
||||
|
||||
private fun addCustom() {
|
||||
|
||||
@@ -10,11 +10,13 @@ package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
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.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.singularToPluralList
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
@@ -36,7 +38,7 @@ object SpawnDetailAdapter : JsonDeserializer<SpawnDetail> {
|
||||
element.singularToPluralList("preset")
|
||||
val presetNames = element.get("presets")?.asJsonArray?.map { it.asString }?.toMutableSet() ?: mutableSetOf()
|
||||
val presets = presetNames.mapNotNull {
|
||||
val preset = Cobblemon.bestSpawner.presets[it]
|
||||
val preset = SpawnDetailPresets.presets[it.asIdentifierDefaultingNamespace()]
|
||||
if (preset == null) {
|
||||
LOGGER.error("Unknown preset name: $it.")
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
|
||||
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
|
||||
import com.cobblemon.mod.common.api.spawning.preset.SpawnDetailPreset
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
@@ -26,7 +27,7 @@ object SpawnDetailPresetAdapter : JsonDeserializer<SpawnDetailPreset> {
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): SpawnDetailPreset {
|
||||
json as JsonObject
|
||||
val type = json.get("type")?.asString ?: BasicSpawnDetailPreset.NAME
|
||||
val clazz = SpawnDetailPreset.presetTypes[type] ?: throw IllegalStateException("Unrecognized preset type: $type")
|
||||
val clazz = SpawnDetailPresets.presetTypes[type] ?: throw IllegalStateException("Unrecognized preset type: $type")
|
||||
return ctx.deserialize(json, clazz)
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.pokemon.feature.SpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
* Adapter for [SpeciesFeatureProvider]s. This will rely on a 'type' property, which will be used as a lookup in
|
||||
* [SpeciesFeatures.types].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 30th, 2022
|
||||
*/
|
||||
object SpeciesFeatureProviderAdapter : JsonDeserializer<SpeciesFeatureProvider<*>> {
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): SpeciesFeatureProvider<*> {
|
||||
val typeName = json.asJsonObject.get("type").asString
|
||||
val clazz = SpeciesFeatures.types[typeName] ?: throw IllegalArgumentException("No type registered in SpeciesFeatures for name: $typeName")
|
||||
return ctx.deserialize(json, clazz)
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,23 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import java.lang.reflect.Type
|
||||
import net.minecraft.util.math.Vec3f
|
||||
|
||||
object Vec3fAdapter : JsonDeserializer<Vec3f> {
|
||||
object Vec3fAdapter : JsonDeserializer<Vec3f>, JsonSerializer<Vec3f> {
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): Vec3f {
|
||||
json as JsonArray
|
||||
return Vec3f(json[0].asFloat, json[1].asFloat, json[2].asFloat)
|
||||
}
|
||||
|
||||
override fun serialize(src: Vec3f, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
|
||||
return JsonArray()
|
||||
.also {
|
||||
it.add(src.x)
|
||||
it.add(src.y)
|
||||
it.add(src.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,24 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import java.lang.reflect.Type
|
||||
import net.minecraft.util.math.Vector4f
|
||||
|
||||
object Vector4fAdapter : JsonDeserializer<Vector4f> {
|
||||
object Vector4fAdapter : JsonDeserializer<Vector4f>, JsonSerializer<Vector4f> {
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): Vector4f {
|
||||
json as JsonArray
|
||||
return Vector4f(json[0].asFloat, json[1].asFloat, json[2].asFloat, json[3].asFloat)
|
||||
}
|
||||
|
||||
override fun serialize(src: Vector4f, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
|
||||
return JsonArray()
|
||||
.also {
|
||||
it.add(src.x)
|
||||
it.add(src.y)
|
||||
it.add(src.z)
|
||||
it.add(src.w)
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:substitute",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:substitute",
|
||||
"model": "cobblemon:substitute.geo",
|
||||
"texture": "cobblemon:textures/pokemon/substitute.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/substitute_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:bulbasaur",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:bulbasaur",
|
||||
"model": "cobblemon:bulbasaur.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0001_bulbasaur/bulbasaur.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0001_bulbasaur/bulbasaur_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:ivysaur",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:ivysaur",
|
||||
"model": "cobblemon:ivysaur.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0002_ivysaur/ivysaur.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0002_ivysaur/ivysaur_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:venusaur",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:venusaur",
|
||||
"model": "cobblemon:venusaur_male.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0003_venusaur/venusaur.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0003_venusaur/venusaur_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"species": "cobblemon:venusaur",
|
||||
"order": 1,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"female"
|
||||
],
|
||||
"model": "cobblemon:venusaur_female.geo"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:charmander",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:charmander",
|
||||
"model": "cobblemon:charmander.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0004_charmander/charmander.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0004_charmander/charmander_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:charmeleon",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:charmeleon",
|
||||
"model": "cobblemon:charmeleon.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0005_charmeleon/charmeleon.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0005_charmeleon/charmeleon_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:charizard",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:charizard",
|
||||
"model": "cobblemon:charizard.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0006_charizard/charizard.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0006_charizard/charizard_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:squirtle",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:squirtle",
|
||||
"model": "cobblemon:squirtle.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0007_squirtle/squirtle.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0007_squirtle/squirtle_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:wartortle",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:wartortle",
|
||||
"model": "cobblemon:wartortle.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0008_wartortle/wartortle.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0008_wartortle/wartortle_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:blastoise",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:blastoise",
|
||||
"model": "cobblemon:blastoise.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0009_blastoise/blastoise.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0009_blastoise/blastoise_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:caterpie",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:caterpie",
|
||||
"model": "cobblemon:caterpie.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0010_caterpie/caterpie.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0010_caterpie/caterpie_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:metapod",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:metapod",
|
||||
"model": "cobblemon:metapod.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0011_metapod/metapod.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0011_metapod/metapod_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:butterfree",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:butterfree",
|
||||
"model": "cobblemon:butterfree_male.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0012_butterfree/butterfree.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0012_butterfree/butterfree_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"species": "cobblemon:butterfree",
|
||||
"order": 1,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"female"
|
||||
],
|
||||
"model": "cobblemon:butterfree_female.geo"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:butterfree",
|
||||
"order": 2,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"valencian"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0012_butterfree/butterfree_valencian.png"
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"valencian",
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0012_butterfree/butterfree_valencian_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:weedle",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:weedle",
|
||||
"model": "cobblemon:weedle.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0013_weedle/weedle.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0013_weedle/weedle_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:kakuna",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:kakuna",
|
||||
"model": "cobblemon:kakuna.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0014_kakuna/kakuna.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0014_kakuna/kakuna_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:beedrill",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:beedrill",
|
||||
"model": "cobblemon:beedrill.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0015_beedrill/beedrill.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0015_beedrill/beedrill_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:pidgey",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:pidgey",
|
||||
"model": "cobblemon:pidgey.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0016_pidgey/pidgey.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0016_pidgey/pidgey_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:pidgeotto",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:pidgeotto",
|
||||
"model": "cobblemon:pidgeotto.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0017_pidgeotto/pidgeotto.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0017_pidgeotto/pidgeotto_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:pidgeot",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:pidgeot",
|
||||
"model": "cobblemon:pidgeot.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0018_pidgeot/pidgeot.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0018_pidgeot/pidgeot_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:rattata",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:rattata",
|
||||
"model": "cobblemon:rattata_male.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0019_rattata/rattata.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0019_rattata/rattata_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"species": "cobblemon:rattata",
|
||||
"order": 1,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"female"
|
||||
],
|
||||
"model": "cobblemon:rattata_female.geo"
|
||||
}
|
||||
]
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"species": "cobblemon:rattata",
|
||||
"order": 2,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"alolan"
|
||||
],
|
||||
"model": "cobblemon:rattata_alolan.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0019_rattata/rattata_alolan.png"
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"alolan",
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0019_rattata/rattata_alolan_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:raticate",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:raticate",
|
||||
"model": "cobblemon:raticate_male.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0020_raticate/raticate.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0020_raticate/raticate_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"species": "cobblemon:raticate",
|
||||
"order": 1,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"female"
|
||||
],
|
||||
"model": "cobblemon:raticate_female.geo"
|
||||
}
|
||||
]
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"species": "cobblemon:raticate",
|
||||
"order": 2,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"alolan"
|
||||
],
|
||||
"model": "cobblemon:raticate_alolan.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0020_raticate/raticate_alolan.png"
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"alolan",
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0020_raticate/raticate_alolan_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:spearow",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:spearow",
|
||||
"model": "cobblemon:spearow.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0021_spearow/spearow.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0021_spearow/spearow_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:fearow",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:fearow",
|
||||
"model": "cobblemon:fearow.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0022_fearow/fearow.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0022_fearow/fearow_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:ekans",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:ekans",
|
||||
"model": "cobblemon:ekans.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0023_ekans/ekans.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0023_ekans/ekans_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"species": "cobblemon:arbok",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:arbok",
|
||||
"model": "cobblemon:arbok.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0024_arbok/arbok.png",
|
||||
"layers": [
|
||||
{
|
||||
"name": "pattern",
|
||||
"scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"tint": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0024_arbok/arbok_pattern_classic.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0024_arbok/arbok_shiny.png",
|
||||
"layers": [
|
||||
{
|
||||
"name": "pattern",
|
||||
"scale": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"tint": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0024_arbok/arbok_pattern_classic_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:pikachu",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:pikachu",
|
||||
"model": "cobblemon:pikachu_male.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0025_pikachu/pikachu.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0025_pikachu/pikachu_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"species": "cobblemon:pikachu",
|
||||
"order": 1,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"female"
|
||||
],
|
||||
"model": "cobblemon:pikachu_female.geo"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:raichu",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:raichu",
|
||||
"model": "cobblemon:raichu_male.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0026_raichu/raichu.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0026_raichu/raichu_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"species": "cobblemon:raichu",
|
||||
"order": 1,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [
|
||||
"female"
|
||||
],
|
||||
"model": "cobblemon:raichu_female.geo"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:sandshrew",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:sandshrew",
|
||||
"model": "cobblemon:sandshrew.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0027_sandshrew/sandshrew.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0027_sandshrew/sandshrew_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:sandslash",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:sandslash",
|
||||
"model": "cobblemon:sandslash.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0028_sandslash/sandslash.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0028_sandslash/sandslash_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:nidoranf",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:nidoranf",
|
||||
"model": "cobblemon:nidoranf.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0029_nidoranf/nidoranf.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0029_nidoranf/nidoranf_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:nidorina",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:nidorina",
|
||||
"model": "cobblemon:nidorina.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0030_nidorina/nidorina.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0030_nidorina/nidorina_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:nidoqueen",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:nidoqueen",
|
||||
"model": "cobblemon:nidoqueen.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0031_nidoqueen/nidoqueen.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0031_nidoqueen/nidoqueen_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:nidoranm",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:nidoranm",
|
||||
"model": "cobblemon:nidoranm.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0032_nidoranm/nidoranm.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0032_nidoranm/nidoranm_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:nidorino",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:nidorino",
|
||||
"model": "cobblemon:nidorino.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0033_nidorino/nidorino.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0033_nidorino/nidorino_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"species": "cobblemon:nidoking",
|
||||
"order": 0,
|
||||
"variations": [
|
||||
{
|
||||
"aspects": [],
|
||||
"poser": "cobblemon:nidoking",
|
||||
"model": "cobblemon:nidoking.geo",
|
||||
"texture": "cobblemon:textures/pokemon/0034_nidoking/nidoking.png",
|
||||
"layers": []
|
||||
},
|
||||
{
|
||||
"aspects": [
|
||||
"shiny"
|
||||
],
|
||||
"texture": "cobblemon:textures/pokemon/0034_nidoking/nidoking_shiny.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
Loaded 100 of 805 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user