Compare commits

...
31 Commits
Author SHA1 Message Date
Nick a0ffa4c565 Work on form registry appender 2022-12-20 17:06:26 -08:00
Licious17 6bf09dbd2a Merge remote-tracking branch 'origin/main' 2022-12-10 13:16:00 +00:00
Licious17 010f9b16f9 Fix Rockruff and Lycanroc data for gimmick form 2022-12-10 13:15:51 +00:00
Hiroku 470f0d37a4 Merge branch 'spawns-as-data' into 'main'
Moved spawn sets and spawn detail presets to data

See merge request cable-mc/cobblemon!228
2022-12-09 08:01:39 +00:00
Paul f7bd3af13b Licensing fix 2022-12-09 17:46:05 +10:00
Paul f4f7c0f19a Moved spawn sets and spawn detail presets to data 2022-12-09 17:30:34 +10:00
Paul 7ff0a66aae Removed faulty spawn cap 2022-12-09 00:00:23 +10:00
Hiroku 0487d431e1 Merge branch 'healer-advancements-redux' into 'main'
Updated Healing Machine Advancements

See merge request cable-mc/cobblemon!227
2022-12-08 09:42:37 +00:00
Hiroku 5429f70a26 Merge branch 'pkm-properties' into 'main'
Added pokeball and nature pokemon property

See merge request cable-mc/cobblemon!226
2022-12-08 09:05:55 +00:00
Licious17 aa8d0d80f1 Added pokeball and nature pokemon property 2022-12-06 12:49:12 +00:00
dhi_holo 9547be150b New Healing Machine Advancement
Removed full_heal and max_heal (dumb names, sorry), and made a (*slightly* jankily written, but fully functional) advancement which checks for you interacting with a healer with atleast one pokeball in it (any type is detected due to the stuff that makes it mildly janky looking).
A more optimized option advancement might be doable eventually, but this is the only thing I could detect to show the healer successfully being used.
Is possible to cheese it by interacting with a healer someone else is using, but shouldn't be an issue in the long run.

Updated en_us lang with new advancement, removing the prior two (the name from full_heal is kept, with a brand new description)
2022-12-06 20:43:00 +10:00
dhi_holo 346e3a3047 Update en_us.json
Fixed Calcuim (Calcium) misspelling
2022-12-06 16:28:11 +10:00
Licious 594bf59496 Merge branch 'aspect-json-rework' into 'main'
Aspect json rework

See merge request cable-mc/cobblemon!225
2022-12-05 15:11:12 +00:00
Paul 7f6fa2f009 Removed asset resolver converter code that is no longer needed 2022-12-05 19:51:11 +10:00
Paul b7720248ad Refactored asset resolver JSONs to be resource pack friendly 2022-12-05 19:49:35 +10:00
Hiroku 60593c7cdf Merge branch 'fix-aw-generation' into 'main'
Fix access widener generations on compile, remove cached AW

See merge request cable-mc/cobblemon!224
2022-12-05 02:50:58 +00:00
Nick 61e545ace3 Fix access widener generations on compile, remove cached AW 2022-12-04 15:28:19 -08:00
Hiroku 045398d204 Merge branch 'model-loading-fix' into 'main'
Model loading fix

See merge request cable-mc/cobblemon!223
2022-12-04 11:23:25 +00:00
Paul c4e318cb1c Updated aspect JSONs to use new model paths 2022-12-04 11:30:01 +10:00
Paul 048bae7585 Merge branch 'main' of gitlab.com:cable-mc/pokemon-cobbled into model-loading-fix 2022-12-03 18:48:46 +10:00
Hiroku fee858e5dc Merge branch 'better-aspects' into 'main'
Reworked species features, complete with new data pack stuff

See merge request cable-mc/cobblemon!222
2022-12-03 00:09:54 +00:00
Paul f938e99036 Merge branch 'main' of gitlab.com:cable-mc/pokemon-cobbled into better-aspects
# Conflicts:
#	CHANGELOG.md
2022-12-02 23:15:13 +10:00
Paul 02ecc87608 Fixed max hp display not being correct for allied pokemon 2022-12-02 23:14:34 +10:00
Paul 5a0d46410a Merge branch 'main' of gitlab.com:cable-mc/pokemon-cobbled into better-aspects
# Conflicts:
#	CHANGELOG.md
2022-12-02 23:05:19 +10:00
Paul e516b6dea2 Reworked species features, complete with new data pack stuff 2022-12-02 23:03:50 +10:00
MoeBoy76 3e3a2a7ec0 Fixed model loading for ResourcePacks, updated Abra aspect to match changes 2022-12-02 21:14:19 +11:00
Hiroku bd1f7c59c3 Merge branch 'entity-attributes-fix' into 'main'
Entity attribute registration fix

See merge request cable-mc/cobblemon!221
2022-11-29 10:54:14 +00:00
Licious 1fdf928b7b Entity attribute registration fix 2022-11-29 10:54:13 +00:00
Paul ee863a4e04 Dated the changelog [skip-ci] 2022-11-28 18:34:35 +10:00
Paul 24f6915cc9 1.1.1 2022-11-28 13:25:14 +10:00
Paul b4207b11ab Fixed sync issue for implemented field in Species 2022-11-28 12:50:32 +10:00
805 changed files with 6569 additions and 4261 deletions

No files matched your search

+4 -1
View File
@@ -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
View File
@@ -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."
+3
View File
@@ -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)
}
@@ -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()
}
}
}
@@ -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
}
@@ -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() }
}
}
@@ -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()
}
}
}
@@ -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")
}
@@ -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)
}
}
@@ -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
@@ -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()
}
@@ -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()
}
@@ -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?
}
@@ -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)
}
}
@@ -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
}
@@ -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()
@@ -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
@@ -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()
@@ -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
@@ -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 }
)
}
@@ -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)
}
}
}
@@ -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
}
@@ -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)
@@ -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)
}
}
}
@@ -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
}
@@ -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.")
}
@@ -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)
}
}
@@ -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)
}
}
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -0,0 +1,12 @@
{
"species": "cobblemon:venusaur",
"order": 1,
"variations": [
{
"aspects": [
"female"
],
"model": "cobblemon:venusaur_female.geo"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -0,0 +1,12 @@
{
"species": "cobblemon:butterfree",
"order": 1,
"variations": [
{
"aspects": [
"female"
],
"model": "cobblemon:butterfree_female.geo"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -0,0 +1,12 @@
{
"species": "cobblemon:rattata",
"order": 1,
"variations": [
{
"aspects": [
"female"
],
"model": "cobblemon:rattata_female.geo"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -0,0 +1,12 @@
{
"species": "cobblemon:raticate",
"order": 1,
"variations": [
{
"aspects": [
"female"
],
"model": "cobblemon:raticate_female.geo"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
]
}
@@ -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"
}
]
}
@@ -0,0 +1,12 @@
{
"species": "cobblemon:pikachu",
"order": 1,
"variations": [
{
"aspects": [
"female"
],
"model": "cobblemon:pikachu_female.geo"
}
]
}
@@ -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"
}
]
}
@@ -0,0 +1,12 @@
{
"species": "cobblemon:raichu",
"order": 1,
"variations": [
{
"aspects": [
"female"
],
"model": "cobblemon:raichu_female.geo"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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"
}
]
}
@@ -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