Compare commits

...
Author SHA1 Message Date
amo 89c2b5cef3 still bugs but molangified the model query 2025-02-03 19:13:01 +00:00
amo bd4a6fc48c fin! except animations are bugged when an appendage is visible? 2025-02-03 15:04:21 +00:00
amo c7bca0ba19 90% of the footwork for model appendages, pls help translation hurt 2025-02-03 06:38:25 +00:00
Polymeta 9b2d032c6a Merge branch 'config-gui' into 'main'
Add config screen

Closes #112

See merge request cable-mc/cobblemon!1508
2025-02-02 18:56:18 +00:00
lionandPolymeta be76f8a180 Add config screen 2025-02-02 18:56:18 +00:00
Hiroku 2af0324ee5 Merge branch 'feat/ev-gained-event' into 'main'
Add EvGainedEvent

See merge request cable-mc/cobblemon!1514
2025-02-01 21:41:37 +00:00
Ina 8aa4f57a33 Add EvGainedEvent 2025-02-01 16:40:08 +00:00
Hiroku 3599dc97cc Merge branch 'reload-cobblemon-config-command' into 'main'
Add '/cobblemonconfig reload' command

Closes #132

See merge request cable-mc/cobblemon!1391
2025-01-31 10:58:08 +00:00
lionandHiroku 8a5d895f03 Add '/cobblemonconfig reload' command 2025-01-31 10:58:08 +00:00
Hiroku 36169db388 Merge branch 'feature/save-file-juggling' into 'main'
old and temp files for everyone

See merge request cable-mc/cobblemon!1484
2025-01-31 07:46:10 +00:00
Paul 1900999a74 Changelogging [skip-ci] 2025-01-31 17:41:56 +10:00
Hiroku ab16814cd2 Merge branch 'fix-setpageondefaultdialoguepage' into 'main'
fix: calling setPages() when initializing the dialogue so default pages can have their timeout scheduled

Closes #1406

See merge request cable-mc/cobblemon!1504
2025-01-31 07:41:29 +00:00
Hiroku 9f622fcd74 Merge branch 'pc-box-2-fix' into 'main'
Fix PC opening on Box 2

See merge request cable-mc/cobblemon!1505
2025-01-31 07:40:16 +00:00
Hiroku 956e2c43b8 Merge branch 'feat-winners-and-losers' into 'main'
feat: set winners and losers on PokemonBattle end

See merge request cable-mc/cobblemon!1501
2025-01-31 07:37:14 +00:00
Hiroku 130f87c9f7 Merge branch 'strip-stripped-log' into 'main'
remove apricorn_stripped_log and apricorn_stripped_log_top as never used

See merge request cable-mc/cobblemon!1502
2025-01-31 07:27:54 +00:00
gbassinello 22b4e8c75f chore: changelog lines 2025-01-31 01:44:50 -03:00
gbassinello c19a3ca444 feat: set winners and losers on battle end 2025-01-31 01:44:50 -03:00
Polymetaandgbassinello a006eb5dcc revert previous attempt and fix camera mode
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-01-31 01:44:50 -03:00
gbassinello ed419e7e53 fix: removing activeDialogues from the DialogueManager map using the correct key (player uuid) 2025-01-31 01:44:50 -03:00
Hiroku bff2387b4d Merge branch 'feature/particle-fixes' into 'main'
revert previous attempt and fix camera mode

See merge request cable-mc/cobblemon!1506
2025-01-31 04:02:00 +00:00
Polymeta 41544efeba revert previous attempt and fix camera mode
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-01-30 23:15:08 +00:00
Paul e6e6c9f7f6 Merge remote-tracking branch 'origin/main' 2025-01-31 08:31:31 +10:00
Paul 22d8e39db3 back to snapshots 2025-01-31 08:31:17 +10:00
Hiroku bfdf1e9e6c Merge branch 'fix-activedialoguesmapkey' into 'main'
fix: removing activeDialogues from the DialogueManager map using the correct key (player uuid)

Closes #1405

See merge request cable-mc/cobblemon!1503
2025-01-30 22:19:22 +00:00
BuildTools c3fccfde09 Fix PC opening on Box 2 because default value for 'box' in OpenPCPacket was '1' instead of '0' 2025-01-30 21:48:18 +02:00
gbassinello 4bb8d43a6e fix: calling setPages() when initializing the dialogue so default pages can have their timeout scheduled 2025-01-30 13:50:18 -03:00
gbassinello fda4b67776 fix: removing activeDialogues from the DialogueManager map using the correct key (player uuid) 2025-01-30 13:34:52 -03:00
Xander Forrest f1ee9d45de remove apricorn_stripped_log and apricorn_stripped_log_top as never used 2025-01-28 19:39:28 +00:00
Polymeta 01f7d13e3f update tag and preset list [skip-ci]
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-01-26 21:32:32 +00:00
Paul d85e30f3a6 But what if we didn't break the yarn mapping 2025-01-26 22:15:17 +10:00
Polymeta 4074aa50ce refactor storage layer and introduce .old filetype as a stopgap for file corruption during crashes
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-01-21 02:14:35 +00:00
84 changed files with 4639 additions and 437 deletions
+42 -1
View File
@@ -1,4 +1,29 @@
# Changelog
## [1.7.0 (Month xth, 2025)](#1-7-0)
### Additions
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
### Changes
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
### Fixes
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
- Fixed PCs always opening at box 2 instead of box 1.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
- Dialogues are correctly removed from memory when they are stopped.
- Dialogues with variably-set initial pages now properly start timeout tracking.
- Added `EvGainedEvent.Pre` and `EvGainedEvent.Post`.
- Deprecated `EVs#add(Stat, Int)`, you will not be able to compile against this code please migrate to the `EVs#add(Stat, Int, EvSource)` you most likely want to use `SidemodEvSource` but please check other implementations or make your own.
### Additions
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
- Added the Model Appendages system, allowing for resolver-based bedrock model attachments to Pokémon models based on Locators.
- View the `egg` examples in `assets/cobblemon/bedrock/appendage` for an example of how to use this system.
## [1.6.1 (January 26th, 2025)](#1-6-1)
### Additions
@@ -12,6 +37,22 @@
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added debug renderer for posable entity locators.
- Added crossover paintings from Close Combat, Premonition, Altar, Slumber, and Nomad.
- Added optional box argument to the /pc command
### Changes
- Pokémon will now be dynamically revealed to the Pokédex as they're seen instead of revealing entire parties at the end of battle regardless.
- Unseen wild Pokémon will update their name from '???' to their real name as soon as a battle starts to reflect the battle UI showing the actual species name.
- Pokémon under the illusion effect will reveal their disguise to the Pokédex first and then the base Pokémon once the disguise is broken.
- Added more support for a variety of Fabric/NeoForge Convention tags.
- Reformatted some tags to be more consistent.
- Edited some recipes to utilize tags instead of direct item ids, for greater mod compatibility.
- Berries will drop if broken at age 0
- Improved Fortune drops on Mint Seeds
- New Slowpoke shiny texture.
- Updated drops for many Pokémon.
- Completely resynced Pokémon move and stat data based on later games. Learnsets have changed considerably to maximise available moves.
- Cobblemon save data now saves .old files where applicable as a means to recover from file corruption due to crashes or similar abrupt stops
### Pokémon Added
@@ -175,7 +216,7 @@
- Added `hideNameTag` field and `HideNPCNameTag` nbt tag to `NPCEntity` to allow hiding the name tag of the NPC.
- Added the player to `PokerodReelEvent` so you know who is doing the reeling.
### MoLang & Datapacks
### MoLang & Datapacks
- Added flows for:
- `forme_change`: Triggered when a Pokémon changes form in battle.
- `mega_evolution`: Triggered when a Pokémon mega evolves in battle. (Note: Third-party mods are required for this feature currently)
+1 -1
View File
@@ -41,7 +41,7 @@ dependencies {
// Integrations
compileOnlyApi(libs.jei.api)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
isTransitive = false
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
@@ -62,11 +62,7 @@ import com.cobblemon.mod.common.api.storage.player.adapter.PlayerDataMongoBacken
import com.cobblemon.mod.common.api.storage.player.factory.CachedPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.client.CobblemonPack
@@ -89,22 +85,11 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.properties.AspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.BattleCloneProperty
import com.cobblemon.mod.common.pokemon.properties.FreezeFrameProperty
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.NoAIProperty
import com.cobblemon.mod.common.pokemon.properties.UnaspectPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.*
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
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.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -112,25 +97,26 @@ import com.mongodb.ConnectionString
import com.mongodb.MongoClientSettings
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.Minecraft
import net.minecraft.commands.synchronization.SingletonArgumentInfo
import net.minecraft.resources.ResourceKey
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.world.item.NameTagItem
import net.minecraft.world.level.Level
import net.minecraft.world.level.storage.LevelResource
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
object Cobblemon {
const val MODID = CobblemonBuildDetails.MOD_ID
@@ -220,7 +206,7 @@ object Cobblemon {
storage.onPlayerDataSync(it)
playerDataManager.syncAllToPlayer(it)
starterHandler.handleJoin(it)
ServerSettingsPacket(this.config.preventCompletePartyDeposit, this.config.displayEntityLevelLabel, this.config.displayEntityNameLabel, this.config.maxPokemonLevel, this.config.maxPokemonFriendship, this.config.maxDynamaxLevel).sendToPlayer(it)
sendServerSettingsPacketToPlayer(it)
}
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
PCLinkManager.removeLink(it.player.uuid)
@@ -268,8 +254,17 @@ object Cobblemon {
}
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
}
fun sendServerSettingsPacketToPlayer(player: ServerPlayer) {
ServerSettingsPacket(
this.config.preventCompletePartyDeposit,
this.config.displayEntityLevelLabel,
this.config.displayEntityNameLabel,
this.config.maxPokemonLevel,
this.config.maxPokemonFriendship,
this.config.maxDynamaxLevel,
).sendToPlayer(player)
}
fun initialize() {
@@ -429,7 +424,23 @@ object Cobblemon {
}
}
private fun initializeConfig() {
loadCobblemonConfig()
saveConfig(this.config)
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadConfig() {
initializeConfig()
bestSpawner.init()
}
fun reloadConfig() {
initializeConfig()
bestSpawner.reloadConfig()
}
private fun loadCobblemonConfig() {
val configFile = File(CONFIG_PATH)
configFile.parentFile.mkdirs()
@@ -472,12 +483,6 @@ object Cobblemon {
} else {
this.config = CobblemonConfig()
}
config.lastSavedVersion = VERSION
this.saveConfig()
bestSpawner.loadConfig()
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadStarterConfig(): StarterConfig {
@@ -500,12 +505,14 @@ object Cobblemon {
}
}
fun saveConfig() {
fun saveConfig(config: CobblemonConfig) {
config.lastSavedVersion = VERSION
try {
val configFile = File(CONFIG_PATH)
val fileWriter = FileWriter(configFile)
// Put the config to json then flush the writer to commence writing.
CobblemonConfig.GSON.toJson(this.config, fileWriter)
CobblemonConfig.GSON.toJson(config, fileWriter)
fileWriter.flush()
fileWriter.close()
} catch (exception: Exception) {
@@ -527,4 +534,4 @@ object Cobblemon {
this.implementation.registerCommandArgument(cobblemonResource("npc_class"), NPCClassArgumentType::class, SingletonArgumentInfo.contextFree(NPCClassArgumentType::npcClass))
}
}
}
@@ -57,6 +57,7 @@ object CobblemonCommands {
FreezePokemonCommand.register(dispatcher)
ApplyPlayerTextureCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
import com.cobblemon.mod.common.client.net.spawn.SpawnExtraDataEntityHandler
@@ -80,6 +81,7 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.RemoveEvolutionPacket
import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfigScreenPacket
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPacket
@@ -284,6 +286,7 @@ object CobblemonNetwork {
// Settings packets
list.add(PacketRegisterInfo(ServerSettingsPacket.ID, ServerSettingsPacket::decode, ServerSettingsPacketHandler))
list.add(PacketRegisterInfo(OpenCobblemonConfigScreenPacket.ID, OpenCobblemonConfigScreenPacket::decode, OpenCobblemonConfigEditorHandler))
// Data registries
list.add(PacketRegisterInfo(AbilityRegistrySyncPacket.ID, AbilityRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -43,7 +43,8 @@ interface SleepDepth {
"comatose" to comatose,
"normal" to normal
)
val adapter = StringIdentifiedObjectAdapter { depths[it] ?: throw IllegalArgumentException("Unknown sleep depth: $it") }
val adapter = StringIdentifiedObjectAdapter({ depths[it] ?: throw IllegalArgumentException("Unknown sleep depth: $it") })
}
fun canSleep(pokemonEntity: PokemonEntity): Boolean
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.pokemon.stats.BattleEvSource
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
@@ -272,7 +273,7 @@ open class PokemonBattle(
opponent.awardExperience(opponentPokemon, experience)
}
Cobblemon.evYieldCalculator.calculate(opponentPokemon, faintedPokemon).forEach { (stat, amount) ->
pokemon.evs.add(stat, amount)
pokemon.evs.add(stat, amount, BattleEvSource(this, opponentPokemon.facedOpponents.toList(), pokemon))
}
}
@@ -55,6 +55,7 @@ class ActiveDialogue(var playerEntity: ServerPlayer, var dialogueReference: Dial
)
dialogueReference.initializationAction.invoke(this)
setPage(currentPage)
if (!completion.isDone) {
val packet = DialogueOpenedPacket(this, true)
@@ -52,6 +52,6 @@ object DialogueManager {
fun stopDialogue(playerEntity: ServerPlayer) {
val activeDialogue = playerEntity.activeDialogue ?: return
DialogueClosedPacket(activeDialogue.dialogueId).sendToPlayer(playerEntity)
activeDialogues.remove(activeDialogue.dialogueId)
activeDialogues.remove(playerEntity.uuid)
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.serialization.StringIdentifiedObjectAdapter
import com.google.gson.annotations.SerializedName
/**
* The way an item will be dropped.
@@ -28,6 +29,9 @@ enum class ItemDropMethod(val methodName: String) {
TO_INVENTORY("to-inventory");
companion object {
val adapter = StringIdentifiedObjectAdapter { str -> values().firstOrNull { it.methodName == str } }
val adapter = StringIdentifiedObjectAdapter(
{ str -> ItemDropMethod.entries.firstOrNull { it.methodName == str } },
{ value -> (value ?: ON_ENTITY).methodName },
)
}
}
@@ -158,6 +158,11 @@ object CobblemonEvents {
@JvmField
val EXPERIENCE_CANDY_USE_POST = EventObservable<ExperienceCandyUseEvent.Post>()
@JvmField
val EV_GAINED_EVENT_PRE = CancelableObservable<EvGainedEvent.Pre>()
@JvmField
val EV_GAINED_EVENT_POST = EventObservable<EvGainedEvent.Post>()
@JvmField
val POKEMON_RELEASED_EVENT_PRE = CancelableObservable<ReleasePokemonEvent.Pre>()
@JvmField
@@ -0,0 +1,43 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.pokemon.stats.EvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
/**
* Fired during EV mutation.
*
* @see [Pre]
* @see [Post]
*/
interface EvGainedEvent {
/**
* The [Stat] having EVs added to itself.
*/
val stat: Stat
/**
* The [EvSource] that fired this event.
*/
val source: EvSource
/**
* Fired before EV mutation occurs, cancelling will cause the mutation to be flagged as not successful (returns 0).
*
* @property stat See [EvGainedEvent.stat].
* @property amount The amount of EVs that will be gained.
* @property source See [EvGainedEvent.source].
*/
class Pre(override val stat: Stat, var amount: Int, override val source: EvSource) : EvGainedEvent, Cancelable()
/**
* Fired after EV mutation occurs, this is purely for notification purposes.
*
* @property stat See [EvGainedEvent.stat].
* @property amount The final amount of EVs gained.
* @property source See [EvGainedEvent.source].
*/
class Post(override val stat: Stat, val amount: Int, override val source: EvSource) : EvGainedEvent
}
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.permission
import com.cobblemon.mod.common.command.AbandonMultiTeam
object CobblemonPermissions {
private const val COMMAND_PREFIX = "command."
@@ -84,6 +82,8 @@ object CobblemonPermissions {
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val COBBLEMON_CONFIG_RELOAD = this.create("${COMMAND_PREFIX}cobblemonconfig.reload", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -0,0 +1,68 @@
package com.cobblemon.mod.common.api.pokemon.stats
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.item.ItemStack
interface EvSource {
/**
* The [Pokemon] being affected.
*/
val pokemon: Pokemon
/**
* Utility function that checks if the source is an implementation of [BattleEvSource].
*/
fun isBattle() = this is BattleEvSource
/**
* Utility function that checks if the source is an implementation of [ItemEvSource].
*/
fun isInteraction() = this is ItemEvSource
/**
* Utility function that checks if the source is an implementation of [SidemodEvSource].
*/
fun isSidemod() = this is SidemodEvSource
}
/**
* Triggered by sidemods.
*
* @property sidemodId The mod ID of the sidemod triggering this source.
* @property pokemon See [EvSource.pokemon].
*/
open class SidemodEvSource(
val sidemodId: String,
override val pokemon: Pokemon
) : EvSource
/**
* An Ev source fired when using Ev mutating items.
*
* @property player The [ServerPlayer] using the item.
* @property stack The [ItemStack] being consumed.
* @property pokemon See [EvSource.pokemon].
*/
open class ItemEvSource(
val player: ServerPlayer,
val stack: ItemStack,
override val pokemon: Pokemon
) : EvSource
/**
* An Ev source fired in battles.
*
* @property battle The associated [PokemonBattle].
* @property facedPokemon The [BattlePokemon]s that the [pokemon] faced.
* @property pokemon See [EvSource.pokemon], comes from the original [BattlePokemon.effectedPokemon].
*/
open class BattleEvSource(
val battle: PokemonBattle,
val facedPokemon: List<BattlePokemon>,
override val pokemon: Pokemon
) : EvSource
@@ -11,6 +11,9 @@ package com.cobblemon.mod.common.api.serialization
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -19,6 +22,13 @@ import java.lang.reflect.Type
* @author Hiroku
* @since July 18th, 2022
*/
class StringIdentifiedObjectAdapter<T>(val fromString: (String) -> T) : JsonDeserializer<T> {
class StringIdentifiedObjectAdapter<T>(
val fromString: (String) -> T,
val toString: ((T) -> String)? = null,
) : JsonDeserializer<T>, JsonSerializer<T> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext) = fromString(json.asString)
override fun serialize(src: T, type: Type?, ctx: JsonSerializationContext?): JsonElement? {
val stringValue = toString?.invoke(src) ?: throw IllegalStateException("No toString function provided for serialization.")
return JsonPrimitive(stringValue)
}
}
@@ -498,7 +498,7 @@ class LookAtDirection : ParticleCameraMode {
rotation.hamiltonProduct(Axis.ZP.rotationDegrees(particleAngle))
// Minecraft in 1.21 or 1.20.4 or something fucked with vertex draw order, and it flipped everything.
rotation.hamiltonProduct(Axis.YP.rotationDegrees(180F))
rotation.hamiltonProduct(Axis.XP.rotationDegrees(180F))
return rotation
}
@@ -83,8 +83,9 @@ object BestSpawner {
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
fun loadConfig() {
fun init() {
LOGGER.info("Starting the Best Spawner...")
SpawningCondition.register(BasicSpawningCondition.NAME, BasicSpawningCondition::class.java)
SpawningCondition.register(AreaSpawningCondition.NAME, AreaSpawningCondition::class.java)
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
@@ -94,6 +95,7 @@ object BestSpawner {
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
SpawningContextCalculator.register(SeafloorSpawningContextCalculator)
SpawningContextCalculator.register(LavafloorSpawningContextCalculator)
@@ -113,14 +115,23 @@ object BestSpawner {
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
config = BestSpawnerConfig.load()
loadConfig()
SpawnDetailPresets.registerPresetType(BasicSpawnDetailPreset.NAME, BasicSpawnDetailPreset::class.java)
SpawnDetailPresets.registerPresetType(PokemonSpawnDetailPreset.NAME, PokemonSpawnDetailPreset::class.java)
}
fun loadConfig() {
config = BestSpawnerConfig.load()
}
fun reloadConfig() {
loadConfig()
spawnerManagers.forEach(SpawnerManager::onConfigReload)
}
fun onServerStarted() {
spawnerManagers.forEach(SpawnerManager::onServerStarted)
fishingSpawner = FishingSpawner()
}
}
}
@@ -96,4 +96,4 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
this.presets = data.toMutableMap()
Cobblemon.LOGGER.info("Loaded ${presets.size} spawn detail presets.")
}
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
import com.cobblemon.mod.common.util.server
@@ -58,6 +59,12 @@ open class SpawnerManager {
}
}
open fun onConfigReload() {
spawners.filterIsInstance<PlayerSpawner>().forEach {
it.ticksBetweenSpawns = Cobblemon.config.ticksBetweenSpawnAttempts
}
}
open fun onServerStarted() {
spawners.clear()
}
@@ -161,4 +161,4 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
return true
}
}
}
@@ -221,4 +221,4 @@ abstract class SpawningContext {
}
return false
}
}
}
@@ -64,4 +64,4 @@ open class SpawningContextInput(
val cause: SpawnCause,
/** The [Level] the spawning context exists in. */
val world: ServerLevel
)
)
@@ -103,4 +103,4 @@ abstract class SpawnDetail : ModDependant {
}
abstract fun doSpawn(ctx: SpawningContext): SpawnAction<*>
}
}
@@ -46,16 +46,16 @@ open class JSONStoreAdapter(
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID, registryAccess: RegistryAccess): T? {
return try {
val br = BufferedReader(FileReader(file))
val json = gson.fromJson<JsonObject>(br)
br.close()
val store = try {
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
} catch (exception: NoSuchMethodException) {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
file.reader().use {
val json = gson.fromJson<JsonObject>(it)
val store = try {
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
} catch (exception: NoSuchMethodException) {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
store.loadFromJSON(json, registryAccess)
store
}
store.loadFromJSON(json, registryAccess)
store
} catch (e: Exception) {
null
}
@@ -42,6 +42,7 @@ abstract class OneToOneFileStoreAdapter<S>(
) : FileStoreAdapter<S>, CobblemonAdapterParent<S>() {
abstract fun save(file: File, serialized: S)
abstract fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID, registryAccess: RegistryAccess): T?
fun getFile(storeClass: Class<out PokemonStore<*>>, uuid: UUID): File {
val className = storeClass.simpleName.lowercase()
val subfolder1 = if (folderPerClass) "$className/" else ""
@@ -56,34 +57,54 @@ abstract class OneToOneFileStoreAdapter<S>(
override fun save(storeClass: Class<out PokemonStore<*>>, uuid: UUID, serialized: S) {
val file = getFile(storeClass, uuid)
val tempFile = File(file.absolutePath + ".temp")
val oldFile = File(file.absolutePath + ".old")
tempFile.createNewFile()
save(tempFile, serialized)
if (file.exists()) {
file.copyTo(oldFile, overwrite = true)
}
tempFile.copyTo(file, overwrite = true)
tempFile.delete()
}
override fun <E : StorePosition, T : PokemonStore<E>> provide(storeClass: Class<T>, uuid: UUID, registryAccess: RegistryAccess): T? {
val file = getFile(storeClass, uuid)
val tempFile = File(file.absolutePath + ".temp")
if (tempFile.exists()) {
try {
val tempLoaded = load(tempFile, storeClass, uuid, registryAccess)
if (tempLoaded != null) {
save(file, serialize(tempLoaded, registryAccess))
return tempLoaded
}
} finally {
tempFile.delete()
}
}
val oldFile = File(file.absolutePath + ".old")
return if (file.exists()) {
return if (file.exists() && file.length() > 0L) {
load(file, storeClass, uuid, registryAccess)
?: let {
LOGGER.error("Pokémon save file for ${storeClass.simpleName} ($uuid) was corrupted. A fresh file will be created.")
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
file.delete()
if (oldFile.exists() && oldFile.length() > 0L) {
var result = load(oldFile, storeClass, uuid, registryAccess) ?: let {
LOGGER.error("Pokémon save file for ${storeClass.simpleName} ($uuid) was corrupted. A fresh file will be created.")
var result = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
save(storeClass, uuid, serialize(result, registryAccess))
oldFile.delete()
return result
}
LOGGER.warn("Loading old Pokémon save file for ${storeClass.simpleName} ($uuid) due to corruption of current file.")
oldFile.copyTo(file, overwrite = true)
result
}
else {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
var result = storeClass.getConstructor(UUID::class.java).newInstance(uuid)
save(storeClass, uuid, serialize(result, registryAccess))
oldFile.delete()
result
}
}
} else {
file.delete()
if (oldFile.exists() && oldFile.length() > 0L) {
var result = load(oldFile, storeClass, uuid, registryAccess) ?: let {
oldFile.delete()
return null
}
oldFile.copyTo(file, overwrite = true)
result
}
null
}
}
@@ -8,10 +8,12 @@
package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.storage.player.InstancedPlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreType
import net.minecraft.server.MinecraftServer
import net.minecraft.world.level.storage.LevelResource
import java.io.File
import java.nio.file.Path
import java.util.UUID
@@ -35,15 +37,70 @@ abstract class FileBasedPlayerDataStoreBackend<T : InstancedPlayerData>(
savePath = server.getWorldPath(LevelResource.PLAYER_DATA_DIR).parent
}
//TODO: Set file extension to be a field
fun getSubFile(uuid: UUID): String {
return if (useNestedStructure) {
"${uuid.toString().substring(0, 2)}/$uuid.$fileExt"
protected fun postSaveFileMoving(playerData: T) {
val tempFile = filePath(playerData.uuid, TEMPORARY_FILE_EXTENSION)
val oldFile = filePath(playerData.uuid, OLD_FILE_EXTENSION)
val file = filePath(playerData.uuid)
if (file.exists()) {
file.copyTo(oldFile, overwrite = true)
}
tempFile.copyTo(file, overwrite = true)
tempFile.delete()
}
protected fun loadWithFallback(uuid: UUID, loadFunction: (File) -> T): T {
val playerFile = filePath(uuid)
val playerFileOld = filePath(uuid, OLD_FILE_EXTENSION)
playerFile.parentFile.mkdirs()
val loadFallback: () -> T = {
playerFile.delete()
if (playerFileOld.exists() && playerFileOld.length() > 0L) {
try {
var result = loadFunction(playerFileOld)
playerFileOld.copyTo(playerFile)
Cobblemon.LOGGER.debug("Loaded .old {} for {}", subfolder, uuid)
result
} catch (e: Exception) {
Cobblemon.LOGGER.error("Failed to load .old $subfolder for $uuid due to ${e.message}. Data is lost")
Cobblemon.LOGGER.error(e)
playerFileOld.delete()
defaultData.invoke(uuid).also(::save)
}
}
else {
Cobblemon.LOGGER.error(".old File $subfolder for $uuid is corrupt or missing. Data is lost")
playerFileOld.delete()
defaultData.invoke(uuid).also(::save)
}
}
return if (playerFile.exists() && playerFile.length() > 0L) {
try {
loadFunction(playerFile)
}
catch (e: Exception) {
Cobblemon.LOGGER.warn("Failed to load $subfolder for $uuid due to ${e.message}")
Cobblemon.LOGGER.warn(e)
loadFallback()
}
} else {
"$uuid.$fileExt"
loadFallback()
}.also { it.initialize() }
}
@JvmOverloads
fun getSubFile(uuid: UUID, extension: String? = null): String {
return if (useNestedStructure) {
"${uuid.toString().substring(0, 2)}/$uuid.$fileExt${if (extension != null) ".$extension" else ""}"
} else {
"$uuid.$fileExt.${if (extension != null) ".$extension" else ""}"
}
}
fun filePath(uuid: UUID) = savePath.resolve("$subfolder/${getSubFile(uuid)}").toFile()
@JvmOverloads
fun filePath(uuid: UUID, extension: String? = null): File = savePath.resolve("$subfolder/${getSubFile(uuid, extension)}").toFile()
companion object {
const val OLD_FILE_EXTENSION = "old"
const val TEMPORARY_FILE_EXTENSION = "tmp"
}
}
@@ -28,29 +28,26 @@ abstract class JsonBackedPlayerDataStoreBackend<T : InstancedPlayerData>(
abstract val classToken: TypeToken<T>
override fun save(playerData: T) {
val file = filePath(playerData.uuid)
file.parentFile.mkdirs()
val pw = PrintWriter(filePath(playerData.uuid))
pw.write(gson.toJson(playerData))
pw.flush()
pw.close()
val fileTmp = filePath(playerData.uuid, TEMPORARY_FILE_EXTENSION)
fileTmp.parentFile.mkdirs()
fileTmp.printWriter().use { pw ->
pw.write(gson.toJson(playerData))
}
postSaveFileMoving(playerData)
}
override fun load(uuid: UUID): T {
val playerFile = filePath(uuid)
playerFile.parentFile.mkdirs()
return if (playerFile.exists() && playerFile.length() > 0L) {
gson.fromJson(BufferedReader(FileReader(playerFile)), classToken).also {
// Resolves old data that's missing new properties
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
if (newProps.isNotEmpty()) {
val defaultData = defaultData(uuid)
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
return loadWithFallback(uuid) {
it.reader().use { reader ->
gson.fromJson(reader, classToken).also {
// Resolves old data that's missing new properties
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
if (newProps.isNotEmpty()) {
val defaultData = defaultData(uuid)
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
}
}
}
} else {
defaultData.invoke(uuid).also(::save)
}.also { it.initialize() }
}
}
}
@@ -24,29 +24,24 @@ abstract class NbtBackedPlayerData<T : InstancedPlayerData>(
type: PlayerInstancedDataStoreType
) : FileBasedPlayerDataStoreBackend<T>(subfolder, type, "nbt") {
abstract val codec: Codec<T>
override fun save(playerData: T) {
val file = filePath(playerData.uuid)
file.parentFile.mkdirs()
val os = DataOutputStream(FileOutputStream(filePath(playerData.uuid)))
val encodeResult = NbtOps.INSTANCE.withEncoder(codec).apply(playerData)
NbtIo.write(encodeResult.result().get() as CompoundTag, os)
os.flush()
os.close()
val fileTmp = filePath(playerData.uuid, TEMPORARY_FILE_EXTENSION)
fileTmp.parentFile.mkdirs()
val encodeResult = codec.encodeStart(NbtOps.INSTANCE, playerData)
NbtIo.write(encodeResult.result().get() as CompoundTag, fileTmp.toPath())
postSaveFileMoving(playerData)
}
override fun load(uuid: UUID): T {
val playerFile = filePath(uuid)
playerFile.parentFile.mkdirs()
return if (playerFile.exists() && playerFile.length() > 0L) {
val input = NbtIo.read(playerFile.toPath())
val decodeResult = NbtOps.INSTANCE.withDecoder(codec).apply(input)
decodeResult.getPartialOrThrow {
return loadWithFallback(uuid) {
val input = NbtIo.read(it.toPath())
val decodeResult = codec.decode(NbtOps.INSTANCE, input)
decodeResult.getOrThrow {
Cobblemon.LOGGER.error("Error decoding $subfolder for player uuid $uuid")
Cobblemon.LOGGER.error(it)
throw UnsupportedOperationException()
}.first
} else {
defaultData.invoke(uuid).also(::save)
}.also { it.initialize() }
}
}
}
@@ -68,6 +68,8 @@ class WinInstruction(val message: BattleMessage): InterpreterInstruction {
}
}
battle.dispatchGo {
battle.winners = winners
battle.losers = losers
battle.end()
CobblemonEvents.BATTLE_VICTORY.post(BattleVictoryEvent(battle, winners, losers, wasCaught))
ShowdownInterpreter.lastCauser.remove(battle.battleId)
@@ -308,9 +308,12 @@ class HealingMachineBlockEntity(
} else {
// Recharging
if (blockEntity.healingCharge < blockEntity.maxCharge) {
val chargePerTick = (Cobblemon.config.chargeGainedPerTick).coerceAtLeast(0F)
val secondsToChargeHealingMachine = (Cobblemon.config.secondsToChargeHealingMachine).coerceAtLeast(0.0)
val totalTicks = secondsToChargeHealingMachine * 20
val chargePerTick = if (totalTicks > 0) blockEntity.maxCharge / totalTicks else 0.0
blockEntity.healingCharge =
(blockEntity.healingCharge + chargePerTick).coerceIn(0F..blockEntity.maxCharge)
(blockEntity.healingCharge + chargePerTick.toFloat()).coerceIn(0F..blockEntity.maxCharge)
blockEntity.updateBlockChargeLevel()
blockEntity.updateRedstoneSignal()
blockEntity.markUpdated()
@@ -21,12 +21,10 @@ import com.cobblemon.mod.common.client.render.models.blockbench.ExcludedLabels
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.animation.*
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.repository.appendage.ModelAppendageModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.getStringOrNull
import com.cobblemon.mod.common.util.*
import java.util.function.Function
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.SimpleSoundInstance
@@ -53,7 +51,7 @@ object ClientMoLangFunctions {
"say" to java.util.function.Function { params -> Minecraft.getInstance().player?.sendSystemMessage(params.getString(0).text()) ?: Unit },
)
val animationFunctions = hashMapOf<String, Function<PosableModel, Function<MoParams, Any>>>(
val posableModelFunctions = hashMapOf<String, Function<PosableModel, Function<MoParams, Any>>>(
"exclude_labels" to Function { model -> Function { params ->
val labels = params.params.map { it.asString() }
ObjectValue(ExcludedLabels(labels))
@@ -244,6 +242,18 @@ object ClientMoLangFunctions {
)
}
},
"appendage_model" to Function {
model -> Function { params ->
val name = params.getString(0)
ObjectValue((ModelAppendageModelRepository to name.asResource()))
}
},
"vanilla_model" to Function {
model -> Function { params ->
val id = params.getString(0).asResource()
ObjectValue(id)
}
},
"primary_animation" to Function { model ->
Function { params ->
val animation = params.get<ObjectValue<ActiveAnimation>>(0)?.obj ?: return@Function DoubleValue.ZERO
@@ -344,7 +354,7 @@ object ClientMoLangFunctions {
}
)
fun PosableModel.animationFunctions(): HashMap<String, Function<MoParams, Any>> = animationFunctions.map { (k, v) -> k to v.apply(this) }.toHashMap()
fun PosableModel.animationFunctions(): HashMap<String, Function<MoParams, Any>> = posableModelFunctions.map { (k, v) -> k to v.apply(this) }.toHashMap()
fun MoLangRuntime.setupClient(): MoLangRuntime {
environment.query.addFunctions(clientFunctions)
@@ -47,6 +47,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscM
import com.cobblemon.mod.common.client.render.models.blockbench.repository.NPCModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.appendage.ModelAppendageModelRepository
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
@@ -100,7 +101,8 @@ object CobblemonClient {
NPCModelRepository,
FossilModelRepository,
BlockEntityModelRepository,
GenericBedrockEntityModelRepository
GenericBedrockEntityModelRepository,
ModelAppendageModelRepository
)
val overlay: PartyOverlay by lazy { PartyOverlay() }
@@ -0,0 +1,113 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.config
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.StringWidget
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout
import net.minecraft.client.gui.layouts.LinearLayout
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.CommonComponents
class CobblemonConfigScreen(
private val previousScreen: Screen?,
) : Screen("gui.cobblemon_config.title".asTranslated()), CobblemonRenderable {
companion object {
const val HEIGHT = 240
const val HEADER_HEIGHT = 45
const val FOOTER_HEIGHT = 33
const val PADDING = 4
const val SLOT_WIDTH = 340
const val SLOT_HEIGHT = 22
const val HALF_SLOT_WIDTH = SLOT_WIDTH / 2
const val RESET_SLOT_WIDTH = 40
const val WIDGET_WIDTH = 110 - RESET_SLOT_WIDTH - PADDING
const val WIDGET_HEIGHT = SLOT_HEIGHT - PADDING
const val LABEL_Y_OFFSET = 4
const val WIDGET_Y_OFFSET = 0
}
val clonedConfig = Cobblemon.config.clone()
val defaultConfig = CobblemonConfig()
private lateinit var layout: HeaderAndFooterLayout
private lateinit var searchEdit: EditBox
private lateinit var variableList: CobblemonConfigVariableList
private lateinit var doneButton: Button
private var searchString: String = ""
override fun init() {
super.init()
if (::layout.isInitialized) layout.visitWidgets { removeWidget(it) }
layout = HeaderAndFooterLayout(this, HEADER_HEIGHT, FOOTER_HEIGHT)
if (::variableList.isInitialized) removeWidget(variableList)
variableList = CobblemonConfigVariableList(this)
variableList.filterConfigurations(searchString)
addRenderableWidget(variableList)
searchEdit = EditBox(
Minecraft.getInstance().font,
width / 2 - HALF_SLOT_WIDTH / 2,
variableList.y,
HALF_SLOT_WIDTH,
SLOT_HEIGHT,
lang("config.ui.search_configurations"),
).also {
it.height = WIDGET_HEIGHT
it.setMaxLength(250)
it.value = searchString
it.setResponder { text ->
variableList.filterConfigurations(text)
searchString = text
}
}
val linearLayout = layout.addToHeader(LinearLayout.vertical())
linearLayout.addChild(StringWidget(lang("config.ui.search_configurations"), this.font))
linearLayout.addChild(searchEdit)
layout.addToHeader(linearLayout)
doneButton = Button.builder(CommonComponents.GUI_DONE) { doneButtonClick() }
.pos(width / 2 - HALF_SLOT_WIDTH / 2, variableList.y + variableList.height + PADDING)
.width(HALF_SLOT_WIDTH).build()
layout.addToFooter(doneButton)
layout.visitWidgets { widget ->
addRenderableWidget(widget)
}
arrangeLayoutElements()
}
private fun arrangeLayoutElements() {
layout.arrangeElements()
variableList.updateSize(this.width, this.layout)
}
private fun doneButtonClick() {
Cobblemon.saveConfig(clonedConfig)
Cobblemon.reloadConfig()
onClose()
}
override fun onClose() {
minecraft?.setScreen(previousScreen)
}
}
@@ -0,0 +1,190 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.config
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.LABEL_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.PADDING
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList.CobblemonConfigVariableListEntry
import com.cobblemon.mod.common.client.gui.config.variable.*
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.Category
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.CobblemonConfigField
import com.cobblemon.mod.common.util.asTranslated
import net.minecraft.SharedConstants
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.ContainerObjectSelectionList
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.client.gui.components.events.GuiEventListener
import net.minecraft.client.gui.narration.NarratableEntry
import kotlin.math.min
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaField
class CobblemonConfigVariableList(
val parent: CobblemonConfigScreen,
) : ContainerObjectSelectionList<CobblemonConfigVariableListEntry>(
Minecraft.getInstance(),
Minecraft.getInstance().window.guiScaledWidth,
HEIGHT,
0,
SLOT_HEIGHT
) {
val entries = mutableListOf<CobblemonConfigCategory>()
init {
this.x = 0
this.y = minecraft.window.guiScaledHeight / 2 - HEIGHT / 2
setRenderHeader(false, 0)
addEntries()
}
@Suppress("UNCHECKED_CAST")
private fun addEntries() {
parent.clonedConfig::class.memberProperties.map { property ->
if (!property.javaField!!.isAnnotationPresent(CobblemonConfigField::class.java)) {
return@map null
}
val entry: CobblemonConfigVariable? =
when (val classifier = property.returnType.classifier) {
String::class -> CobblemonConfigStringVariable(this, property as KMutableProperty1<out CobblemonConfig, String>)
Int::class -> CobblemonConfigIntVariable(this, property as KMutableProperty1<out CobblemonConfig, Int>)
Float::class -> CobblemonConfigFloatVariable(this, property as KMutableProperty1<out CobblemonConfig, Float>)
Double::class -> CobblemonConfigDoubleVariable(this, property as KMutableProperty1<out CobblemonConfig, Double>)
Boolean::class -> CobblemonConfigBooleanVariable(this, property as KMutableProperty1<out CobblemonConfig, Boolean>)
is KClass<*> -> if (classifier.java.isEnum) {
CobblemonConfigEnumVariable(this, property as KMutableProperty1<out CobblemonConfig, Enum<*>>)
} else null
else -> null
}
if (entry == null) {
if (SharedConstants.IS_RUNNING_IN_IDE) {
Cobblemon.LOGGER.warn("Widget for variable type: ${property.returnType.classifier} not registered: ${property.name}")
}
return@map null
}
return@map entry
}
.filterNotNull()
.groupBy { it.cobblemonConfigFieldAnnotation.category }
.toSortedMap(compareBy { it.ordinal })
.forEach { category, variables ->
val categoryEntry = CobblemonConfigCategory(this, category, variables)
entries.add(categoryEntry)
}
entries.forEach { entry -> entry.add() }
}
override fun renderWidget(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
super.renderWidget(guiGraphics, mouseX, mouseY, partialTick)
val hoveredVariableTooltip = this.hovered?.tooltip
if (isMouseInsideScrollList(mouseX, mouseY) && hoveredVariableTooltip != null) {
guiGraphics.renderTooltip(minecraft.font, hoveredVariableTooltip.toCharSequence(minecraft), mouseX, mouseY)
}
}
fun isMouseInsideScrollList(mouseX: Int, mouseY: Int): Boolean {
val bottomX = parent.width / 2 - SLOT_WIDTH / 2
val upperX = parent.width / 2 + SLOT_WIDTH / 2
return mouseX >= bottomX && mouseX <= upperX && mouseY >= y && mouseY <= bottom
}
override fun getRowWidth(): Int = width
override fun getScrollbarPosition(): Int = width / 2 - SLOT_WIDTH / 2 + SLOT_WIDTH + PADDING
fun filterConfigurations(text: String) {
for (entry in entries) {
entry.remove()
}
for (entry in entries) {
if (text.isBlank() || entry.filter(text)) {
entry.add(text)
}
}
scrollAmount = min(maxScroll.toDouble(), scrollAmount)
}
abstract class CobblemonConfigVariableListEntry(
val parent: CobblemonConfigVariableList,
) : Entry<CobblemonConfigVariableListEntry>() {
val children = mutableListOf<GuiEventListener>()
var tooltip: Tooltip? = null
fun getX(left: Int) = parent.width / 2 - SLOT_WIDTH / 2
fun getY(top: Int) = top
abstract fun filter(text: String): Boolean
override fun children(): MutableList<out GuiEventListener> = children
override fun narratables(): MutableList<out NarratableEntry> = children.filterIsInstance<NarratableEntry>().toMutableList()
}
class CobblemonConfigCategory(
parent: CobblemonConfigVariableList,
val category: Category,
val configVariables: List<CobblemonConfigVariable>
) : CobblemonConfigVariableListEntry(parent) {
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
drawScaledText(
context = context,
text = "cobblemon.config.ui.category.${category.lang}".asTranslated().yellow(),
x = parent.width / 2,
y = getY(top) + LABEL_Y_OFFSET,
shadow = true,
centered = true,
)
}
fun add(filter: String? = null) {
parent.addEntry(this)
configVariables.forEach { variable ->
if (filter == null || variable.filter(filter)) {
parent.addEntry(variable)
}
}
}
fun remove() {
parent.removeEntry(this)
configVariables.forEach { variable -> parent.removeEntry(variable) }
}
override fun filter(text: String): Boolean = configVariables.any { it.filter(text) }
}
}
@@ -0,0 +1,126 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.config.variable
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.PADDING
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.network.chat.CommonComponents
import net.minecraft.network.chat.Component
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
abstract class CobblemonConfigButtonVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, *>,
) : CobblemonConfigVariable(parent, config) {
val button: Button =
Button.builder(labelLang) { onButtonClick() }
.size(WIDGET_WIDTH, WIDGET_HEIGHT)
.build()
init {
children.addFirst(button)
}
abstract fun onButtonClick()
override fun updateConfigValueDisplay() {
button.message = Component.literal(getConfigValueAsString())
}
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
super.render(context, index, top, left, width, height, mouseX, mouseY, hovering, partialTick)
button.x = resetButton.x - button.width - PADDING
button.y = getY(top) + WIDGET_Y_OFFSET
button.render(context, mouseX, mouseY, partialTick)
}
}
class CobblemonConfigBooleanVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Boolean>,
) : CobblemonConfigButtonVariable(parent, config) {
init {
updateConfigValueDisplay()
}
override fun getConfigValueAsString(): String = (if (isSelected()) CommonComponents.GUI_YES else CommonComponents.GUI_NO).string
override fun onButtonClick() = setConfigValue(!isSelected())
private fun isSelected() = (config.getter.call(parent.parent.clonedConfig) as Boolean?) ?: false
}
class CobblemonConfigEnumVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Enum<*>>,
) : CobblemonConfigButtonVariable(parent, config) {
private var enumIndex = 0
@Suppress("UNCHECKED_CAST")
private val enumValues: Array<Enum<*>> = config.returnType.classifier
.let { it as? KClass<Enum<*>> }
?.java?.enumConstants ?: emptyArray()
init {
val currentEnumValue = getCurrentEnumValue()
enumValues.firstOrNull { it.name == currentEnumValue?.name }?.let {
enumIndex = it.ordinal
}
updateConfigValueDisplay()
}
override fun getConfigValueAsString(): String {
return getCurrentEnumValue()?.name?.let {
lang("$labelEntry.${it.lowercase()}").string
} ?: ""
}
private fun getConfigValueTooltipString(): String {
return getCurrentEnumValue()?.name?.let {
lang("$labelEntry.${it.lowercase()}.tooltip").string
} ?: ""
}
override fun onButtonClick() {
enumIndex = (enumIndex + 1) % enumValues.size
val newEnumValue = enumValues[enumIndex]
setConfigValue(newEnumValue)
}
override fun updateConfigValueDisplay() {
super.updateConfigValueDisplay()
tooltip = Tooltip.create(Component.literal(getConfigValueTooltipString()))
}
private fun getCurrentEnumValue(): Enum<*>? = (config.getter.call(parent.parent.clonedConfig) as Enum<*>?)
}
@@ -0,0 +1,155 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.config.variable
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.PADDING
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.util.lang
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.components.EditBox.DEFAULT_TEXT_COLOR
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.network.chat.Component
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.jvm.javaField
abstract class CobblemonConfigEditBoxVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, *>,
) : CobblemonConfigVariable(parent, config) {
val editBox = EditBox(
Minecraft.getInstance().font,
parent.x,
parent.y,
WIDGET_WIDTH,
SLOT_HEIGHT,
labelLang,
).also {
it.height = WIDGET_HEIGHT
it.setMaxLength(250)
it.value = getConfigValueAsString()
}
init {
children.addFirst(editBox)
}
override fun updateConfigValueDisplay() {
editBox.value = getConfigValueAsString()
}
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
super.render(context, index, top, left, width, height, mouseX, mouseY, hovering, partialTick)
editBox.x = resetButton.x - editBox.width - PADDING
editBox.y = getY(top) + WIDGET_Y_OFFSET
editBox.render(context, mouseX, mouseY, partialTick)
}
}
class CobblemonConfigStringVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, String>,
) : CobblemonConfigEditBoxVariable(parent, config) {
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as String?) ?: ""
init {
editBox.setResponder { value ->
setConfigValue(value, false)
}
}
}
class CobblemonConfigIntVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Int>,
) : CobblemonConfigEditBoxVariable(parent, config) {
private val intConstraintAnnotation: IntConstraint? =
config.javaField!!.annotations.firstOrNull{ it is IntConstraint } as IntConstraint?
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as Int?).toString()
init {
editBox.setFilter { value -> value.toIntOrNull() != null || value.isBlank() }
editBox.setResponder { value ->
if (value.isBlank()) {
return@setResponder
}
val intValue = value.toInt()
setConfigValue(intValue, false)
if (intConstraintAnnotation != null) {
val isBetweenIntConstraint = intValue >= intConstraintAnnotation.min && intValue <= intConstraintAnnotation.max
if (isBetweenIntConstraint) {
tooltip = originalTooltip
editBox.setTextColor(DEFAULT_TEXT_COLOR)
} else {
val errorLang = lang("config.ui.error.int_constraint", intConstraintAnnotation.min, intConstraintAnnotation.max).red()
tooltip = Tooltip.create(tooltipLang.plainCopy().add(Component.literal("\n")).add(errorLang))
ChatFormatting.RED.color?.let { editBox.setTextColor(it) }
}
}
}
}
}
class CobblemonConfigFloatVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Float>,
) : CobblemonConfigEditBoxVariable(parent, config) {
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as Float?).toString()
init {
editBox.setFilter { value -> value.toFloatOrNull() != null || value.isBlank() }
editBox.setResponder { value ->
if (value.toFloatOrNull() != null) {
setConfigValue(value.toFloat(), false)
}
}
}
}
class CobblemonConfigDoubleVariable(
parent: CobblemonConfigVariableList,
config: KMutableProperty1<out CobblemonConfig, Double>,
) : CobblemonConfigEditBoxVariable(parent, config) {
override fun getConfigValueAsString(): String = (config.getter.call(parent.parent.clonedConfig) as Double?).toString()
init {
editBox.setFilter { value -> value.toDoubleOrNull() != null || value.isBlank() }
editBox.setResponder { value ->
if (value.toDoubleOrNull() != null) {
setConfigValue(value.toDouble(), false)
}
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.config.variable
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.LABEL_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.RESET_SLOT_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.SLOT_WIDTH
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_HEIGHT
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen.Companion.WIDGET_Y_OFFSET
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigVariableList.CobblemonConfigVariableListEntry
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.CobblemonConfigField
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.Tooltip
import net.minecraft.network.chat.MutableComponent
import java.text.Normalizer
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.jvm.javaField
abstract class CobblemonConfigVariable(
parent: CobblemonConfigVariableList,
val config: KMutableProperty1<out CobblemonConfig, *>,
) : CobblemonConfigVariableListEntry(parent) {
val cobblemonConfigFieldAnnotation: CobblemonConfigField =
config.javaField!!.annotations.firstOrNull{ it is CobblemonConfigField } as CobblemonConfigField
val labelEntry = "config.ui.${cobblemonConfigFieldAnnotation.lang}"
val tooltipEntry = "$labelEntry.tooltip"
val labelLang: MutableComponent = lang(labelEntry)
val tooltipLang: MutableComponent = lang(tooltipEntry)
val originalTooltip: Tooltip? = if (!tooltipLang.string.endsWith(tooltipEntry)) Tooltip.create(tooltipLang) else null
val resetButton: Button =
Button.builder(lang("config.ui.reset")) {
val defaultValue = config.getter.call(parent.parent.defaultConfig)
setConfigValue(defaultValue)
}
.size(RESET_SLOT_WIDTH, WIDGET_HEIGHT)
.build()
init {
tooltip = originalTooltip
children.add(resetButton)
updateResetButton()
}
abstract fun getConfigValueAsString(): String
abstract fun updateConfigValueDisplay()
fun setConfigValue(value: Any?, updateConfigValueDisplay: Boolean = true) {
config.setter.call(parent.parent.clonedConfig, value)
if (updateConfigValueDisplay) {
updateConfigValueDisplay()
}
updateResetButton()
}
private fun updateResetButton() {
resetButton.active = config.getter.call(parent.parent.clonedConfig) != config.getter.call(parent.parent.defaultConfig)
}
override fun filter(text: String): Boolean {
val normalizedLabel = Normalizer.normalize(labelLang.string, Normalizer.Form.NFD).replace("\\p{M}+".toRegex(), "")
return normalizedLabel.contains(text, ignoreCase = true)
}
override fun render(
context: GuiGraphics,
index: Int,
top: Int,
left: Int,
width: Int,
height: Int,
mouseX: Int,
mouseY: Int,
hovering: Boolean,
partialTick: Float
) {
val x = getX(left)
val y = getY(top)
drawScaledText(
context = context,
text = labelLang,
x = getX(left),
y = getY(top) + LABEL_Y_OFFSET,
shadow = true
)
resetButton.x = x + SLOT_WIDTH - RESET_SLOT_WIDTH
resetButton.y = y + WIDGET_Y_OFFSET
resetButton.render(context, mouseX, mouseY, partialTick)
}
}
@@ -18,7 +18,7 @@ class GrabbedStorageSlot(
) : StorageSlot(x, y, parent, {}) {
init {
isSelected = true
isSlotSelected = true
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
@@ -46,7 +46,7 @@ open class StorageSlot(
private val slotOverlayMoveIconResource = cobblemonResource("textures/gui/pasture/pc_slot_icon_move.png")
}
protected var isSelected = false;
protected var isSlotSelected = false;
override fun playDownSound(soundManager: SoundManager) {
}
@@ -84,7 +84,7 @@ open class StorageSlot(
context.disableScissor()
if (!isSelected) {
if (!isSlotSelected) {
// Ensure elements are not hidden behind Pokémon render
matrices.pushPose()
matrices.translate(0.0, 0.0, 100.0)
@@ -129,7 +129,7 @@ open class StorageSlot(
matrices.translate(0.0, 0.0, 500.0)
val config = parent.pcGui.configuration
if (pokemon.tetheringId != null && !isSelected) {
if (pokemon.tetheringId != null && !isSlotSelected) {
if (isStationary()) {
blitk(
matrixStack = matrices,
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.settings
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen
import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfigScreenPacket
import net.minecraft.client.Minecraft
object OpenCobblemonConfigEditorHandler : ClientNetworkPacketHandler<OpenCobblemonConfigScreenPacket> {
override fun handle(packet: OpenCobblemonConfigScreenPacket, client: Minecraft) {
client.setScreen(CobblemonConfigScreen(Minecraft.getInstance().screen))
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.render
import com.cobblemon.mod.common.util.math.geometry.getOrigin
import com.cobblemon.mod.common.util.math.geometry.transformPosition
import com.mojang.blaze3d.vertex.PoseStack
import net.fabricmc.loader.impl.lib.sat4j.core.Vec
import org.joml.Matrix4f
import net.minecraft.world.phys.Vec3
@@ -204,8 +204,8 @@ class SnowstormParticle(
val spriteURange = sprite.u1 - sprite.u0
val spriteVRange = sprite.v1 - sprite.v0
val maxU = uvs.startU * spriteURange + sprite.u0
val minU = uvs.endU * spriteURange + sprite.u0
val minU = uvs.startU * spriteURange + sprite.u0
val maxU = uvs.endU * spriteURange + sprite.u0
val minV = uvs.startV * spriteVRange + sprite.v0
val maxV = uvs.endV * spriteVRange + sprite.v0
@@ -12,7 +12,7 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
import com.cobblemon.mod.common.client.render.layer.CobblemonRenderLayers
import com.cobblemon.mod.common.client.render.layer.CobblemonRenderTypes
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.setPosition
import com.cobblemon.mod.common.util.math.geometry.Axis
@@ -54,7 +54,7 @@ class BerryBlockRenderer(private val context: BlockEntityRendererProvider.Contex
if (entity.renderState == null) {
entity.renderState = BerryBlockEntityRenderState()
}
val bf = vertexConsumers.getBuffer(CobblemonRenderLayers.BERRY_LAYER)
val bf = vertexConsumers.getBuffer(CobblemonRenderTypes.BERRY_LAYER)
val renderState = entity.renderState as BerryBlockEntityRenderState
// if (renderState.needsRebuild || renderState.vboLightLevel != light) {
renderToBuffer(entity, matrices, light, overlay, renderState, bf)
@@ -19,7 +19,7 @@ import net.minecraft.Util
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderStateShard.*
object CobblemonRenderLayers {
object CobblemonRenderTypes {
val BERRY_LAYER = run {
val multiPhaseParameters = RenderType.CompositeState.builder()
.setLightmapState(LIGHTMAP)
@@ -0,0 +1,129 @@
package com.cobblemon.mod.common.client.render.layer
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.appendage.ModelAppendageModelRepository
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.client.resources.model.ModelResourceLocation
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemDisplayContext
import org.joml.Vector3f
object ModelAppendageRenderer {
fun <T> render(
poseStack: PoseStack,
bufferSource: MultiBufferSource,
packedLight: Int,
livingEntity: T,
limbSwing: Float,
limbSwingAmount: Float,
ageInTicks: Float,
netHeadYaw: Float,
headPitch: Float,
locator: String,
context: RenderContext,
parentModel: ResourceLocation
) where T : PokemonEntity {
val clientDelegate = livingEntity.delegate as PokemonClientDelegate
val appendages = ModelAppendageModelRepository.getAppendagesForModelAndTarget(parentModel, locator) ?: return
// avoid editing the original context
val clonedContext = context.clone()
appendages.forEach { appendage ->
val modelObj = appendage.model.resolveObject(clientDelegate.runtime).obj
if (modelObj is ResourceLocation) {
poseStack.pushPose()
clientDelegate.locatorStates[locator]?.let { bone ->
val stack = if(modelObj.toString() == "cobblemon:held_item") livingEntity.pokemon.heldItemNoCopy() else BuiltInRegistries.ITEM.get(modelObj).defaultInstance
val pos = bone.getOrigin().subtract(livingEntity.position())
val scale = Vector3f()
bone.matrix.getScale(scale)
poseStack.scale(scale.x(), scale.y(), scale.z())
poseStack.translate(pos.x, pos.y, pos.z)
poseStack.mulPose(bone.clone().matrix.setTranslation(0.0f, 0.0f, 0.0f))
poseStack.scale(
appendage.scale.x().toFloat(), appendage.scale.y().toFloat(),
appendage.scale.z().toFloat()
)
poseStack.mulPose(Axis.XP.rotationDegrees(appendage.rotation.x().toFloat()))
poseStack.mulPose(Axis.YP.rotationDegrees(appendage.rotation.y().toFloat()))
poseStack.mulPose(Axis.ZP.rotationDegrees(appendage.rotation.z().toFloat()))
poseStack.translate(appendage.offset.x(), appendage.offset.y(), appendage.offset.z())
Minecraft.getInstance().itemRenderer.renderStatic(stack, ItemDisplayContext.THIRD_PERSON_RIGHT_HAND, packedLight, OverlayTexture.NO_OVERLAY, poseStack, bufferSource, livingEntity.level(), 0)
}
poseStack.popPose()
} else if (modelObj is Pair<*, *>) {
val repo = modelObj.first as ModelAppendageModelRepository
val resLoc = modelObj.second as ResourceLocation
val model =
repo.getPoserNullable(resLoc, clientDelegate) ?: return
model.context = clonedContext
model.setLayerContext(
bufferSource,
clientDelegate,
ModelAppendageModelRepository.getLayers(resLoc, clientDelegate)
)
poseStack.pushPose()
clientDelegate.locatorStates[locator]?.let { bone ->
val pos = bone.getOrigin().subtract(livingEntity.position())
val scale = Vector3f()
bone.matrix.getScale(scale)
poseStack.scale(scale.x(), scale.y(), scale.z())
poseStack.translate(pos.x, pos.y, pos.z)
poseStack.mulPose(bone.clone().matrix.setTranslation(0.0f, 0.0f, 0.0f))
poseStack.mulPose(Axis.XP.rotationDegrees(180f))
poseStack.translate(0.0, -1.75, 0.0)
poseStack.scale(
appendage.scale.x().toFloat(), appendage.scale.y().toFloat(),
appendage.scale.z().toFloat()
)
poseStack.mulPose(Axis.XP.rotationDegrees(appendage.rotation.x().toFloat()))
poseStack.mulPose(Axis.YP.rotationDegrees(appendage.rotation.y().toFloat()))
poseStack.mulPose(Axis.ZP.rotationDegrees(appendage.rotation.z().toFloat()))
poseStack.translate(appendage.offset.x(), appendage.offset.y(), appendage.offset.z())
model.applyAnimations(
livingEntity,
clientDelegate,
limbSwing,
limbSwingAmount,
ageInTicks,
netHeadYaw,
headPitch
)
val buffer = bufferSource.getBuffer(
RenderType.entityCutout(
ModelAppendageModelRepository.getTexture(
resLoc,
clientDelegate
)
)
)
model.withLayerContext(
bufferSource,
clientDelegate,
ModelAppendageModelRepository.getLayers(resLoc, clientDelegate)
) {
model.render(clonedContext, poseStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, -0x1)
}
}
model.green = 1F
model.blue = 1F
model.red = 1F
model.resetLayerContext()
poseStack.popPose()
}
}
}
}
@@ -0,0 +1,15 @@
package com.cobblemon.mod.common.client.render.layer.appendage
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.google.gson.annotations.SerializedName
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
data class ModelAppendage(
@SerializedName("target_model") val targetModel: ResourceLocation?,
@SerializedName("model") val model: ExpressionLike,
@SerializedName("target_locator") val targetLocator: String,
@SerializedName("scale") val scale: Vec3 = Vec3(1.0, 1.0, 1.0),
@SerializedName("rotation") val rotation: Vec3 = Vec3(0.0, 0.0, 0.0),
@SerializedName("offset") val offset: Vec3 = Vec3(0.0, 0.0, 0.0)
)
@@ -80,6 +80,12 @@ class RenderContext {
this.context.remove(key)
}
fun clone(): RenderContext {
val newContext = RenderContext()
newContext.context.putAll(this.context)
return newContext
}
/**
* Represents a key used for accessing values in the context.
*
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.repository.Varyi
import com.cobblemon.mod.common.client.util.exists
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
@@ -72,6 +73,7 @@ abstract class VaryingModelRepository<T : PosableModel> {
GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(Vec3::class.java, Vec3dAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
@@ -213,7 +215,7 @@ abstract class VaryingModelRepository<T : PosableModel> {
Cobblemon.LOGGER.info("Loaded $models $title models.")
}
fun reload(resourceManager: ResourceManager) {
open fun reload(resourceManager: ResourceManager) {
Cobblemon.LOGGER.info("Loading $title assets...")
this.variations.clear()
this.posers.clear()
@@ -222,7 +224,7 @@ abstract class VaryingModelRepository<T : PosableModel> {
registerVariations(resourceManager)
}
fun getPoser(name: ResourceLocation, state: PosableState): T {
open fun getPoser(name: ResourceLocation, state: PosableState): T {
try {
val poser = this.variations[name]?.getPoser(state)
if (poser != null) {
@@ -0,0 +1,73 @@
package com.cobblemon.mod.common.client.render.models.blockbench.repository.appendage
import com.cobblemon.mod.common.client.render.layer.appendage.ModelAppendage
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.endsWith
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager
import java.io.File
import java.nio.charset.StandardCharsets
object ModelAppendageModelRepository : VaryingModelRepository<PosableModel>() {
override val poserClass: Class<PosableModel>
get() = PosableModel::class.java
override val title: String
get() = "Model Appendage"
override val type: String
get() = "appendage"
override val variationDirectories: List<String>
get() = listOf("bedrock/$type/variations")
override val poserDirectories: List<String>
get() = listOf("bedrock/$type/posers")
override val modelDirectories: List<String>
get() = listOf("bedrock/$type/models")
override val animationDirectories: List<String>
get() = listOf("bedrock/$type/animations")
val appendageDirectories: List<String>
get() = listOf("bedrock/$type/appendages")
val appendages = mutableMapOf<ResourceLocation, ModelAppendage>()
override val fallback: ResourceLocation = cobblemonResource("substitute")
override val isForLivingEntityRenderer: Boolean
get() = true
override fun registerInBuiltPosers() {}
override fun reload(resourceManager: ResourceManager) {
super.reload(resourceManager)
this.appendages.clear()
registerJsonAppendages(resourceManager)
}
private fun registerJsonAppendages(resourceManager: ResourceManager) {
for (directory in appendageDirectories) {
resourceManager
.listResources(directory) { path -> path.endsWith(".json") }
.forEach { (identifier, resource) ->
resource.open().use { stream ->
val json = String(stream.readAllBytes(), StandardCharsets.UTF_8)
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
appendages[resolvedIdentifier] = gson.fromJson(json, ModelAppendage::class.java)
}
}
}
}
fun getAppendagesForModelAndTarget(model: ResourceLocation, target: String): List<ModelAppendage> {
return appendages.entries.filter { (it.value.targetModel == model || it.value.targetModel == null) && it.value.targetLocator == target }.map { it.value }
}
fun getPoserNullable(name: ResourceLocation, state: PosableState): PosableModel? {
try {
val poser = this.variations[name]?.getPoser(state)
if (poser != null) {
return poser
}
} catch(e: IllegalStateException) {
// e.printStackTrace()
}
return this.variations[fallback]?.getPoser(state)
}
}
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BE
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BEAM_SHRINK_TIME
import com.cobblemon.mod.common.client.keybind.boundKey
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
import com.cobblemon.mod.common.client.render.layer.ModelAppendageRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PosablePokemonEntityModel
@@ -41,7 +42,6 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import kotlin.math.*
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font.DisplayMode
@@ -61,15 +61,17 @@ import org.joml.Math
import org.joml.Quaternionf
import org.joml.Vector3f
import org.joml.Vector4f
import kotlin.math.*
class PokemonRenderer(
context: EntityRendererProvider.Context
var context: EntityRendererProvider.Context
) : MobRenderer<PokemonEntity, PosablePokemonEntityModel>(context, PosablePokemonEntityModel(), 0.5f) {
companion object {
val recallBeamColour = Vector4f(1F, 0.1F, 0.1F, 1F)
fun ease(x: Double): Double {
return 1 - (1 - x).pow(3)
}
private val LEVEL_LABEL_STYLE = Style.EMPTY.withColor(ChatFormatting.WHITE)
.withBold(false)
.withItalic(false)
@@ -88,7 +90,10 @@ class PokemonRenderer(
}
override fun getTextureLocation(entity: PokemonEntity): ResourceLocation {
return PokemonModelRepository.getTexture(entity.pokemon.species.resourceIdentifier, entity.delegate as PokemonClientDelegate)
return PokemonModelRepository.getTexture(
entity.pokemon.species.resourceIdentifier,
entity.delegate as PokemonClientDelegate
)
}
override fun render(
@@ -100,7 +105,10 @@ class PokemonRenderer(
packedLight: Int
) {
val clientDelegate = entity.delegate as PokemonClientDelegate
shadowRadius = min((entity.boundingBox.maxX - entity.boundingBox.minX), (entity.boundingBox.maxZ) - (entity.boundingBox.minZ)).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).entityScaleModifier
shadowRadius = min(
(entity.boundingBox.maxX - entity.boundingBox.minX),
(entity.boundingBox.maxZ) - (entity.boundingBox.minZ)
).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).entityScaleModifier
model.posableModel = PokemonModelRepository.getPoser(entity.pokemon.species.resourceIdentifier, clientDelegate)
model.posableModel.context = model.context
model.setupEntityTypeContext(entity)
@@ -126,7 +134,7 @@ class PokemonRenderer(
clientDelegate
)
}
if(entity.platform != PlatformType.NONE) {
if (entity.platform != PlatformType.NONE) {
drawPlatform(
poseMatrix,
entity,
@@ -138,10 +146,34 @@ class PokemonRenderer(
poseMatrix.translate(0.0, 0.25 * (entity.delegate as PokemonClientDelegate).entityScaleModifier, 0.0)
}
modelNow.setLayerContext(buffer, clientDelegate, PokemonModelRepository.getLayers(entity.pokemon.species.resourceIdentifier, clientDelegate))
modelNow.setLayerContext(
buffer,
clientDelegate,
PokemonModelRepository.getLayers(entity.pokemon.species.resourceIdentifier, clientDelegate)
)
super.render(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
PokemonModelRepository.variations[entity.pokemon.species.resourceIdentifier]?.getResolvedModel(clientDelegate).let { parentModel ->
clientDelegate.locatorStates.keys.forEach { locatorKey ->
if (parentModel != null) {
ModelAppendageRenderer.render(
poseMatrix,
buffer,
packedLight,
entity,
0f,
0f,
entity.age.toFloat() + partialTicks,
entityYaw,
entity.lookAngle.x.toFloat(),
locatorKey,
model.context,
parentModel
)
}
}
}
modelNow.green = 1F
modelNow.blue = 1F
@@ -165,7 +197,7 @@ class PokemonRenderer(
val s = clientDelegate.secondsSinceBeamEffectStarted
if (beamMode == 3) {
if (s > BEAM_EXTEND_TIME) {
val value = (s - BEAM_EXTEND_TIME) / BEAM_SHRINK_TIME
val value = (s - BEAM_EXTEND_TIME) / BEAM_SHRINK_TIME
val colourValue = 1F - min(0.6F, value)
modelNow.green = colourValue
modelNow.blue = colourValue
@@ -181,7 +213,9 @@ class PokemonRenderer(
val lookVec = phaseTarget.lookAngle.yRot((PI / 2).toFloat()).multiply(1.0, 0.0, 1.0).normalize()
phaseTarget.getEyePosition(partialTicks).subtract(0.0, 0.4, 0.0).subtract(lookVec.scale(0.3))
} else {
val lookVec = phaseTarget.lookAngle.yRot((PI / 2 - (phaseTarget.visualRotationYInDegrees - phaseTarget.xRot).toRadians()).toFloat()).multiply(1.0, 0.0, 1.0).normalize()
val lookVec =
phaseTarget.lookAngle.yRot((PI / 2 - (phaseTarget.visualRotationYInDegrees - phaseTarget.xRot).toRadians()).toFloat())
.multiply(1.0, 0.0, 1.0).normalize()
phaseTarget.getEyePosition(partialTicks).subtract(0.0, 0.7, 0.0).subtract(lookVec.scale(0.4))
}
}
@@ -192,7 +226,8 @@ class PokemonRenderer(
clientDelegate.sendOutPosition = clientDelegate.sendOutPosition!!.add(0.0, 0.04, 0.0)
beamSourcePosition = clientDelegate.sendOutPosition!!
}
val offsetDirection = beamSourcePosition.subtract(entity.position()).normalize().scale(-clientDelegate.ballOffset.toDouble())
val offsetDirection =
beamSourcePosition.subtract(entity.position()).normalize().scale(-clientDelegate.ballOffset.toDouble())
val facingDir: Vec3
with(beamSourcePosition.subtract(entity.position())) {
var newOffset = offsetDirection.scale(2.0)
@@ -200,16 +235,17 @@ class PokemonRenderer(
newOffset = newOffset.scale((distance / 10.0) * 5)
facingDir = newOffset.normalize()
clientDelegate.sendOutOffset = newOffset
poseMatrix.translate(x+newOffset.x, y+newOffset.y, z+newOffset.z)
poseMatrix.translate(x + newOffset.x, y + newOffset.y, z + newOffset.z)
}
val dir = beamSourcePosition.subtract(entity.position()).normalize()
val angle = Mth.atan2(dir.z, dir.x) - PI / 2
poseMatrix.mulPose(Axis.YP.rotation(-angle.toFloat() + (180 * Math.PI / 180).toFloat()))
if (beamMode == 1 && !clientDelegate.ballDone){
if(entity.pokemon.caughtBall.name.toString().contains("beast")){
if (beamMode == 1 && !clientDelegate.ballDone) {
if (entity.pokemon.caughtBall.name.toString().contains("beast")) {
// get rotation angle on x-axis for facingDir
val xAngleFacingDir = Mth.atan2(facingDir.y, sqrt(facingDir.x * facingDir.x + facingDir.z * facingDir.z))
val xAngleFacingDir =
Mth.atan2(facingDir.y, sqrt(facingDir.x * facingDir.x + facingDir.z * facingDir.z))
poseMatrix.mulPose(Axis.XP.rotation(-xAngleFacingDir.toFloat()))
}
drawPokeBall(
@@ -221,7 +257,7 @@ class PokemonRenderer(
packedLight = packedLight,
ball = CobblemonClient.storage.myParty.firstOrNull { it?.uuid == entity.pokemon.uuid }?.caughtBall
?: clientDelegate.currentEntity.pokemon.caughtBall,
distance = ceil(beamSourcePosition.distanceTo(entity.position())/4f).toInt()
distance = ceil(beamSourcePosition.distanceTo(entity.position()) / 4f).toInt()
)
}
poseMatrix.popPose()
@@ -231,7 +267,8 @@ class PokemonRenderer(
}
override fun scale(pEntity: PokemonEntity, pPoseStack: PoseStack, pPartialTickTime: Float) {
val scale = pEntity.pokemon.form.baseScale * pEntity.pokemon.scaleModifier * (pEntity.delegate as PokemonClientDelegate).entityScaleModifier
val scale =
pEntity.pokemon.form.baseScale * pEntity.pokemon.scaleModifier * (pEntity.delegate as PokemonClientDelegate).entityScaleModifier
pPoseStack.scale(scale, scale, scale)
}
@@ -245,9 +282,17 @@ class PokemonRenderer(
* @param colour The colour of the beam.
* @param buffer The vertex consumer provider.
*/
fun renderBeam(matrixStack: PoseStack, partialTicks: Float, entity: PokemonEntity, beamTarget: Entity, buffer: MultiBufferSource, offset: Vec3) {
fun renderBeam(
matrixStack: PoseStack,
partialTicks: Float,
entity: PokemonEntity,
beamTarget: Entity,
buffer: MultiBufferSource,
offset: Vec3
) {
val clientDelegate = entity.delegate as PokemonClientDelegate
val pokemonPosition = entity.position().add(0.0, entity.bbHeight / 2.0 * clientDelegate.entityScaleModifier.toDouble(), 0.0)
val pokemonPosition =
entity.position().add(0.0, entity.bbHeight / 2.0 * clientDelegate.entityScaleModifier.toDouble(), 0.0)
var beamSourcePosition = if (beamTarget is EmptyPokeBallEntity) {
(beamTarget.delegate as PokeBallPosableState).locatorStates["beam"]?.getOrigin() ?: beamTarget.position()
} else {
@@ -257,7 +302,9 @@ class PokemonRenderer(
} else if (beamTarget is NPCEntity) {
(beamTarget.delegate as NPCClientDelegate).locatorStates["beam"]?.getOrigin() ?: beamTarget.position()
} else {
val lookVec = beamTarget.lookAngle.yRot((PI / 2 - (beamTarget.visualRotationYInDegrees - beamTarget.xRot).toRadians()).toFloat()).multiply(1.0, 0.0, 1.0).normalize()
val lookVec =
beamTarget.lookAngle.yRot((PI / 2 - (beamTarget.visualRotationYInDegrees - beamTarget.xRot).toRadians()).toFloat())
.multiply(1.0, 0.0, 1.0).normalize()
beamTarget.getEyePosition(partialTicks).subtract(0.0, 0.7, 0.0).subtract(lookVec.scale(0.4))
}
}
@@ -272,8 +319,9 @@ class PokemonRenderer(
// the further away the source position is, the smaller the newOffset should be. Max distance is 20 blocks
val distance = beamSourcePosition.distanceTo(entity.position())
newOffset = newOffset.scale((distance / 10.0) * 5)
newOffset = newOffset.multiply(0.0, 1+ease(clientDelegate.ballOffset.toDouble()), 0.0)
val direction = pokemonPosition.subtract(beamSourcePosition.add(newOffset)).let { Vector3f(it.x.toFloat(), it.y.toFloat(), it.z.toFloat()) }
newOffset = newOffset.multiply(0.0, 1 + ease(clientDelegate.ballOffset.toDouble()), 0.0)
val direction = pokemonPosition.subtract(beamSourcePosition.add(newOffset))
.let { Vector3f(it.x.toFloat(), it.y.toFloat(), it.z.toFloat()) }
matrixStack.pushPose()
with(beamSourcePosition.subtract(entity.position())) {
@@ -349,10 +397,10 @@ class PokemonRenderer(
}
val player = Minecraft.getInstance().player ?: return
val d = this.entityRenderDispatcher.distanceToSqr(entity)
if (d <= 4096.0){
if (d <= 4096.0) {
val scale = min(1.5, max(0.65, d.remap(DoubleRange(-16.0, 96.0), DoubleRange(0.0, 1.0))))
val sizeScale = Mth.lerp(scale.remap(DoubleRange(0.65, 1.5), DoubleRange(0.0,1.0)), 0.5, 1.0)
val offsetScale = Mth.lerp(scale.remap(DoubleRange(0.65, 1.5), DoubleRange(0.0,1.0)), 0.0,1.0)
val sizeScale = Mth.lerp(scale.remap(DoubleRange(0.65, 1.5), DoubleRange(0.0, 1.0)), 0.5, 1.0)
val offsetScale = Mth.lerp(scale.remap(DoubleRange(0.65, 1.5), DoubleRange(0.0, 1.0)), 0.0, 1.0)
val entityHeight = entity.boundingBox.ysize + 0.5f
matrices.pushPose()
matrices.translate(0.0, entityHeight, 0.0)
@@ -374,15 +422,48 @@ class PokemonRenderer(
var h = (-this.font.width(label) / 2).toFloat()
val y = 0F
val packedLight = LightTexture.pack(15, 15)
this.font.drawInBatch(label, h, y, 0x20FFFFFF, false, matrix4f, vertexConsumers, DisplayMode.SEE_THROUGH, opacity, packedLight)
this.font.drawInBatch(
label,
h,
y,
0x20FFFFFF,
false,
matrix4f,
vertexConsumers,
DisplayMode.SEE_THROUGH,
opacity,
packedLight
)
this.font.drawInBatch(label, h, y, -1, false, matrix4f, vertexConsumers, DisplayMode.NORMAL, 0, packedLight)
if (CobblemonClient.clientPlayerData.showChallengeLabel && entity.canBattle(player)) {
val sendOutBinding = PartySendBinding.boundKey().displayName
val battlePrompt = lang("challenge_label", sendOutBinding)
h = (-this.font.width(battlePrompt) / 2).toFloat()
this.font.drawInBatch(battlePrompt, h, y + 10, 0x20FFFFFF, false, matrix4f, vertexConsumers, DisplayMode.SEE_THROUGH, opacity, packedLight)
this.font.drawInBatch(battlePrompt, h, y + 10, -1, false, matrix4f, vertexConsumers, DisplayMode.NORMAL, 0, packedLight)
this.font.drawInBatch(
battlePrompt,
h,
y + 10,
0x20FFFFFF,
false,
matrix4f,
vertexConsumers,
DisplayMode.SEE_THROUGH,
opacity,
packedLight
)
this.font.drawInBatch(
battlePrompt,
h,
y + 10,
-1,
false,
matrix4f,
vertexConsumers,
DisplayMode.NORMAL,
0,
packedLight
)
}
matrices.popPose()
}
@@ -391,7 +472,10 @@ class PokemonRenderer(
private fun resolveBaseLabel(entity: PokemonEntity): MutableComponent {
return when {
!ServerSettings.displayEntityNameLabel -> Component.empty()
Cobblemon.config.displayNameForUnknownPokemon || CobblemonClient.clientPokedexData.getKnowledgeForSpecies(entity.pokemon.species.resourceIdentifier) != PokedexEntryProgress.NONE -> entity.name.copy()
Cobblemon.config.displayNameForUnknownPokemon || CobblemonClient.clientPokedexData.getKnowledgeForSpecies(
entity.pokemon.species.resourceIdentifier
) != PokedexEntryProgress.NONE -> entity.name.copy()
else -> Component.literal(HIDDEN_NAME)
}
}
@@ -416,7 +500,7 @@ class PokemonRenderer(
} else {
matrixStack.translate(0.0, -0.2, 0.0)
val rot = 360F * distance
if(ball.name.toString().contains("beast")){
if (ball.name.toString().contains("beast")) {
matrixStack.mulPose(Axis.ZN.rotationDegrees(Math.lerp(0F, rot, scale)))
} else {
matrixStack.mulPose(Axis.XN.rotationDegrees(Math.lerp(0F, rot, scale)))
@@ -440,12 +524,12 @@ class PokemonRenderer(
}
private fun drawPlatform(
matrixStack: PoseStack,
entity: PokemonEntity,
scale: Float = 1F,
buff: MultiBufferSource,
packedLight: Int,
) {
matrixStack: PoseStack,
entity: PokemonEntity,
scale: Float = 1F,
buff: MultiBufferSource,
packedLight: Int,
) {
val (modelResource, textureResource) = PlatformType.getModelWithTexture(entity.platform)
val model = MiscModelRepository.modelOf(modelResource) ?: return
matrixStack.pushPose()
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.net.messages.client.settings.OpenCobblemonConfigScreenPacket
import com.cobblemon.mod.common.util.commandLang
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands
import net.minecraft.server.level.ServerPlayer
object CobblemonConfigCommand {
fun register(dispatcher: CommandDispatcher<CommandSourceStack>) {
dispatcher.register(
Commands.literal("cobblemonconfig")
.executes {
it.source.playerOrException.sendPacket(OpenCobblemonConfigScreenPacket())
Command.SINGLE_SUCCESS
}
.then(
Commands.literal("reload")
.permission(CobblemonPermissions.COBBLEMON_CONFIG_RELOAD)
.executes {
Cobblemon.reloadConfig()
it.source.server.playerList.players.forEach { player ->
if (player is ServerPlayer) {
Cobblemon.sendServerSettingsPacketToPlayer(player)
}
}
it.source.sendSuccess({ commandLang("cobblemon_config.reload") }, true)
Command.SINGLE_SUCCESS
}
)
)
}
}
@@ -8,15 +8,15 @@
package com.cobblemon.mod.common.config
enum class Category {
Starter,
Pokedex,
Pokemon,
Spawning,
Battles,
PassiveStatus,
Healing,
Storage,
World,
Debug
enum class Category(val lang: String) {
Pokemon("pokemon"),
Spawning("spawning"),
Battles("battles"),
PassiveStatus("passive_status"),
Healing("healing"),
World("world"),
Pokedex("pokedex"),
Storage("storage"),
Starter("starter"),
Debug("debug"),
}
@@ -16,6 +16,8 @@ import com.cobblemon.mod.common.pokeball.catching.calculators.CobblemonCaptureCa
import com.cobblemon.mod.common.util.adapters.CaptureCalculatorAdapter
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
import com.google.gson.GsonBuilder
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import com.google.gson.annotations.SerializedName
class CobblemonConfig {
@@ -31,114 +33,112 @@ class CobblemonConfig {
var lastSavedVersion: String = "0.0.1"
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "max_pokemon_level")
@IntConstraint(min = 1, max = 1000)
var maxPokemonLevel = 100
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "max_pokemon_friendship")
@IntConstraint(min = 0, max = 1000)
var maxPokemonFriendship = 255
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "announce_drop_items")
var announceDropItems = true
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "default_drop_item_method")
var defaultDropItemMethod = ItemDropMethod.ON_ENTITY
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "ambient_pokemon_cry_ticks")
@LastChangedVersion("1.4.0")
var ambientPokemonCryTicks = 1080
@NodeCategory(Category.Storage)
@CobblemonConfigField(Category.Storage, lang = "default_box_count")
@IntConstraint(min = 1, max = 1000)
var defaultBoxCount = 30
@NodeCategory(Category.Storage)
@CobblemonConfigField(Category.Storage, lang = "pokemon_save_interval_seconds")
@IntConstraint(min = 1, max = 120)
var pokemonSaveIntervalSeconds = 30
@NodeCategory(Category.Storage)
@CobblemonConfigField(Category.Storage, lang = "storage_format")
var storageFormat = "nbt"
@NodeCategory(Category.Storage)
@CobblemonConfigField(Category.Storage, lang = "prevent_complete_party_deposit")
var preventCompletePartyDeposit = false
@NodeCategory(Category.Storage)
@CobblemonConfigField(Category.Storage, lang = "mongo_db_connection_string")
var mongoDBConnectionString = "mongodb://localhost:27017"
@NodeCategory(Category.Storage)
@CobblemonConfigField(Category.Storage, lang = "mongo_db_database_name")
var mongoDBDatabaseName = "cobblemon"
// TODO new types of constraint
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "max_vertical_correction_blocks")
@IntConstraint(min = 1, max = 200)
var maxVerticalCorrectionBlocks = 64
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "minimum_level_range_max")
@IntConstraint(min = 1, max = 1000)
var minimumLevelRangeMax = 10
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "enable_spawning")
var enableSpawning = true
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "minimum_distance_between_entities")
var minimumDistanceBetweenEntities = 8.0
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "max_nearby_blocks_horizontal_range")
var maxNearbyBlocksHorizontalRange = 4
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "max_nearby_blocks_vertical_range")
var maxNearbyBlocksVerticalRange = 2
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "max_vertical_space")
var maxVerticalSpace = 8
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "world_slice_diameter")
var worldSliceDiameter = 8
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "world_slice_height")
var worldSliceHeight = 16
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "ticks_between_spawn_attempts")
var ticksBetweenSpawnAttempts = 20F
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "minimum_slice_distance_from_player")
var minimumSliceDistanceFromPlayer = 16F
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "maximum_slice_distance_from_player")
var maximumSliceDistanceFromPlayer = 16 * 4F
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "export_spawn_config")
var exportSpawnConfig = false
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "save_pokemon_to_world")
var savePokemonToWorld = true
@NodeCategory(Category.Starter)
@CobblemonConfigField(Category.Starter, lang = "export_starter_config")
var exportStarterConfig = false
@NodeCategory(Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "auto_update_showdown")
var autoUpdateShowdown = true
@NodeCategory(Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "default_flee_distance")
var defaultFleeDistance = 16F * 2
@NodeCategory(category = Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "allow_experience_from_pvp")
var allowExperienceFromPvP = true
@NodeCategory(category = Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "experience_share_multiplier")
var experienceShareMultiplier = .5
@NodeCategory(category = Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "lucky_egg_multiplier")
var luckyEggMultiplier = 1.5
@NodeCategory(category = Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "allow_spectating")
var allowSpectating = true
@NodeCategory(category = Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "experience_multiplier")
var experienceMultiplier = 2F
@NodeCategory(category = Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "pokemon_per_chunk")
var pokemonPerChunk = 1F
@NodeCategory(Category.PassiveStatus)
@CobblemonConfigField(Category.PassiveStatus, lang = "passive_statuses")
var passiveStatuses = mutableMapOf(
Statuses.POISON.configEntry(),
Statuses.POISON_BADLY.configEntry(),
@@ -148,110 +148,124 @@ class CobblemonConfig {
Statuses.BURN.configEntry()
)
@NodeCategory(Category.Healing)
@CobblemonConfigField(Category.Healing, lang = "infinite_healer_charge")
var infiniteHealerCharge = false
@NodeCategory(Category.Healing)
@CobblemonConfigField(Category.Healing, lang = "max_healer_charge")
var maxHealerCharge = 6.0f
@NodeCategory(Category.Healing)
var chargeGainedPerTick = 0.000333333f
@CobblemonConfigField(Category.Healing, lang = "seconds_to_charge_healing_machine")
var secondsToChargeHealingMachine = 900.0
@NodeCategory(Category.Healing)
@CobblemonConfigField(Category.Healing, lang = "default_faint_timer")
var defaultFaintTimer = 300
@NodeCategory(Category.Healing)
@CobblemonConfigField(Category.Healing, lang = "faint_awaken_health_percent")
var faintAwakenHealthPercent = 0.2f
@NodeCategory(Category.Healing)
@CobblemonConfigField(Category.Healing, lang = "heal_percent")
var healPercent = 0.05
@NodeCategory(Category.Healing)
@CobblemonConfigField(Category.Healing, lang = "heal_timer")
var healTimer = 60
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "base_apricorn_tree_generation_chance")
var baseApricornTreeGenerationChance = 0.1F
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_level_label")
var displayEntityLevelLabel = true
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_name_label")
var displayEntityNameLabel = true
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "display_name_for_unknown_pokemon")
var displayNameForUnknownPokemon = false
@NodeCategory(Category.Pokemon)
var displayEntityLabelsWhenCrouchingOnly = false
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_labels_when_crouching_only")
var displayEntityLabelsWhenCrouchingOnly = true
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "shiny_rate")
var shinyRate = 8192F
@NodeCategory(Category.Pokemon)
val shinyNoticeParticlesDistance = 24F
@CobblemonConfigField(Category.Pokemon, lang = "shiny_notice_particles_distance")
var shinyNoticeParticlesDistance = 24F
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "capture_calculator")
var captureCalculator: CaptureCalculator = CobblemonCaptureCalculator
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "player_damage_pokemon")
var playerDamagePokemon = true
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "apple_leftovers_chance")
var appleLeftoversChance = 0.025
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "max_roots_in_area")
var maxRootsInArea = 5
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "big_root_propagation_chance")
var bigRootPropagationChance = 0.1
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "energy_root_chance")
var energyRootChance = 0.25
@NodeCategory(Category.Pokemon)
@CobblemonConfigField(Category.Pokemon, lang = "max_dynamax_level")
@IntConstraint(min = 0, max = 10)
var maxDynamaxLevel = 10
@NodeCategory(Category.Spawning)
@CobblemonConfigField(Category.Spawning, lang = "tera_type_rate")
var teraTypeRate = 20F
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "default_pastured_pokemon_limit")
var defaultPasturedPokemonLimit = 16
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "pasture_block_update_ticks")
var pastureBlockUpdateTicks = 40
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "pasture_max_wander_distance")
var pastureMaxWanderDistance = 64
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "pasture_max_per_chunk")
var pastureMaxPerChunk = 4F
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "max_inserted_fossil_items")
var maxInsertedFossilItems = 2
@NodeCategory(Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "walking_in_battle_animations")
var walkingInBattleAnimations = false
@NodeCategory(Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "battle_wild_max_distance")
var battleWildMaxDistance = 12F
@NodeCategory(Category.World)
@CobblemonConfigField(Category.World, lang = "trade_max_distance")
var tradeMaxDistance = 12F
@NodeCategory(Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "battle_pvp_max_distance")
@SerializedName("battlePvPMaxDistance", alternate = ["BattlePvPMaxDistance"])
var battlePvPMaxDistance = 32F
@NodeCategory(Category.Battles)
@CobblemonConfigField(Category.Battles, lang = "battle_spectate_max_distance")
var battleSpectateMaxDistance = 64F
@NodeCategory(Category.Pokedex)
@CobblemonConfigField(Category.Pokedex, lang = "max_pokedex_scanning_detection_range")
var maxPokedexScanningDetectionRange = 10.0
@NodeCategory(Category.Pokedex)
@CobblemonConfigField(Category.Pokedex, lang = "hide_unimplemented_pokemon_in_the_pokedex")
var hideUnimplementedPokemonInThePokedex = false
@NodeCategory(Category.Debug)
@CobblemonConfigField(Category.Debug, lang = "enable_debug_keys")
var enableDebugKeys = false
fun clone(): CobblemonConfig {
val newConfig = CobblemonConfig()
CobblemonConfig::class.memberProperties.forEach { property ->
if (property is kotlin.reflect.KMutableProperty<*>) {
property.isAccessible = true
val value = property.getter.call(this)
if (value != null) {
property.setter.call(newConfig, value)
}
}
}
return newConfig
}
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.config
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class NodeCategory(
val category: Category
annotation class CobblemonConfigField(
val category: Category,
val lang: String,
)
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.config.constraint
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class IntConstraint(
val min : Int,
val max : Int
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.config.starter
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.config.Category
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.NodeCategory
import com.cobblemon.mod.common.config.CobblemonConfigField
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.google.gson.GsonBuilder
@@ -24,14 +24,14 @@ class StarterConfig {
.create()
}
@NodeCategory(Category.Starter)
@CobblemonConfigField(Category.Starter, lang = "allow_starter_on_join")
var allowStarterOnJoin = true
@NodeCategory(Category.Starter)
@CobblemonConfigField(Category.Starter, lang = "prompt_starter_once_only")
@LastChangedVersion("1.5.0")
var promptStarterOnceOnly = true
@NodeCategory(Category.Starter)
@CobblemonConfigField(Category.Starter, lang = "starters")
var starters = mutableListOf(
StarterCategory(
name = "Kanto",
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMechanics
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.stats.ItemEvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.pokemon.Pokemon
@@ -44,10 +45,8 @@ class FriendshipRaisingBerryItem(block: BerryBlock, val stat: Stat) : BerryItem(
val increasedFriendship = pokemon.incrementFriendship(friendshipRaiseAmount)
val currentStat = pokemon.evs.getOrDefault(stat)
val newEV = max(currentStat - genericRuntime.resolveInt(CobblemonMechanics.berries.evLowerAmount), 0)
pokemon.setEV(stat, newEV)
val decreasedEVs = currentStat != pokemon.evs.getOrDefault(stat)
val evLowerAmount = max(genericRuntime.resolveInt(CobblemonMechanics.berries.evLowerAmount), 0)
val decreasedEVs = pokemon.evs.add(stat, evLowerAmount, ItemEvSource(player, stack, pokemon)) != 0
return if (increasedFriendship || decreasedEVs) {
player.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.item.interactive
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.stats.ItemEvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.pokemon.EVs
@@ -35,7 +36,7 @@ class FeatherItem(val stat: Stat) : CobblemonItem(Properties()), PokemonSelectin
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack> {
val evsGained = pokemon.evs.add(stat, EV_YIELD)
val evsGained = pokemon.evs.add(stat, EV_YIELD, ItemEvSource(player, stack, pokemon))
return if (evsGained > 0) {
pokemon.entity?.playSound(CobblemonSounds.MEDICINE_FEATHER_USE, 1F, 1F)
if (!player.isCreative) {
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.item.interactive
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.stats.ItemEvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.pokemon.EVs
@@ -35,7 +36,7 @@ class VitaminItem(val stat: Stat) : CobblemonItem(Properties()), PokemonSelectin
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack> {
val evsGained = pokemon.evs.add(stat, EV_YIELD)
val evsGained = pokemon.evs.add(stat, EV_YIELD, ItemEvSource(player, stack, pokemon))
return if (evsGained > 0) {
pokemon.entity?.playSound(CobblemonSounds.MEDICINE_PILLS_USE, 1F, 1F)
if (!player.isCreative) {
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.settings
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class OpenCobblemonConfigScreenPacket: NetworkPacket<OpenCobblemonConfigScreenPacket> {
companion object {
val ID = cobblemonResource("open_cobblemon_config_screen")
fun decode(buffer: RegistryFriendlyByteBuf) = OpenCobblemonConfigScreenPacket()
}
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
}
}
@@ -26,7 +26,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
* @author Hiroku
* @since June 20th, 2022
*/
class OpenPCPacket(val storeID: UUID, val box: Int = 1) : NetworkPacket<OpenPCPacket> {
class OpenPCPacket(val storeID: UUID, val box: Int = 0) : NetworkPacket<OpenPCPacket> {
override val id = ID
@@ -9,6 +9,9 @@
package com.cobblemon.mod.common.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.EvGainedEvent
import com.cobblemon.mod.common.api.pokemon.stats.EvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
@@ -35,19 +38,50 @@ class EVs : PokemonStats() {
* @param value The amount to attempt to add, this wil safely be coerced to the highest possible value.
* @return The amount added or 0 if the addition was impossible.
*/
@Deprecated(
message = "Please update to modern API",
replaceWith = ReplaceWith("add(key, value, source)", "com.cobblemon.mod.common.api.pokemon.stats.Stat", "com.cobblemon.mod.common.api.pokemon.stats.EvSource"),
level = DeprecationLevel.ERROR
)
fun add(key: Stat, value: Int): Int {
return this.performAdd(key, value)
}
/**
* Safely adds the given amount of EVs to this store.
*
* @param key The [Stat] being mutated.
* @param value The amount to attempt to add, this wil safely be coerced to the highest possible value.
* @param source The [EvSource] of this addition.
* @return The amount added or 0 if the addition was impossible.
*/
fun add(key: Stat, value: Int, source: EvSource): Int {
CobblemonEvents.EV_GAINED_EVENT_PRE.postThen(
event = EvGainedEvent.Pre(key, value, source),
ifCanceled = { return 0 },
ifSucceeded = { event ->
val result = this.performAdd(event.stat, event.amount)
CobblemonEvents.EV_GAINED_EVENT_POST.post(EvGainedEvent.Post(key, result, source))
return result
}
)
// This never runs, just the impl of the observables doesn't allow us to infer the actual return
return 0
}
private fun performAdd(stat: Stat, value: Int): Int {
val currentTotal = this.sumOf { it.value }
if (currentTotal == MAX_TOTAL_VALUE && value > 0) {
return 0
}
val currentStat = this.getOrDefault(key)
val currentStat = this.getOrDefault(stat)
val possibleForStat = MAX_STAT_VALUE - currentStat
val possibleForTotal = MAX_TOTAL_VALUE - currentTotal
val coercedValue = value.coerceIn(-currentStat, min(possibleForStat, possibleForTotal))
val newValue = currentStat + coercedValue
// avoid unnecessary updates
if (newValue != currentStat) {
this[key] = newValue
this[stat] = newValue
return coercedValue
}
return 0
@@ -0,0 +1,564 @@
{
"format_version": "1.8.0",
"animations": {
"animation.egg.idle": {
"loop": true,
"bones": {
"egg": {
"rotation": [0, 0, 0],
"position": [0, 0, 0]
},
"body": {
"rotation": [0, 0, 0],
"position": [0, 0, 0]
},
"egg_shell1": {
"position": [0, "math.sin(q.anim_time*90+1)", 0]
},
"egg_shell2": {
"position": [0, "math.sin(q.anim_time*90+2)", 0]
},
"egg_shell3": {
"position": [0, "math.sin(q.anim_time*90+3)", 0]
},
"egg_shell4": {
"position": [0, "math.sin(q.anim_time*90+4)", 0]
},
"egg_shell5": {
"position": [0, "math.sin(q.anim_time*90+5)", 0]
},
"egg_shell6": {
"position": [0, "math.sin(q.anim_time*90+6)", 0]
},
"egg_shell7": {
"position": [0, "math.sin(q.anim_time*90+7)", 0]
},
"egg_shell8": {
"position": [0, "math.sin(q.anim_time*90+8)", 0]
},
"egg_shell9": {
"position": [0, "math.sin(q.anim_time*90+9)", 0]
},
"egg_shell10": {
"position": [0, "math.sin(q.anim_time*90+10)", 0]
},
"egg_shell11": {
"position": [0, "math.sin(q.anim_time*90+11)", 0]
},
"egg_shell12": {
"position": [0, "math.sin(q.anim_time*90+12)", 0]
},
"egg_shell13": {
"position": [0, "math.sin(q.anim_time*90+13)", 0]
},
"egg_shell14": {
"position": [0, "math.sin(q.anim_time*90+14)", 0]
},
"egg_shell15": {
"position": [0, "math.sin(q.anim_time*90+15)", 0]
},
"egg_shell16": {
"position": [0, "math.sin(q.anim_time*90+16)", 0]
},
"egg_shell17": {
"position": [0, "math.sin(q.anim_time*90+17)", 0]
},
"egg_shell18": {
"position": [0, "math.sin(q.anim_time*90+18)", 0]
},
"egg_shell19": {
"position": [0, "math.sin(q.anim_time*90+19)", 0]
},
"egg_shell20": {
"position": [0, "math.sin(q.anim_time*90+20)", 0]
},
"egg_shell21": {
"position": [0, "math.sin(q.anim_time*90+21)", 0]
},
"egg_shell22": {
"position": [0, "math.sin(q.anim_time*90+22)", 0]
},
"egg_shell23": {
"position": [0, "math.sin(q.anim_time*90+23)", 0]
},
"egg_shell24": {
"position": [0, "math.sin(q.anim_time*90+24)", 0]
},
"egg_shell25": {
"position": [0, "math.sin(q.anim_time*90+25)", 0]
},
"egg_shell26": {
"position": [0, "math.sin(q.anim_time*90+26)", 0]
},
"egg_shell27": {
"position": [0, "math.sin(q.anim_time*90+27)", 0]
},
"egg_shell28": {
"position": [0, "math.sin(q.anim_time*90+28)", 0]
},
"egg_shell29": {
"position": [0, "math.sin(q.anim_time*90+29)", 0]
},
"egg_shell30": {
"position": [0, "math.sin(q.anim_time*90+30)", 0]
},
"egg_shell31": {
"position": [0, "math.sin(q.anim_time*90+31)", 0]
},
"egg_shell32": {
"position": [0, "math.sin(q.anim_time*90+32)", 0]
},
"egg_shell33": {
"position": [0, "math.sin(q.anim_time*90+33)", 0]
},
"egg_shell34": {
"position": [0, "math.sin(q.anim_time*90+34)", 0]
},
"egg_shell35": {
"position": [0, "math.sin(q.anim_time*90+35)", 0]
},
"egg_shell36": {
"position": [0, "math.sin(q.anim_time*90+36)", 0]
},
"egg_shell37": {
"position": [0, "math.sin(q.anim_time*90+37)", 0]
},
"egg_shell38": {
"position": [0, "math.sin(q.anim_time*90+38)", 0]
},
"egg_shell39": {
"position": [0, "math.sin(q.anim_time*90+39)", 0]
},
"egg_shell40": {
"position": [0, "math.sin(q.anim_time*90+40)", 0]
},
"egg_shell41": {
"position": [0, "math.sin(q.anim_time*90+41)", 0]
},
"egg_shell42": {
"position": [0, "math.sin(q.anim_time*90+42)", 0]
},
"egg_shell43": {
"position": [0, "math.sin(q.anim_time*90+43)", 0]
},
"egg_shell44": {
"position": [0, "math.sin(q.anim_time*90+44)", 0]
},
"egg_shell45": {
"position": [0, "math.sin(q.anim_time*90+45)", 0]
},
"egg_shell46": {
"position": [0, "math.sin(q.anim_time*90+46)", 0]
},
"egg_shell47": {
"position": [0, "math.sin(q.anim_time*90+47)", 0]
},
"egg_shell48": {
"position": [0, "math.sin(q.anim_time*90+48)", 0]
},
"egg_shell49": {
"position": [0, "math.sin(q.anim_time*90+49)", 0]
},
"egg_shell50": {
"position": [0, "math.sin(q.anim_time*90+50)", 0]
},
"egg_shell51": {
"position": [0, "math.sin(q.anim_time*90+51)", 0]
},
"egg_shell52": {
"position": [0, "math.sin(q.anim_time*90+52)", 0]
},
"egg_shell53": {
"position": [0, "math.sin(q.anim_time*90+53)", 0]
},
"egg_shell54": {
"position": [0, "math.sin(q.anim_time*90+54)", 0]
},
"egg_shell55": {
"position": [0, "math.sin(q.anim_time*90+55)", 0]
},
"egg_shell56": {
"position": [0, "math.sin(q.anim_time*90+56)", 0]
},
"egg_shell57": {
"position": [0, "math.sin(q.anim_time*90+57)", 0]
},
"egg_shell58": {
"position": [0, "math.sin(q.anim_time*90+58)", 0]
},
"egg_shell59": {
"position": [0, "math.sin(q.anim_time*90+59)", 0]
},
"egg_shell60": {
"position": [0, "math.sin(q.anim_time*90+60)", 0]
},
"egg_shell61": {
"position": [0, "math.sin(q.anim_time*90+61)", 0]
},
"egg_shell62": {
"position": [0, "math.sin(q.anim_time*90+62)", 0]
},
"egg_shell63": {
"position": [0, "math.sin(q.anim_time*90+63)", 0]
},
"egg_shell64": {
"position": [0, "math.sin(q.anim_time*90+64)", 0]
},
"egg_shell65": {
"position": [0, "math.sin(q.anim_time*90+65)", 0]
},
"egg_shell66": {
"position": [0, "math.sin(q.anim_time*90+66)", 0]
},
"egg_shell67": {
"position": [0, "math.sin(q.anim_time*90+67)", 0]
},
"egg_shell68": {
"position": [0, "math.sin(q.anim_time*90+68)", 0]
},
"egg_shell69": {
"position": [0, "math.sin(q.anim_time*90+69)", 0]
},
"egg_shell70": {
"position": [0, "math.sin(q.anim_time*90+70)", 0]
},
"egg_shell71": {
"position": [0, "math.sin(q.anim_time*90+71)", 0]
},
"egg_shell72": {
"position": [0, "math.sin(q.anim_time*90+72)", 0]
},
"egg_shell73": {
"position": [0, "math.sin(q.anim_time*90+73)", 0]
},
"egg_shell74": {
"position": [0, "math.sin(q.anim_time*90+74)", 0]
},
"egg_shell75": {
"position": [0, "math.sin(q.anim_time*90+75)", 0]
},
"egg_shell76": {
"position": [0, "math.sin(q.anim_time*90+76)", 0]
},
"egg_shell77": {
"position": [0, "math.sin(q.anim_time*90+77)", 0]
},
"egg_shell78": {
"position": [0, "math.sin(q.anim_time*90+78)", 0]
},
"egg_shell79": {
"position": [0, "math.sin(q.anim_time*90+79)", 0]
},
"egg_shell80": {
"position": [0, "math.sin(q.anim_time*90+80)", 0]
},
"egg_shell81": {
"position": [0, "math.sin(q.anim_time*90+81)", 0]
},
"egg_shell82": {
"position": [0, "math.sin(q.anim_time*90+82)", 0]
},
"egg_shell83": {
"position": [0, "math.sin(q.anim_time*90+83)", 0]
},
"egg_shell84": {
"position": [0, "math.sin(q.anim_time*90+84)", 0]
},
"egg_shell85": {
"position": [0, "math.sin(q.anim_time*90+85)", 0]
},
"egg_shell86": {
"position": [0, "math.sin(q.anim_time*90+86)", 0]
},
"egg_shell87": {
"position": [0, "math.sin(q.anim_time*90+87)", 0]
},
"egg_shell88": {
"position": [0, "math.sin(q.anim_time*90+88)", 0]
},
"egg_shell89": {
"position": [0, "math.sin(q.anim_time*90+89)", 0]
},
"egg_shell90": {
"position": [0, "math.sin(q.anim_time*90+90)", 0]
},
"egg_shell91": {
"position": [0, "math.sin(q.anim_time*90+91)", 0]
},
"egg_shell92": {
"position": [0, "math.sin(q.anim_time*90+92)", 0]
},
"egg_shell93": {
"position": [0, "math.sin(q.anim_time*90+93)", 0]
},
"egg_shell94": {
"position": [0, "math.sin(q.anim_time*90+94)", 0]
},
"egg_shell95": {
"position": [0, "math.sin(q.anim_time*90+95)", 0]
},
"egg_shell96": {
"position": [0, "math.sin(q.anim_time*90+96)", 0]
},
"egg_shell97": {
"position": [0, "math.sin(q.anim_time*90+97)", 0]
},
"egg_shell98": {
"position": [0, "math.sin(q.anim_time*90+98)", 0]
},
"egg_shell99": {
"position": [0, "math.sin(q.anim_time*90+99)", 0]
},
"egg_shell100": {
"position": [0, "math.sin(q.anim_time*90+100)", 0]
},
"egg_shell105": {
"position": [0, "math.sin(q.anim_time*90+105+1)", 0]
},
"egg_shell106": {
"position": [0, "math.sin(q.anim_time*90+106+1)", 0]
},
"egg_shell107": {
"position": [0, "math.sin(q.anim_time*90+107+1)", 0]
},
"egg_shell108": {
"position": [0, "math.sin(q.anim_time*90+108+1)", 0]
},
"egg_shell109": {
"position": [0, "math.sin(q.anim_time*90+109+1)", 0]
},
"egg_shell110": {
"position": [0, "math.sin(q.anim_time*90+110+1)", 0]
},
"egg_shell111": {
"position": [0, "math.sin(q.anim_time*90+111+1)", 0]
},
"egg_shell112": {
"position": [0, "math.sin(q.anim_time*90+112+1)", 0]
},
"egg_shell113": {
"position": [0, "math.sin(q.anim_time*90+113+1)", 0]
},
"egg_shell114": {
"position": [0, "math.sin(q.anim_time*90+114+1)", 0]
},
"egg_shell115": {
"position": [0, "math.sin(q.anim_time*90+115+1)", 0]
},
"egg_shell116": {
"position": [0, "math.sin(q.anim_time*90+116+1)", 0]
},
"egg_shell117": {
"position": [0, "math.sin(q.anim_time*90+117+1)", 0]
},
"egg_shell118": {
"position": [0, "math.sin(q.anim_time*90+118+1)", 0]
},
"egg_shell119": {
"position": [0, "math.sin(q.anim_time*90+119+1)", 0]
},
"egg_shell120": {
"position": [0, "math.sin(q.anim_time*90+120+1)", 0]
},
"egg_shell121": {
"position": [0, "math.sin(q.anim_time*90+121+1)", 0]
},
"egg_shell123": {
"position": [0, "math.sin(q.anim_time*90+123+1)", 0]
},
"egg_shell124": {
"position": [0, "math.sin(q.anim_time*90+124+1)", 0]
},
"egg_shell125": {
"position": [0, "math.sin(q.anim_time*90+125+1)", 0]
},
"egg_shell126": {
"position": [0, "math.sin(q.anim_time*90+126+1)", 0]
},
"egg_shell127": {
"position": [0, "math.sin(q.anim_time*90+127+1)", 0]
},
"egg_shell128": {
"position": [0, "math.sin(q.anim_time*90+128+1)", 0]
},
"egg_shell129": {
"position": [0, "math.sin(q.anim_time*90+129+1)", 0]
},
"egg_shell130": {
"position": [0, "math.sin(q.anim_time*90+130+1)", 0]
},
"egg_shell131": {
"position": [0, "math.sin(q.anim_time*90+131+1)", 0]
},
"egg_shell132": {
"position": [0, "math.sin(q.anim_time*90+132+1)", 0]
},
"egg_shell133": {
"position": [0, "math.sin(q.anim_time*90+133+1)", 0]
},
"egg_shell134": {
"position": [0, "math.sin(q.anim_time*90+134+1)", 0]
},
"egg_shell135": {
"position": [0, "math.sin(q.anim_time*90+135+1)", 0]
},
"egg_shell136": {
"position": [0, "math.sin(q.anim_time*90+136+1)", 0]
},
"egg_shell137": {
"position": [0, "math.sin(q.anim_time*90+137+1)", 0]
},
"egg_shell138": {
"position": [0, "math.sin(q.anim_time*90+138+1)", 0]
},
"egg_shell139": {
"position": [0, "math.sin(q.anim_time*90+139+1)", 0]
},
"egg_shell140": {
"position": [0, "math.sin(q.anim_time*90+140+1)", 0]
},
"egg_shell141": {
"position": [0, "math.sin(q.anim_time*90+141+1)", 0]
},
"egg_shell142": {
"position": [0, "math.sin(q.anim_time*90+142+1)", 0]
},
"egg_shell143": {
"position": [0, "math.sin(q.anim_time*90+143+1)", 0]
},
"egg_shell144": {
"position": [0, "math.sin(q.anim_time*90+144+1)", 0]
},
"egg_shell145": {
"position": [0, "math.sin(q.anim_time*90+145+1)", 0]
},
"egg_shell146": {
"position": [0, "math.sin(q.anim_time*90+146+1)", 0]
},
"egg_shell147": {
"position": [0, "math.sin(q.anim_time*90+147+1)", 0]
},
"egg_shell148": {
"position": [0, "math.sin(q.anim_time*90+148+1)", 0]
},
"egg_shell149": {
"position": [0, "math.sin(q.anim_time*90+149+1)", 0]
},
"egg_shell150": {
"position": [0, "math.sin(q.anim_time*90+150+1)", 0]
},
"egg_shell151": {
"position": [0, "math.sin(q.anim_time*90+151+1)", 0]
},
"egg_shell152": {
"position": [0, "math.sin(q.anim_time*90+152+1)", 0]
},
"egg_shell153": {
"position": [0, "math.sin(q.anim_time*90+153+1)", 0]
},
"egg_shell154": {
"position": [0, "math.sin(q.anim_time*90+154+1)", 0]
},
"egg_shell155": {
"position": [0, "math.sin(q.anim_time*90+155+1)", 0]
},
"egg_shell156": {
"position": [0, "math.sin(q.anim_time*90+156+1)", 0]
},
"egg_shell157": {
"position": [0, "math.sin(q.anim_time*90+157+1)", 0]
},
"egg_shell158": {
"position": [0, "math.sin(q.anim_time*90+158+1)", 0]
},
"egg_shell159": {
"position": [0, "math.sin(q.anim_time*90+159+1)", 0]
},
"egg_shell160": {
"position": [0, "math.sin(q.anim_time*90+160+1)", 0]
},
"egg_shell161": {
"position": [0, "math.sin(q.anim_time*90+161+1)", 0]
},
"egg_shell162": {
"position": [0, "math.sin(q.anim_time*90+162+1)", 0]
},
"egg_shell163": {
"position": [0, "math.sin(q.anim_time*90+163+1)", 0]
},
"egg_shell164": {
"position": [0, "math.sin(q.anim_time*90+164+1)", 0]
},
"egg_shell165": {
"position": [0, "math.sin(q.anim_time*90+165+1)", 0]
},
"egg_shell166": {
"position": [0, "math.sin(q.anim_time*90+166+1)", 0]
},
"egg_shell167": {
"position": [0, "math.sin(q.anim_time*90+167+1)", 0]
},
"egg_shell168": {
"position": [0, "math.sin(q.anim_time*90+168+1)", 0]
},
"egg_shell169": {
"position": [0, "math.sin(q.anim_time*90+169+1)", 0]
},
"egg_shell170": {
"position": [0, "math.sin(q.anim_time*90+170+1)", 0]
},
"egg_shell171": {
"position": [0, "math.sin(q.anim_time*90+171+1)", 0]
},
"egg_shell172": {
"position": [0, "math.sin(q.anim_time*90+172+1)", 0]
},
"egg_shell173": {
"position": [0, "math.sin(q.anim_time*90+173+1)", 0]
},
"egg_shell174": {
"position": [0, "math.sin(q.anim_time*90+174+1)", 0]
},
"egg_shell175": {
"position": [0, "math.sin(q.anim_time*90+175+1)", 0]
},
"egg_shell176": {
"position": [0, "math.sin(q.anim_time*90+176+1)", 0]
},
"egg_shell177": {
"position": [0, "math.sin(q.anim_time*90+177+1)", 0]
},
"egg_shell178": {
"position": [0, "math.sin(q.anim_time*90+178+1)", 0]
},
"egg_shell179": {
"position": [0, "math.sin(q.anim_time*90+179+1)", 0]
},
"egg_shell180": {
"position": [0, "math.sin(q.anim_time*90+180+1)", 0]
},
"egg_shell181": {
"position": [0, "math.sin(q.anim_time*90+181+1)", 0]
},
"egg_shell182": {
"position": [0, "math.sin(q.anim_time*90+182+1)", 0]
},
"egg_shell183": {
"position": [0, "math.sin(q.anim_time*90+183+1)", 0]
},
"egg_shell184": {
"position": [0, "math.sin(q.anim_time*90+184+1)", 0]
},
"egg_shell185": {
"position": [0, "math.sin(q.anim_time*90+185+1)", 0]
},
"egg_body": {
"position": [0, 0, 0]
},
"egg_body185": {
"position": [0, 0, 5]
}
}
}
}
}
@@ -0,0 +1,7 @@
{
"model": "q.appendage_model('cobblemon:egg');",
"target_locator": "head",
"scale": [1.0, 1.0, 1.0],
"offset": [0.0, 0, 0.0],
"rotation": [0.0, 45.0, 0.0]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
{
"poses": {
"standing": {
"poseName": "standing",
"transformTicks": 0,
"poseTypes": ["STAND", "WALK", "SLEEP", "HOVER", "FLY", "FLOAT", "SWIM", "PORTRAIT", "PROFILE"],
"animations": [
"q.bedrock('egg', 'idle')"
]
}
}
}
@@ -0,0 +1,15 @@
{
"name": "cobblemon:egg",
"order": 0,
"variations": [
{
"aspects": [
"shiny"
],
"poser": "cobblemon:egg",
"model": "cobblemon:egg.geo",
"texture": "cobblemon:textures/appendage/egg.png",
"layers": []
}
]
}
@@ -1528,6 +1528,173 @@
"cobblemon.command.pokemonrestart": "Cleared %1$s's party and PC.",
"cobblemon.command.freezepokemon.non_pokemon": "You are not looking at a Pokémon.",
"cobblemon.command.npcedit.non_npc": "You are not looking at a NPC.",
"cobblemon.command.cobblemon_config.reload": "Reloading Cobblemon settings.",
"cobblemon.config.ui.search_configurations": "Search Configurations",
"cobblemon.config.ui.reset": "Reset",
"cobblemon.config.ui.error.int_constraint": "The value must be between %1$s and %2$s.",
"cobblemon.config.ui.category.starter": "Starter",
"cobblemon.config.ui.category.pokedex": "Pokédex",
"cobblemon.config.ui.category.pokemon": "Pokémon",
"cobblemon.config.ui.category.spawning": "Spawning",
"cobblemon.config.ui.category.battles": "Battles",
"cobblemon.config.ui.category.passive_status": "Passive Status",
"cobblemon.config.ui.category.healing": "Healing",
"cobblemon.config.ui.category.storage": "Storage",
"cobblemon.config.ui.category.world": "World",
"cobblemon.config.ui.category.debug": "Debug",
"cobblemon.config.ui.max_pokemon_level": "Max Pokémon Level",
"cobblemon.config.ui.max_pokemon_level.tooltip": "The maximum level Pokémon can get to.",
"cobblemon.config.ui.max_pokemon_friendship": "Max Pokémon Friendship",
"cobblemon.config.ui.max_pokemon_friendship.tooltip": "The maximum friendship value.",
"cobblemon.config.ui.announce_drop_items": "Announce Drop Items",
"cobblemon.config.ui.announce_drop_items.tooltip": "Whether to notify the Player of dropped items from defeating wild Pokémon.",
"cobblemon.config.ui.default_drop_item_method": "Default Drop Item Method",
"cobblemon.config.ui.default_drop_item_method.tooltip": "Defines how items are dropped (entity, player, or inventory).",
"cobblemon.config.ui.default_drop_item_method.on_entity": "On Entity",
"cobblemon.config.ui.default_drop_item_method.on_entity.tooltip": "Drops the item on the entity that is dying, if it exists. If not, drops at the position parsed into drops.",
"cobblemon.config.ui.default_drop_item_method.on_player": "On Player",
"cobblemon.config.ui.default_drop_item_method.on_player.tooltip": "Drops the item on the player that caused the drop, if they exist. If not, drops at the position parsed into drops.",
"cobblemon.config.ui.default_drop_item_method.to_inventory": "To Inventory",
"cobblemon.config.ui.default_drop_item_method.to_inventory.tooltip": "Puts the item in the player's inventory or drops it on the ground if the inventory is full. If the player that caused the drop doesn't exist, it drops at the position parsed into drops.",
"cobblemon.config.ui.ambient_pokemon_cry_ticks": "Ambient Pokémon Cry Ticks",
"cobblemon.config.ui.ambient_pokemon_cry_ticks.tooltip": "Ticks between Pokémon cries. (Currently unused)",
"cobblemon.config.ui.default_box_count": "Default Box Count",
"cobblemon.config.ui.default_box_count.tooltip": "The number of boxes available in the PC.",
"cobblemon.config.ui.pokemon_save_interval_seconds": "Pokémon Save Interval Seconds",
"cobblemon.config.ui.pokemon_save_interval_seconds.tooltip": "Seconds between saving storage data to file. eg Party & PC data.",
"cobblemon.config.ui.storage_format": "Storage Format",
"cobblemon.config.ui.storage_format.tooltip": "The storage format used for storing Cobblemon data. (nbt/json/mongodb)",
"cobblemon.config.ui.prevent_complete_party_deposit": "Prevent Complete Party Deposit",
"cobblemon.config.ui.prevent_complete_party_deposit.tooltip": "Whether to prevent Players from depositing their last Party Pokémon into the PC.",
"cobblemon.config.ui.mongo_db_connection_string": "MongoDB Connection String",
"cobblemon.config.ui.mongodb_connection_string.tooltip": "The database connection string for your MongoDB database for Cobblemon data storage.",
"cobblemon.config.ui.mongo_db_database_name": "MongoDB Database Name",
"cobblemon.config.ui.mongodb_database_name.tooltip": "The name of the MongoDB for Cobblemon data storage.",
"cobblemon.config.ui.max_vertical_correction_blocks": "Max Vertical Correction Blocks",
"cobblemon.config.ui.max_vertical_correction_blocks.tooltip": "The maximum number of blocks that can be corrected when choosing a spawning area. The larger this value, the more reliably spawning will work in vertically bumpy terrain such as mountains. Raising this value only has a very small, negative impact on performance.",
"cobblemon.config.ui.minimum_level_range_max": "Minimum Level Range Max",
"cobblemon.config.ui.minimum_level_range_max.tooltip": "The value considered to be the highest party Pokémon level when a player's party is empty or extremely low level. The higher this value, the higher the levels of Pokémon can be around players with no or very weak Pokémon.",
"cobblemon.config.ui.enable_spawning": "Enable Spawning",
"cobblemon.config.ui.enable_spawning.tooltip": "Whether to allow Pokémon to spawn.",
"cobblemon.config.ui.minimum_distance_between_entities": "Minimum Distance Between Entities",
"cobblemon.config.ui.minimum_distance_between_entities.tooltip": "The minimum block distance between spawning Pokémon and existing Entities in the World. This prevents clustered Pokémon spawning.",
"cobblemon.config.ui.max_nearby_blocks_horizontal_range": "Max Nearby Blocks Horizontal Range",
"cobblemon.config.ui.max_nearby_blocks_horizontal_range.tooltip": "The maximum number of horizontal blocks away that a block can be for it to still be considered 'nearby' in a spawn condition. Lowering this value can slightly improve performance.",
"cobblemon.config.ui.max_nearby_blocks_vertical_range": "Max Nearby Blocks Vertical Range",
"cobblemon.config.ui.max_nearby_blocks_vertical_range.tooltip": "The maximum number of vertical blocks away that a block can be for it to still be considered 'nearby' in a spawn condition. Lowering this value can slightly improve performance.",
"cobblemon.config.ui.max_vertical_space": "Max Vertical Space",
"cobblemon.config.ui.max_vertical_space.tooltip": "How far to check above a spawn location to investigate head room. This value exists for performance reasons but should never be less than the largest 'height'/'depth' condition in a spawn file.",
"cobblemon.config.ui.world_slice_diameter": "World Slice Diameter",
"cobblemon.config.ui.world_slice_diameter.tooltip": "The diameter of the world slices that a spawn action can occur in. When this value is large, there is more reliably frequent spawning at the cost of performance.",
"cobblemon.config.ui.world_slice_height": "World Slice Height",
"cobblemon.config.ui.world_slice_height.tooltip": "The height of the world slices that a spawn action can occur in. When this value is large, there is more reliably frequent spawning at the cost of performance.",
"cobblemon.config.ui.ticks_between_spawn_attempts": "Ticks Between Spawn Attempts",
"cobblemon.config.ui.ticks_between_spawn_attempts.tooltip": "The amount of ticks (~20/second) between spawn attempts.",
"cobblemon.config.ui.minimum_slice_distance_from_player": "Minimum Slice Distance From Player",
"cobblemon.config.ui.minimum_slice_distance_from_player.tooltip": "Minimum amount of blocks from the Player that Pokémon can spawn.",
"cobblemon.config.ui.maximum_slice_distance_from_player": "Maximum Slice Distance From Player",
"cobblemon.config.ui.maximum_slice_distance_from_player.tooltip": "Maximum amount of blocks from the Player that Pokémon can spawn.",
"cobblemon.config.ui.export_spawn_config": "Export Spawn Config",
"cobblemon.config.ui.export_spawn_config.tooltip": "Whether to generate the spawn config for more specific spawning settings.",
"cobblemon.config.ui.save_pokemon_to_world": "Save Pokémon to World",
"cobblemon.config.ui.save_pokemon_to_world.tooltip": "Whether to save Pokémon to the World so they exist after a Chunk/World is reloaded.",
"cobblemon.config.ui.export_starter_config": "Export Starter Config",
"cobblemon.config.ui.export_starter_config.tooltip": "Whether to generate starter.json for editing the starter list.",
"cobblemon.config.ui.auto_update_showdown": "Auto Update Showdown",
"cobblemon.config.ui.auto_update_showdown.tooltip": "Whether to automatically update Showdown to the version shipped in the current Cobblemon version.",
"cobblemon.config.ui.default_flee_distance": "Default Flee Distance",
"cobblemon.config.ui.default_flee_distance.tooltip": "How far away a Player must get to flee a battle (non-PvP). If the value is -1, battles cannot be fled.",
"cobblemon.config.ui.allow_experience_from_pvp": "Allow Experience from PvP",
"cobblemon.config.ui.allow_experience_from_pvp.tooltip": "Whether to allow Exp. gain from PvP battles.",
"cobblemon.config.ui.experience_share_multiplier": "Experience Share Multiplier",
"cobblemon.config.ui.experience_share_multiplier.tooltip": "Multiplier of how much of the total Exp is given to a Pokémon holding an Exp. Share.",
"cobblemon.config.ui.lucky_egg_multiplier": "Lucky Egg Multiplier",
"cobblemon.config.ui.lucky_egg_multiplier.tooltip": "Multiplier of how much of the total Exp is given to a Pokémon holding a Lucky Egg.",
"cobblemon.config.ui.allow_spectating": "Allow Spectating",
"cobblemon.config.ui.allow_spectating.tooltip": "Whether to allow battles to be spectated.",
"cobblemon.config.ui.experience_multiplier": "Experience Multiplier",
"cobblemon.config.ui.experience_multiplier.tooltip": "Additional Exp. multiplier for Exp. calculation.",
"cobblemon.config.ui.pokemon_per_chunk": "Pokémon per Chunk",
"cobblemon.config.ui.pokemon_per_chunk.tooltip": "The amount of Pokémon allowed within a Chunk when spawning.",
"cobblemon.config.ui.passive_statuses": "Passive Statuses",
"cobblemon.config.ui.passive_statuses.tooltip": "Range of seconds before clearing the status effect. The value is selected randomly from the range each time an effect is applied.",
"cobblemon.config.ui.infinite_healer_charge": "Infinite Healer Charge",
"cobblemon.config.ui.infinite_healer_charge.tooltip": "Whether Healing Machines have infinite charges to heal Pokémon.",
"cobblemon.config.ui.max_healer_charge": "Max Healer Charge",
"cobblemon.config.ui.max_healer_charge.tooltip": "The maximum amount of full HP restoration Healing Machines can have. 1 charge is equivalent to a Max Revive, plus PP restoration.",
"cobblemon.config.ui.seconds_to_charge_healing_machine": "Seconds to Fully Charge a Healing Machine",
"cobblemon.config.ui.seconds_to_charge_healing_machine.tooltip": "The amount of seconds that takes for a Healing Machine to fully charge.",
"cobblemon.config.ui.default_faint_timer": "Default Faint Timer",
"cobblemon.config.ui.default_faint_timer.tooltip": "How many seconds before reviving a fainted Pokémon.",
"cobblemon.config.ui.faint_awaken_health_percent": "Faint Awaken Health Percent",
"cobblemon.config.ui.faint_awaken_health_percent.tooltip": "How much HP to restore when defaultFaintTimer is up.",
"cobblemon.config.ui.heal_percent": "Heal Percent",
"cobblemon.config.ui.heal_percent.tooltip": "How much HP to restore when healTimer is up.",
"cobblemon.config.ui.heal_timer": "Heal Timer",
"cobblemon.config.ui.heal_timer.tooltip": "How many seconds between HP naturally restoring.",
"cobblemon.config.ui.base_apricorn_tree_generation_chance": "Base Apricorn Tree Generation Chance",
"cobblemon.config.ui.base_apricorn_tree_generation_chance.tooltip": "Multiplier on biome-based chance for Apricorn trees to spawn during world generation.",
"cobblemon.config.ui.display_entity_level_label": "Display Entity Level Label",
"cobblemon.config.ui.display_entity_level_label.tooltip": "Whether to display the level above a Pokémon.",
"cobblemon.config.ui.display_entity_name_label": "Display Entity Name Label",
"cobblemon.config.ui.display_entity_name_label.tooltip": "Whether to display the name above a Pokémon.",
"cobblemon.config.ui.display_name_for_unknown_pokemon": "Display Name for Unknown Pokémon",
"cobblemon.config.ui.display_name_for_unknown_pokemon.tooltip": "Whether to display the name above a Pokémon if it has not been scanned with the Pokédex yet.",
"cobblemon.config.ui.display_entity_labels_when_crouching_only": "Display Entity Labels When Crouching Only",
"cobblemon.config.ui.display_entity_labels_when_crouching_only.tooltip": "Whether to display the name above a Pokémon only when crouching or at all times.",
"cobblemon.config.ui.shiny_rate": "Shiny Rate",
"cobblemon.config.ui.shiny_rate.tooltip": "The chance for a spawned Pokémon to be shiny. If 0 no spawned Pokémon will be shiny. Calculated as 1/x.",
"cobblemon.config.ui.shiny_notice_particles_distance": "Shiny Notice Particles Distance",
"cobblemon.config.ui.shiny_notice_particles_distance.tooltip": "The distance at which shiny particles for Pokémon will start appearing.",
"cobblemon.config.ui.capture_calculator": "Capture Calculator",
"cobblemon.config.ui.capture_calculator.tooltip": "The ID of the capture calculator. Can be added by another mod. If the ID doesn't exist it uses the default capture calculator.",
"cobblemon.config.ui.player_damage_pokemon": "Player Damage Pokémon",
"cobblemon.config.ui.player_damage_pokemon.tooltip": "Whether Players can attack Pokémon like normal Minecraft entities.",
"cobblemon.config.ui.apple_leftovers_chance": "Apple Leftovers Chance",
"cobblemon.config.ui.apple_leftovers_chance.tooltip": "The chance to drop Leftovers when a Player eats an item that has #cobblemon:held/leaves_leftovers.",
"cobblemon.config.ui.max_roots_in_area": "Max Roots in Area",
"cobblemon.config.ui.max_roots_in_area.tooltip": "The maximum number of #cobblemon:roots that can generate within a 8x2x8 sphere.",
"cobblemon.config.ui.big_root_propagation_chance": "Big Root Propagation Chance",
"cobblemon.config.ui.big_root_propagation_chance.tooltip": "The chance that Big Roots will grow more roots where possible.",
"cobblemon.config.ui.energy_root_chance": "Energy Root Chance",
"cobblemon.config.ui.energy_root_chance.tooltip": "The chance that a Big Root will grow an Energy Root.",
"cobblemon.config.ui.max_dynamax_level": "Max Dynamax Level",
"cobblemon.config.ui.max_dynamax_level.tooltip": "The maximum Dynamax level Pokémon can be upgraded to.",
"cobblemon.config.ui.tera_type_rate": "Tera Type Rate",
"cobblemon.config.ui.tera_type_rate.tooltip": "The chance for a spawned Pokémon to have a Tera type that doesn't match it's typing. Calculated as 1/x.",
"cobblemon.config.ui.default_pastured_pokemon_limit": "Default Pastured Pokémon Limit",
"cobblemon.config.ui.default_pastured_pokemon_limit.tooltip": "The maximum Pokémon that can be placed in a Pasture block.",
"cobblemon.config.ui.pasture_block_update_ticks": "Pasture Block Update Ticks",
"cobblemon.config.ui.pasture_block_update_ticks.tooltip": "The number of server ticks between Pasture block updates.",
"cobblemon.config.ui.pasture_max_wander_distance": "Pasture Max Wander Distance",
"cobblemon.config.ui.pasture_max_wander_distance.tooltip": "The maximum amount of blocks a Pokémon can move from the Pasture block.",
"cobblemon.config.ui.pasture_max_per_chunk": "Pasture Max per Chunk",
"cobblemon.config.ui.pasture_max_per_chunk.tooltip": "The maximum number of pastured Pokémon that can be sent out in a chunk.",
"cobblemon.config.ui.max_inserted_fossil_items": "Max Inserted Fossil Items",
"cobblemon.config.ui.max_inserted_fossil_items.tooltip": "The maximum amount of fossils that can be added to the FossilAnalyzer.",
"cobblemon.config.ui.walking_in_battle_animations": "Walking in Battle Animations",
"cobblemon.config.ui.walking_in_battle_animations.tooltip": "Whether to allow walking animations to play during battle animations.",
"cobblemon.config.ui.battle_wild_max_distance": "Battle Wild Max Distance",
"cobblemon.config.ui.battle_wild_max_distance.tooltip": "The maximum distance at which wild battles can occur.",
"cobblemon.config.ui.trade_max_distance": "Trade Max Distance",
"cobblemon.config.ui.trade_max_distance.tooltip": "The maximum distance at which trades are allowed.",
"cobblemon.config.ui.battle_pvp_max_distance": "Battle PvP Max Distance",
"cobblemon.config.ui.battle_pvp_max_distance.tooltip": "The maximum distance at which PvP battles can occur.",
"cobblemon.config.ui.battle_spectate_max_distance": "Battle Spectate Max Distance",
"cobblemon.config.ui.battle_spectate_max_distance.tooltip": "The maximum distance at which players can spectate battles.",
"cobblemon.config.ui.max_pokedex_scanning_detection_range": "Max Pokédex Scanning Detection Range",
"cobblemon.config.ui.max_pokedex_scanning_detection_range.tooltip": "The maximum distance at which the Pokédex can detect and scan Pokémon.",
"cobblemon.config.ui.hide_unimplemented_pokemon_in_the_pokedex": "Hide Unimplemented Pokémon in the Pokédex",
"cobblemon.config.ui.hide_unimplemented_pokemon_in_the_pokedex.tooltip": "Whether to hide unimplemented Pokémon from the Pokédex.",
"cobblemon.config.ui.enable_debug_keys": "Enable Debug Keys",
"cobblemon.config.ui.enable_debug_keys.tooltip": "Enables additional commands and chat logging to assist with implementing Pokémon (see the wiki for a list of commands).",
"cobblemon.config.ui.allow_starter_on_join": "Allow Starter on Join",
"cobblemon.config.ui.prompt_starter_once_only": "Prompt Starter Once Only",
"cobblemon.config.ui.starters": "Starters",
"advancements.cobblemon.root": "The World of Cobblemon!",
"advancements.cobblemon.root.description": "Choose a starter Pokémon and set out on a new adventure",
Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

@@ -8,11 +8,13 @@
package com.cobblemon.mod.common.pokemon
import com.cobblemon.mod.common.api.pokemon.stats.SidemodEvSource
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.junit.BootstrapMinecraft
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
@BootstrapMinecraft
@@ -34,11 +36,12 @@ internal class EVsKtTest {
}
@Test
@Disabled("Requires GameTest due to need to access Pokemon")
fun `attempt to add an illegal amount of evs`() {
val evs = EVs.createEmpty()
evs[Stats.HP] = EVs.MAX_STAT_VALUE
evs[Stats.ATTACK] = EVs.MAX_STAT_VALUE
val added = evs.add(Stats.DEFENCE, 7)
val added = evs.add(Stats.DEFENCE, 7, SidemodEvSource("test", Pokemon()))
assert(added == 6)
}
@@ -49,9 +52,11 @@ internal class EVsKtTest {
}
@Test
@Disabled("Requires GameTest due to need to access Pokemon")
fun `decrement EVs`() {
val evs = EVs.createEmpty()
evs[Stats.HP] = EVs.MAX_STAT_VALUE
assert(evs.add(Stats.HP, -(EVs.MAX_STAT_VALUE + 1)) == -EVs.MAX_STAT_VALUE)
assert(evs.add(Stats.HP, -(EVs.MAX_STAT_VALUE + 1), SidemodEvSource("test", Pokemon())) == -EVs.MAX_STAT_VALUE)
}
}
@@ -7,7 +7,8 @@ This file contains tags related to different biomes in cobblemon, as well as som
<details>
<summary><b>Tag:</b> #biome:is_arid</summary>
- #cobblemon:is_sandy
- #cobblemon:is_badlands
- #cobblemon:is_desert
- #cobblemon:is_savanna
</details>
@@ -1039,15 +1040,164 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #evolution:alolan</summary>
<summary><b>Tag:</b> #regional:bergmite_hisuibiome</summary>
- #cobblemon:is_tropical_island
- #minecraft:is_beach
- #cobblemon:is_freezing
- #cobblemon:is_hills
</details>
<details>
<summary><b>Tag:</b> #evolution:cyndaquil_quilava_hisui_bias</summary>
<summary><b>Tag:</b> #regional:bergmite_kalosbiome</summary>
- #cobblemon:is_frozen_ocean
</details>
<details>
<summary><b>Tag:</b> #regional:cubone_alolabiome</summary>
- #cobblemon:is_beach
- #cobblemon:is_volcanic
</details>
<details>
<summary><b>Tag:</b> #regional:cubone_kantobiome</summary>
- #cobblemon:is_arid
</details>
<details>
<summary><b>Tag:</b> #regional:dartrix_alolabiome</summary>
- #cobblemon:is_beach
- #cobblemon:is_ocean
- #cobblemon:is_tropical_island
</details>
<details>
<summary><b>Tag:</b> #regional:dartrix_hisuibiome</summary>
- #cobblemon:is_freezing
- #cobblemon:is_hills
</details>
<details>
<summary><b>Tag:</b> #regional:dewott_hisuibiome</summary>
- #cobblemon:is_coast
- #cobblemon:is_cold_ocean
- minecraft:snowy_beach
</details>
<details>
<summary><b>Tag:</b> #regional:dewott_unovabiome</summary>
- #cobblemon:is_beach
- #cobblemon:is_ocean
- #cobblemon:is_tropical_island
</details>
<details>
<summary><b>Tag:</b> #regional:exeggcute_alolabiome</summary>
- #cobblemon:is_beach
- #cobblemon:is_tropical_island
</details>
<details>
<summary><b>Tag:</b> #regional:exeggcute_kantobiome</summary>
- #cobblemon:is_jungle
- #cobblemon:is_savanna
</details>
<details>
<summary><b>Tag:</b> #regional:goomy_hisuibiome</summary>
- #cobblemon:is_dripstone
- #cobblemon:is_lush
</details>
<details>
<summary><b>Tag:</b> #regional:goomy_kalosbiome</summary>
- #cobblemon:is_overworld
- #cobblemon:is_swamp
</details>
<details>
<summary><b>Tag:</b> #regional:koffing_galarbiome</summary>
- #cobblemon:is_mushroom
</details>
<details>
<summary><b>Tag:</b> #regional:koffing_kantobiome</summary>
- #cobblemon:nether/is_toxic
</details>
<details>
<summary><b>Tag:</b> #regional:mimejr_galarbiome</summary>
- #cobblemon:is_freezing
</details>
<details>
<summary><b>Tag:</b> #regional:mimejr_sinnohbiome</summary>
- #cobblemon:is_temperate
</details>
<details>
<summary><b>Tag:</b> #regional:petilil_hisuibiome</summary>
- #cobblemon:is_freezing
- #cobblemon:is_hills
</details>
<details>
<summary><b>Tag:</b> #regional:petilil_unovabiome</summary>
- #cobblemon:is_floral
- #cobblemon:is_forest
- #cobblemon:is_tropical_island
</details>
<details>
<summary><b>Tag:</b> #regional:pikachu_alolabiome</summary>
- #cobblemon:is_beach
- #cobblemon:is_tropical_island
</details>
<details>
<summary><b>Tag:</b> #regional:pikachu_kantobiome</summary>
- #cobblemon:is_forest
</details>
<details>
<summary><b>Tag:</b> #regional:quilava_hisuibiome</summary>
- #cobblemon:is_taiga
- #cobblemon:nether/is_crimson
@@ -1057,60 +1207,62 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #evolution:lilligant_unovan</summary>
<summary><b>Tag:</b> #regional:quilava_johtobiome</summary>
- #cobblemon:is_temperate
</details>
<details>
<summary><b>Tag:</b> #regional:rufflet_hisuibiome</summary>
- #cobblemon:is_freezing
- #cobblemon:is_hills
</details>
<details>
<summary><b>Tag:</b> #regional:rufflet_unovabiome</summary>
- #cobblemon:is_temperate
</details>
<details>
<summary><b>Tag:</b> #vivillon:archipelago</summary>
- #cobblemon:is_mushroom
</details>
<details>
<summary><b>Tag:</b> #vivillon:archipelago_anti</summary>
- #cobblemon:is_magical
- #cobblemon:is_spooky
- #cobblemon:is_swamp
</details>
<details>
<summary><b>Tag:</b> #vivillon:continental</summary>
- #cobblemon:is_plains
</details>
<details>
<summary><b>Tag:</b> #vivillon:continental_anti</summary>
- #cobblemon:is_floral
- #cobblemon:is_forest
- #cobblemon:is_tropical_island
- #cobblemon:is_mushroom
- #cobblemon:is_savanna
- minecraft:sunflower_plains
</details>
<details>
<summary><b>Tag:</b> #evolution:oshawott_dewott_hisui_bias</summary>
- #c:stony_shores
- minecraft:snowy_beach
- minecraft:stony_shore
- terralith:basalt_cliffs
- terralith:granite_cliffs
- terralith:white_cliffs
- wythers:calcite_coast
- wythers:cold_island
- wythers:cold_stony_shore
- wythers:deepslate_shore
- wythers:frigid_island
- wythers:frozen_island
- wythers:gravelly_beach
- wythers:icy_shore
</details>
<details>
<summary><b>Tag:</b> #evolution:rowlet_dartrix_alolan</summary>
- #cobblemon:is_forest
- #cobblemon:is_taiga
</details>
<details>
<summary><b>Tag:</b> #evolution:rowlet_dartrix_hisui_bias</summary>
- #cobblemon:is_snowy_forest
- #cobblemon:is_snowy_taiga
</details>
<details>
<summary><b>Tag:</b> #evolution:sliggoo_hisuian</summary>
- #cobblemon:is_dripstone
- #cobblemon:is_lush
</details>
<details>
<summary><b>Tag:</b> #evolution:vivillon_elegant</summary>
<summary><b>Tag:</b> #vivillon:elegant</summary>
- #cobblemon:is_magical
- #cobblemon:is_spooky
@@ -1118,7 +1270,123 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #evolution:vivillon_monsoon</summary>
<summary><b>Tag:</b> #vivillon:elegant_anti</summary>
- #cobblemon:is_mountain
</details>
<details>
<summary><b>Tag:</b> #vivillon:fancy</summary>
- #cobblemon:is_cherry_blossom
</details>
<details>
<summary><b>Tag:</b> #vivillon:forsaken</summary>
- #cobblemon:is_deep_dark
</details>
<details>
<summary><b>Tag:</b> #vivillon:garden</summary>
- #cobblemon:is_forest
</details>
<details>
<summary><b>Tag:</b> #vivillon:garden_anti</summary>
- #cobblemon:is_cherry_blossom
- #cobblemon:is_floral
- #cobblemon:is_jungle
- #cobblemon:is_magical
- #cobblemon:is_mountain
- #cobblemon:is_mushroom
- #cobblemon:is_plains
- #cobblemon:is_spooky
- #cobblemon:is_swamp
</details>
<details>
<summary><b>Tag:</b> #vivillon:high_plains</summary>
- #cobblemon:is_badlands
</details>
<details>
<summary><b>Tag:</b> #vivillon:icy_snow</summary>
- #cobblemon:is_taiga
</details>
<details>
<summary><b>Tag:</b> #vivillon:icy_snow_anti</summary>
- #cobblemon:is_mountain
</details>
<details>
<summary><b>Tag:</b> #vivillon:inferno</summary>
- #minecraft:is_nether
</details>
<details>
<summary><b>Tag:</b> #vivillon:jungle</summary>
- #cobblemon:is_jungle
</details>
<details>
<summary><b>Tag:</b> #vivillon:jungle_anti</summary>
- #cobblemon:is_mountain
</details>
<details>
<summary><b>Tag:</b> #vivillon:marine</summary>
- #cobblemon:is_ocean
</details>
<details>
<summary><b>Tag:</b> #vivillon:marine_anti</summary>
- #cobblemon:is_frozen_ocean
- #cobblemon:is_warm_ocean
</details>
<details>
<summary><b>Tag:</b> #vivillon:meadow</summary>
- #cobblemon:is_floral
</details>
<details>
<summary><b>Tag:</b> #vivillon:meadow_anti</summary>
- #cobblemon:is_cherry_blossom
- #cobblemon:is_magical
- minecraft:sunflower_plains
</details>
<details>
<summary><b>Tag:</b> #vivillon:monsoon</summary>
- #cobblemon:is_mountain
- #cobblemon:is_peak
@@ -1126,7 +1394,18 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #evolution:vivillon_ocean</summary>
<summary><b>Tag:</b> #vivillon:monsoon_anti</summary>
- #cobblemon:is_badlands
- #cobblemon:is_desert
- #cobblemon:is_floral
- #cobblemon:is_mushroom
- #cobblemon:is_tropical_island
</details>
<details>
<summary><b>Tag:</b> #vivillon:ocean</summary>
- #cobblemon:is_coast
- #cobblemon:is_warm_ocean
@@ -1134,7 +1413,24 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #evolution:vivillon_river</summary>
<summary><b>Tag:</b> #vivillon:ocean_anti</summary>
- #cobblemon:is_cold_ocean
- #cobblemon:is_frozen_ocean
- #cobblemon:is_lukewarm_ocean
- #cobblemon:is_temperate_ocean
</details>
<details>
<summary><b>Tag:</b> #vivillon:polar</summary>
- #cobblemon:is_frozen_ocean
</details>
<details>
<summary><b>Tag:</b> #vivillon:river</summary>
- #cobblemon:is_river
- #cobblemon:is_swamp
@@ -1142,7 +1438,48 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #evolution:vivillon_sun</summary>
<summary><b>Tag:</b> #vivillon:river_anti</summary>
- #cobblemon:is_forest
- #cobblemon:is_jungle
- #cobblemon:is_magical
- #cobblemon:is_spooky
</details>
<details>
<summary><b>Tag:</b> #vivillon:sandstorm</summary>
- #cobblemon:is_desert
</details>
<details>
<summary><b>Tag:</b> #vivillon:sandstorm_anti</summary>
- #cobblemon:is_badlands
</details>
<details>
<summary><b>Tag:</b> #vivillon:savanna</summary>
- #cobblemon:is_savanna
</details>
<details>
<summary><b>Tag:</b> #vivillon:savanna_anti</summary>
- #cobblemon:is_badlands
- #cobblemon:is_desert
- #cobblemon:is_floral
- #cobblemon:is_mountain
</details>
<details>
<summary><b>Tag:</b> #vivillon:sun</summary>
- #cobblemon:is_sky
- #cobblemon:is_volcanic
@@ -1151,7 +1488,7 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #evolution:vivillon_tundra</summary>
<summary><b>Tag:</b> #vivillon:tundra</summary>
- #cobblemon:is_glacial
- #cobblemon:is_snowy_forest
@@ -1159,6 +1496,20 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #vivillon:tundra_anti</summary>
- #cobblemon:is_mountain
</details>
<details>
<summary><b>Tag:</b> #vivillon:void</summary>
- #minecraft:is_end
</details>
<details>
<summary><b>Tag:</b> #has_density:dense</summary>
@@ -1439,6 +1790,7 @@ This file contains tags related to different biomes in cobblemon, as well as som
<details>
<summary><b>Tag:</b> #has_season:spring</summary>
- #aether:is_aether
- #cobblemon:is_floral
- #cobblemon:is_lukewarm_ocean
- #cobblemon:is_magical
@@ -1666,60 +2018,109 @@ This file contains tags related to different biomes in cobblemon, as well as som
</details>
<details>
<summary><b>Tag:</b> #regional_bias_evolutions:alolan</summary>
<summary><b>Tag:</b> #space:is_fiery</summary>
- #cobblemon:is_tropical_island
- #minecraft:is_beach
- #stellaris:mercury_biomes
- space:mercury_hotspot
- space:mercury_plains
- space:venus_highlands
- space:venus_lava_caves
- space:venus_lowlands
- space:venus_midlands
- space:venus_volcanic_plains
- stellaris:infernal_venus_barrens
</details>
<details>
<summary><b>Tag:</b> #regional_bias_evolutions:cyndaquil_quilava_hisui_bias</summary>
<summary><b>Tag:</b> #space:is_icy</summary>
- #cobblemon:is_taiga
- #cobblemon:nether/is_crimson
- #cobblemon:nether/is_forest
- #cobblemon:nether/is_overgrowth
- #space:ice_craters
- stellaris:mars_ice_spikes
</details>
<details>
<summary><b>Tag:</b> #regional_bias_evolutions:oshawott_dewott_hisui_bias</summary>
<summary><b>Tag:</b> #space:is_mars</summary>
- #c:stony_shores
- minecraft:snowy_beach
- minecraft:stony_shore
- terralith:basalt_cliffs
- terralith:granite_cliffs
- terralith:white_cliffs
- wythers:calcite_coast
- wythers:coastal_mangroves
- wythers:cold_island
- wythers:cold_stony_shore
- wythers:deepslate_shore
- wythers:frigid_island
- wythers:frozen_island
- wythers:gravelly_beach
- wythers:icy_shore
- wythers:mediterranean_island
- wythers:temperate_island
- wythers:tropical_island
- wythers:warm_stony_shore
- #stellaris:mars_biomes
- #stellaris:mars_biomes
- #stellaris:mars_biomes
- space:mars_dripstone
- space:mars_highlands
- space:mars_ice
- space:mars_lowlands
- space:mars_lush_caves
- space:mars_midlands
</details>
<details>
<summary><b>Tag:</b> #regional_bias_evolutions:rowlet_dartrix_alolan</summary>
<summary><b>Tag:</b> #space:is_mercury</summary>
- #cobblemon:is_forest
- #cobblemon:is_taiga
- #stellaris:mercury_biomes
- space:mercury_hotspot
- space:mercury_ice
- space:mercury_plains
</details>
<details>
<summary><b>Tag:</b> #regional_bias_evolutions:rowlet_dartrix_hisui_bias</summary>
<summary><b>Tag:</b> #space:is_moon</summary>
- #cobblemon:is_snowy_forest
- #cobblemon:is_snowy_taiga
- #stellaris:moon_biomes
- space:moon_highlands
- space:moon_ice
- space:moon_lowlands
- space:moon_midlands
- space:moon_rocks
- swe:moon
</details>
<details>
<summary><b>Tag:</b> #space:is_space</summary>
- #stellaris:mars_biomes
- #stellaris:mercury_biomes
- #stellaris:moon_biomes
- #stellaris:venus_biomes
- space:mars_dripstone
- space:mars_highlands
- space:mars_ice
- space:mars_lowlands
- space:mars_lush_caves
- space:mars_midlands
- space:mercury_hotspot
- space:mercury_ice
- space:mercury_plains
- space:moon_highlands
- space:moon_ice
- space:moon_lowlands
- space:moon_midlands
- space:moon_rocks
- space:space
- space:venus_highlands
- space:venus_lava_caves
- space:venus_lowlands
- space:venus_midlands
- space:venus_plankton_bloom
- space:venus_sky
- space:venus_volcanic_plains
- swe:moon
</details>
<details>
<summary><b>Tag:</b> #space:is_venus</summary>
- #stellaris:venus_biomes
- space:venus_highlands
- space:venus_lava_caves
- space:venus_lowlands
- space:venus_midlands
- space:venus_plankton_bloom
- space:venus_sky
- space:venus_volcanic_plains
</details>
@@ -186,6 +186,7 @@ This file contains tags related to block in cobblemon.
- #c:farmland
- #c:farmlands
- aether:aether_farmland
- minecraft:farmland
</details>
@@ -465,6 +466,19 @@ This file contains tags related to block in cobblemon.
</details>
<details>
<summary><b>Tag:</b> #block:gilded_chests</summary>
- cobblemon:black_gilded_chest
- cobblemon:blue_gilded_chest
- cobblemon:gilded_chest
- cobblemon:green_gilded_chest
- cobblemon:pink_gilded_chest
- cobblemon:white_gilded_chest
- cobblemon:yellow_gilded_chest
</details>
<details>
<summary><b>Tag:</b> #block:glazed_terracotta_blocks</summary>
@@ -1172,6 +1186,7 @@ This file contains tags related to block in cobblemon.
- botania:white_floating_flower
- botania:white_mystical_flower
- botania:white_shiny_flower
- cobblemon:pep_up_flower
- deep_aether:enchanted_blossom
- farmersdelight:wild_carrots
- minecraft:azure_bluet
@@ -1863,10 +1878,7 @@ This file contains tags related to block in cobblemon.
<details>
<summary><b>Tag:</b> #block:logs</summary>
- cobblemon:apricorn_log
- cobblemon:apricorn_wood
- cobblemon:stripped_apricorn_log
- cobblemon:stripped_apricorn_wood
- #cobblemon:apricorn_logs
</details>
@@ -229,14 +229,6 @@ This file contains tags related to items in cobblemon.
</details>
<details>
<summary><b>Tag:</b> #item:cobblemon_seeds</summary>
- #cobblemon:mint_seeds
- cobblemon:vivichoke_seeds
</details>
<details>
<summary><b>Tag:</b> #item:cooked_meat</summary>
@@ -329,6 +321,7 @@ This file contains tags related to items in cobblemon.
- cobblemon:reaper_cloth
- cobblemon:ribbon_sweet
- cobblemon:sachet
- cobblemon:shell_helmet
- cobblemon:star_sweet
- cobblemon:strawberry_sweet
- cobblemon:sweet_apple
@@ -433,6 +426,19 @@ This file contains tags related to items in cobblemon.
</details>
<details>
<summary><b>Tag:</b> #item:gilded_chests</summary>
- cobblemon:black_gilded_chest
- cobblemon:blue_gilded_chest
- cobblemon:gilded_chest
- cobblemon:green_gilded_chest
- cobblemon:pink_gilded_chest
- cobblemon:white_gilded_chest
- cobblemon:yellow_gilded_chest
</details>
<details>
<summary><b>Tag:</b> #item:hanging_signs</summary>
@@ -590,9 +596,9 @@ This file contains tags related to items in cobblemon.
- #cobblemon:apricorn_sprouts
- #cobblemon:apricorns
- #cobblemon:berries
- #cobblemon:cobblemon_seeds
- #cobblemon:herbs
- #cobblemon:mint_leaf
- #cobblemon:seeds
- cobblemon:big_root
- cobblemon:energy_root
- cobblemon:medicinal_leek
@@ -820,12 +826,9 @@ This file contains tags related to items in cobblemon.
<details>
<summary><b>Tag:</b> #item:seeds</summary>
- #cobblemon:cobblemon_seeds
- minecraft:beetroot_seeds
- minecraft:melon_seeds
- minecraft:pumpkin_seeds
- minecraft:torchflower_seeds
- minecraft:wheat_seeds
- #cobblemon:held/terrain_seeds
- #cobblemon:mint_seeds
- cobblemon:vivichoke_seeds
</details>
@@ -882,6 +885,7 @@ This file contains tags related to items in cobblemon.
- cobblemon:deepslate_sun_stone_ore
- cobblemon:sun_stone_ore
- cobblemon:terracotta_sun_stone_ore
</details>
@@ -949,7 +953,7 @@ This file contains tags related to items in cobblemon.
<details>
<summary><b>Tag:</b> #item:tier_1_poke_ball_materials</summary>
- minecraft:copper_ingot
- #c:ingots/copper
</details>
@@ -977,7 +981,7 @@ This file contains tags related to items in cobblemon.
<details>
<summary><b>Tag:</b> #item:tier_2_poke_ball_materials</summary>
- minecraft:iron_ingot
- #c:ingots/iron
</details>
@@ -1000,7 +1004,7 @@ This file contains tags related to items in cobblemon.
<details>
<summary><b>Tag:</b> #item:tier_3_poke_ball_materials</summary>
- minecraft:gold_ingot
- #c:ingots/gold
</details>
@@ -1015,7 +1019,7 @@ This file contains tags related to items in cobblemon.
<details>
<summary><b>Tag:</b> #item:tier_4_poke_ball_materials</summary>
- minecraft:diamond
- #c:gems/diamond
</details>
@@ -1345,6 +1349,13 @@ This file contains tags related to items in cobblemon.
</details>
<details>
<summary><b>Tag:</b> #item:bookshelf_books</summary>
- #cobblemon:pokedex
</details>
<details>
<summary><b>Tag:</b> #item:chest_boats</summary>
@@ -1397,26 +1408,28 @@ This file contains tags related to items in cobblemon.
<details>
<summary><b>Tag:</b> #item:logs</summary>
- cobblemon:apricorn_log
- cobblemon:apricorn_wood
- cobblemon:stripped_apricorn_log
- cobblemon:stripped_apricorn_wood
- #cobblemon:apricorn_logs
</details>
<details>
<summary><b>Tag:</b> #item:logs_that_burn</summary>
- cobblemon:apricorn_log
- cobblemon:apricorn_wood
- cobblemon:stripped_apricorn_log
- cobblemon:stripped_apricorn_wood
- #cobblemon:apricorn_logs
</details>
<details>
<summary><b>Tag:</b> #item:parrot_food</summary>
- #cobblemon:seeds
</details>
<details>
<summary><b>Tag:</b> #item:piglin_loved</summary>
- #cobblemon:gilded_chests
- cobblemon:relic_coin
- cobblemon:relic_coin_pouch
- cobblemon:relic_coin_sack
@@ -37,15 +37,15 @@ This file contains all current spawn presets of cobblemon.
| **structures** | `minecraft:end_city` | |
## foliage
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **neededNearbyBlocks** | `#minecraft:leaves`; `#c:leaves` | |
| Key | Conditions | Anticonditions |
|------------------------|----------------------------------|----------------|
| **neededNearbyBlocks** | `#minecraft:leaves`; `#c:leaves` | |
## illager_structures
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **minY** | `62` | |
| **structures** | `minecraft:pillager_outpost`; `minecraft:swamp_hut`; `minecraft:mansion` | |
| Key | Conditions | Anticonditions |
|----------------|--------------------------------------------------------------------------|----------------|
| **minY** | `62` | |
| **structures** | `minecraft:pillager_outpost`; `minecraft:swamp_hut`; `minecraft:mansion` | |
## jungle_pyramid
| Key | Conditions | Anticonditions |
@@ -54,12 +54,11 @@ This file contains all current spawn presets of cobblemon.
| **neededBaseBlocks** | `minecraft:cobblestone`; `#minecraft:mossy_cobblestone` | |
| **neededNearbyBlocks** | `#cobblemon:jungle_pyramid_blocks` | |
| **structures** | `minecraft:jungle_pyramid` | |
## lava
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **fluid** | `#minecraft:lava` | |
| Key | Conditions | Anticonditions |
|-----------|-------------------|----------------|
| **fluid** | `#minecraft:lava` | |
## mansion
| Key | Conditions | Anticonditions |
@@ -75,10 +74,10 @@ This file contains all current spawn presets of cobblemon.
| **neededBaseBlocks** | `#cobblemon:natural` | `minecraft:farmland` |
## nether_fossil
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **neededNearbyBlocks** | `minecraft:bone_block` | |
| **structures** | `minecraft:nether_fossil` | |
| Key | Conditions | Anticonditions |
|------------------------|---------------------------|----------------|
| **neededNearbyBlocks** | `minecraft:bone_block` | |
| **structures** | `minecraft:nether_fossil` | |
## nether_structures
| Key | Conditions | Anticonditions |
@@ -120,17 +119,17 @@ This file contains all current spawn presets of cobblemon.
| **structures** | `#minecraft:ruined_portal` | |
## salt
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **biomes** | `biomesoplenty:hot_springs`; `biomesoplenty:wasteland`; `biomesoplenty:wasteland_steppe`; `terralith:amethyst_canyon`; `terralith:amethyst_rainforest`; `terralith:skylands_winter`; `terralith:yellowstone`; `wythers:calcite_caverns`; `wythers:calcite_coast`; `wythers:mediterranean_island_thermal_spring`; `wythers:salt_lakes_pink`; `wythers:salt_lakes_turquoise`; `wythers:salt_lakes_white`; `wythers:thermal_taiga` | |
| Key | Conditions | Anticonditions |
|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|
| **biomes** | `biomesoplenty:hot_springs`; `biomesoplenty:wasteland`; `biomesoplenty:wasteland_steppe`; `terralith:amethyst_canyon`; `terralith:amethyst_rainforest`; `terralith:skylands_winter`; `terralith:yellowstone`; `wythers:calcite_caverns`; `wythers:calcite_coast`; `wythers:mediterranean_island_thermal_spring`; `wythers:salt_lakes_pink`; `wythers:salt_lakes_turquoise`; `wythers:salt_lakes_white`; `wythers:thermal_taiga` | |
## stronghold
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **maxY** | `62` | |
| **neededBaseBlocks** | `#minecraft:stone_bricks` | |
| **neededNearbyBlocks** | `minecraft:cracked_stone_bricks`; `minecraft:mossy_stone_bricks` | |
| **structures** | `minecraft:stronghold` | |
| Key | Conditions | Anticonditions |
|------------------------|------------------------------------------------------------------|----------------|
| **maxY** | `62` | |
| **neededBaseBlocks** | `#minecraft:stone_bricks` | |
| **neededNearbyBlocks** | `minecraft:cracked_stone_bricks`; `minecraft:mossy_stone_bricks` | |
| **structures** | `minecraft:stronghold` | |
## trail_ruins
| Key | Conditions | Anticonditions |
@@ -150,17 +149,16 @@ This file contains all current spawn presets of cobblemon.
|------------------------|------------------------------|-----------------------------------------------|
| **neededNearbyBlocks** | `#cobblemon:concrete_blocks` | |
| **structures** | | `#minecraft:village`; `minecraft:trail_ruins` |
## water
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **fluid** | `#minecraft:water` | |
| Key | Conditions | Anticonditions |
|-----------|--------------------|----------------|
| **fluid** | `#minecraft:water` | |
## webs
| Key | Conditions | Anticonditions |
| --- | ---------- | -------------- |
| **biomes** | `biomesoplenty:spider_nest`; `terralith:cave/infested_caves`; `wythers:forbidden_forest`; `wythers:phantasmal_forest`; `wythers:phantasmal_swamp` | |
| Key | Conditions | Anticonditions |
|------------|---------------------------------------------------------------------------------------------------------------------------------------------------|----------------|
| **biomes** | `biomesoplenty:spider_nest`; `terralith:cave/infested_caves`; `wythers:forbidden_forest`; `wythers:phantasmal_forest`; `wythers:phantasmal_swamp` | |
## wild
| Key | Conditions | Anticonditions |
+5 -1
View File
@@ -39,6 +39,7 @@ repositories {
mavenLocal()
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.terraformersmc.com/")
}
dependencies {
@@ -56,10 +57,13 @@ dependencies {
modApi(libs.fabric.api)
modApi(libs.bundles.fabric)
modCompileOnly(libs.bundles.fabric.integrations.compileOnly) {
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
isTransitive = false
}
modImplementation(libs.bundles.fabric.integrations.implementation)
modRuntimeOnly(libs.bundles.fabric.integrations.runtimeOnly)
// modImplementation(libs.flywheelFabric)
// include(libs.flywheelFabric)
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.fabric.client.integration.modmenu
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen
import com.terraformersmc.modmenu.api.ConfigScreenFactory
import com.terraformersmc.modmenu.api.ModMenuApi
class CobblemonModMenu : ModMenuApi {
override fun getModConfigScreenFactory(): ConfigScreenFactory<*> {
return ConfigScreenFactory { parent ->
CobblemonConfigScreen(parent)
}
}
}
@@ -27,6 +27,9 @@
],
"dynamiclights": [
"com.cobblemon.mod.fabric.compat.lambdynamiclights.LambDynamicLightsCompatEntrypoint"
],
"modmenu": [
"com.cobblemon.mod.fabric.client.integration.modmenu.CobblemonModMenu"
]
},
"mixins": [
+2 -2
View File
@@ -11,7 +11,7 @@ mc_version = 1.21.1
parchment_version = 2024.07.28@zip
mod_id=cobblemon
mod_version=1.6.1
snapshot=false
mod_version=1.7.0
snapshot=true
fabric.loom.multiProjectOptimisation=false
+1
View File
@@ -7,6 +7,7 @@ repositories {
maven("https://maven.architectury.dev/")
maven("https://maven.fabricmc.net/")
maven("https://maven.minecraftforge.net/")
maven("https://maven.terraformersmc.com/")
}
dependencies {
+4 -1
View File
@@ -25,6 +25,7 @@ fabric-kotlin = "1.11.0+kotlin.2.0.0"
fabric-permissions-api = "0.2-SNAPSHOT"
lambDynamicLights = "3.0.1+1.21.1"
debugutils-fabric = "1.21-1.0.6-fabric"
mod-menu = "11.0.3"
# NeoForge
neoforge = "21.1.66"
@@ -70,6 +71,7 @@ lambDynamicLights = { group = "maven.modrinth", name = "lambdynamiclights", vers
jei-api-fabric = { group = "mezz.jei", name = "jei-1.21-fabric-api", version.ref = "jei-api" }
jei-fabric = { group = "mezz.jei", name = "jei-1.21-fabric", version.ref = "jei-api"}
fabric-debugutils = { group = "maven.modrinth", name = "debugutils", version.ref = "debugutils-fabric"}
mod-menu = { group = "com.terraformersmc", name = "modmenu", version.ref = "mod-menu" }
# NeoForge
neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" }
@@ -90,7 +92,8 @@ classgraph = { group = "io.github.classgraph", name = "classgraph", version.ref
[bundles]
kotlin = [ "kotlin-stdlib", "kotlin-reflect" ]
fabric = [ "fabric-kotlin", "fabric-permissions" ]
fabric-integrations-compileOnly = [ "adorn-fabric", "lambDynamicLights", "jei-api-fabric" ]
common-integrations-compileOnly = [ "adorn-fabric", "lambDynamicLights", "jei-api-fabric" ]
fabric-integrations-implementation = [ "mod-menu" ]
fabric-integrations-runtimeOnly = [ "jei-fabric" ]
mongo = [ "mongo-driver-core", "mongo-driver-sync" ]
unitTesting = [ "junit-fabric", "junit-params", "mockito-core", "mock-kotlin", "classgraph" ]
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonClient.pokedexUsageContext
import com.cobblemon.mod.common.client.CobblemonClient.reloadCodedAssets
import com.cobblemon.mod.common.client.gui.config.CobblemonConfigScreen
import com.cobblemon.mod.common.client.keybind.CobblemonKeyBinds
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.client.render.atlas.CobblemonAtlases
@@ -31,6 +32,7 @@ import java.util.function.Supplier
import net.minecraft.client.Minecraft
import net.minecraft.client.color.block.BlockColor
import net.minecraft.client.color.item.ItemColor
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.model.geom.ModelLayerLocation
import net.minecraft.client.model.geom.builders.LayerDefinition
import net.minecraft.client.particle.ParticleProvider
@@ -54,6 +56,8 @@ import net.minecraft.world.item.Item
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityType
import net.neoforged.fml.ModContainer
import net.neoforged.fml.ModList
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
import net.neoforged.neoforge.client.ClientHooks
import net.neoforged.neoforge.client.event.ClientTickEvent
@@ -64,6 +68,7 @@ import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent
import net.neoforged.neoforge.client.event.RegisterShadersEvent
import net.neoforged.neoforge.client.event.RenderGuiEvent
import net.neoforged.neoforge.client.event.RenderGuiLayerEvent
import net.neoforged.neoforge.client.gui.IConfigScreenFactory
import net.neoforged.neoforge.client.gui.VanillaGuiLayers
import net.neoforged.neoforge.common.NeoForge
import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS
@@ -93,6 +98,11 @@ object CobblemonNeoForgeClient : CobblemonClientImplementation {
CobblemonModelPredicateRegistry.registerPredicates()
}
NeoForgeClientPlatformEventHandler.register()
val modContainer = ModList.get().getModContainerById(Cobblemon.MODID).get()
modContainer.registerExtensionPoint(IConfigScreenFactory::class.java, object : IConfigScreenFactory {
override fun createScreen(modContainer: ModContainer, arg: Screen): Screen = CobblemonConfigScreen(arg)
})
}
private fun onRegisterReloadListener(event: RegisterClientReloadListenersEvent) {