Compare commits

..
Author SHA1 Message Date
JazzMcNade 6ba2804a46 Added type checking 2024-05-14 00:46:40 -05:00
JazzMcNade d13d5183c8 Implemented missing functions. 2024-05-14 00:24:09 -05:00
1359 changed files with 3589 additions and 39391 deletions
-1
View File
@@ -3,4 +3,3 @@
# are changed when only line endings change.
src/generated/**/.cache/cache text eol=lf
src/generated/**/*.json text eol=lf
CHANGELOG.md merge=union
-1
View File
@@ -36,7 +36,6 @@ common/src/main/resources/assets/cobblemon/showdown
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
/.ignored/
common/.fabric
# MacOS
.DS_Store
+3 -130
View File
@@ -1,138 +1,11 @@
# Changelog
## [1.5.3](#1-5-3)
### Fixes
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
- Fixed the Seafloor spawning context not being a usable context.
- Fixed Pokemon spawning in non-full blocks like slabs.
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
### Additions
- Added extra visuals to early berry growth stages
### Changes
- Altered the item model for Medicinal Leeks and Roasted Leeks to be held like sticks and other rod items.
- Adjusted some berry balance values like yield and growth times
- Changes to mulch buffs/durations
### Developer
### Data Pack & Resource Pack Creators
- Added support for "shedders" similar to Shedinja's evolution logic.
## [1.5.2 (May 27th, 2024)](#1-5-2)
### Fixes
- Fixed Wooloo and Dubwool dyeing making your game start dying. Only if you do it on a server.
- Fixed Gimmighoul causing PCs and parties to look glitched out, also only in a server.
- Fixed players being able to dye other people's sheep Pokémon.
## [1.5.1 (May 27th, 2024)](#1-5-1)
### Additions
- Added unique send out particles for Cherish, Dream, Beast, and Ancient Origin balls.
- Made Wooloo and Dubwool dye-able like sheep. So cute!
- Added stat up and down particles.
- Most status effects now have particles! These include: Paralysis, Poison, Sleep, Confusion, Infatuation.
#### Move Particle Effects
- Confusion
- Cotton Guard
- Growl
- Ice Punch
- Fire Punch
- Thunder Punch
- Minimize
- Quick Attack
- Protect
- Swords Dance
- Sand Attack
- Poison Powder
- Sleep Powder
- Stun Spore
- Powder
- Rage Powder
- Spore
- Thunder Wave
### Changes
- Sounds for Relic Coin Sacks have been correctly renamed. Relic Coin Pouches received new sounds for breaking and placing.
- Readjusted Petilil portraits so they fit a bit better.
- Improved handling of Pokémon taken from the Restoration Tank block to be a bit more stable.
- Made Mulch cheaper to craft.
## [1.5.1](#1-5-1)
### Fixes
- Fixed a bug in which adding organic material to the restoration tank via right click was adding the full count of the stack currently in hand - but only taking 1 of the item.
- Fixed a niche issue where some properties of entities were not initialized correctly, causing Pokémon that appeared to be level 1 until you battle them.
- Fixed Fossilized Drake being missing from the Fossils item tag.
- Fixed Gilded Chest block entity not being cleared on block break, creating spooky ghost blocks. Old ones can be fixed by placing something like a furnace where it was, then breaking the furnace.
- Fixed sherd brokenness on Forge.
- Fixed Supplementaries incompatibility.
- Fixed Fossil Compartment crash with Jade / WAILA forks.
- Fixed pasture block PC lookups when the player is offline.
- Fixed an untranslated battle message that occurs when using a move that just ran out of PP (e.g. Fire Blast that just got spited mid-turn).
- Fixed held items being eaten even when the held item evolutions are already unlocked.
- Fixed Hisuian Decidueye not being Grass/Fighting.
- Fixed both Decidueye forms learning both Triple Arrows and Spirit Shackle.
- Fixed Pineco being unable to evolve into Shulker Forretress.
- Fixed Kabutops T-posing when underwater. It doesn't have proper swimming animations yet, though.
- Fixed Pidgey's missing walk animation.
- Fixed Cyndaquil's hidden flames clipping if it was swimming.
- Fixed Chimecho and Chingling being unable to spawn near bells. They are meant to!
- Fixed Tyrantrum and Wailord Party Overlay models peeking through the chat box. It was kinda funny though.
- Fixed hitbox sizes for Seedot, Nuzleaf, and Shiftry.
- Fixed Budew and Lechonk sliding if they walked for too long.
- Fixed Shedinja T-posing in battle.
- Fixed recoil evolution condition not working, making things like Basculegion unobtainable.
- Fixed issue where poser debug tools didn't work on JSON posers.
- Fixed issue where gilded chests don't close when going far away.
- Fixed issue where the restoration tank's renderer was reading old data, making it appear wrong.
- Fixed issue where the lights on the restoration tank would not animate if it was facing east. Very specific.
- Fixed client crash with the fossil machine when updating block state on a chunk that is unloaded in the client. I don't understand this but the devs are sure that all of those are real words.
- Fixed Restoration Tank crash with Create upon the tank block's destruction.
- Fixed Restoration Tank over consuming items when interacting with Create blocks.
- Fixed addons that add very many moves to a learn-set causing disappearing Pokémon (visually) issues on servers.
- Fixed Hyper Cutter and Big Pecks incorrectly stating that it prevented accuracy from being lowered in battle.
- Fixed missing messages for Rough Skin and Iron Barbs in battle.
- Fixed a bug where sometimes Pokémon sendouts wouldn't create an entity, or the entity would spawn at 0 0 0 which is not a good place for a Pokémon to be. Or any of us, really.
- Fixed issue in which a locked gilded chest would animate to the open state when the client fails to open it, such as when it is locked.
- Fixed a bug where aspects of a form would not be properly reflected on form changes (eg. Normal -> Hisui).
- Fixed generic battle effect sounds not sounding the way they were intended to.
- Fixed particle effects often not having access to some specific entity functions from MoLang.
- Fixed particles sometimes lasting a single tick too long, causing (very quick) visual glitches.
- Fixed particle rotations being inverted.
- Fixed particle events not spawning at the instigating particle's location.
- Fixed a bunch of spam during world generation.
- Fixed a bug in which throwing a Poké Ball at a player owned Pokémon with the ability Illusion would reveal its true species. Hilarious meta strategy.
- Fixed root-part animations not working for JSON posed Pokémon. You didn't notice this but if we didn't fix this in this update then if you use Quick Attack a lot you'd have seen a whole lot of [this](https://cdn.discordapp.com/attachments/1076993968803434627/1242660506783715369/Minecraft__1.20.1_-_Singleplayer_2024-05-21_22-08-17.mp4?ex=66549408&is=66534288&hm=ff95ee293eb15634fd63e6546534ea279540a1c892605e8d561593ca2c5600c5&) which is damn funny but very unintended.
-Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
### Developer
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. Provides more information this way.
- Added an event that fires when a fossil is revived, with or without a player.
- Added IVS and EVS property extractors.
- Fixed PCStore#resize not allowing PC boxes size reduction.
### Data Pack & Resource Pack Creators
- Added support for MoLang conditions for quirks and poses.
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual. There is some backwards compatibility for AttackDefenceRatio, though.
- Changed "dimensions" spawn condition to check with dimension IDs instead of effects, so custom dimension IDs can be used.
- Added parametric motion and rotation support to particle effects.
- Added entity_scale as a molang var for particles (likely only applicable to Pokemon)
- Added support for primary quirk animations using the following format:
```JSON
{
"quirks": [
"q.bedrock_primary_quirk('<pokemon>', '<animation>', <minSeconds>, <maxSeconds>, <loopTimes>, '<excludedLabels>', q.curve('<waveFunction>'))"
]
}
```
### Localization
- Updated translations for:
- Simplified and Traditional Chinese.
- Spanish.
## [1.5.0 - The Ruins and Revival Update (May 12th, 2024)](#1-5-0)
## [1.5.0 - The Ruins and Revival Update (May Xth, 2024)](#1-5-0)
#### "You're telling me that Mojang has added archaeology to the game? Hmm... that gives me an idea."
### Additions
@@ -102,6 +102,7 @@ import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.sherds.CobblemonSherds
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.DataKeys
@@ -234,7 +235,7 @@ object Cobblemon {
val player = event.player
val block = player.world.getBlockState(event.pos).block
player.party().forEach { pokemon ->
pokemon.lockedEvolutions
pokemon.evolutions
.filterIsInstance<BlockClickEvolution>()
.forEach { evolution ->
evolution.attemptEvolution(pokemon, BlockClickEvolution.BlockInteractionContext(block, player.world))
@@ -371,6 +372,36 @@ object Cobblemon {
BATTLE_VICTORY.subscribe { AdvancementHandler.onWinBattle(it) }
EVOLUTION_COMPLETE.subscribe(Priority.LOWEST) { event ->
AdvancementHandler.onEvolve(event)
val pokemon = event.pokemon
val ninjaskIdentifier = cobblemonResource("ninjask")
// Ensure the config option is enabled and that the result was a ninjask and that shedinja exists
if (this.config.ninjaskCreatesShedinja && pokemon.species.resourceIdentifier == ninjaskIdentifier && PokemonSpecies.getByIdentifier(Pokemon.SHEDINJA) != null) {
val player = pokemon.getOwnerPlayer() ?: return@subscribe
if (player.isCreative || player.inventory.containsAny { it.item is PokeBallItem }) {
var pokeball = Items.AIR
player.inventory.combinedInventory.forEach {
it.forEach {
itemStack -> if (itemStack.item is PokeBallItem && pokeball == Items.AIR) {
pokeball = itemStack.item as PokeBallItem
}
}
}
if (!player.isCreative) {
player.inventory.removeAmountIf(1) { it.item is PokeBallItem }
}
if (pokeball == Items.AIR) {
pokeball = CobblemonItems.POKE_BALL
}
val properties = event.evolution.result.copy()
properties.species = Pokemon.SHEDINJA.toString()
val product = pokemon.clone()
product.removeHeldItem()
properties.apply(product)
product.caughtBall = (pokeball as PokeBallItem).pokeBall
pokemon.storeCoordinates.get()?.store?.add(product)
CobblemonCriteria.EVOLVE_POKEMON.trigger(player, EvolvePokemonContext(event.pokemon.preEvolution!!.species.resourceIdentifier, product.species.resourceIdentifier, playerData.get(player).advancementData.totalEvolvedCount))
}
}
}
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
@@ -385,6 +416,7 @@ object Cobblemon {
).ifSuccessful { it.mute = true }
}
CobblemonSherds.registerSherds()
}
fun getLevel(dimension: RegistryKey<World>): World? {
@@ -214,213 +214,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_TUMBLESTONE_STAIRS = this.create("polished_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_TUMBLESTONE)))
@JvmField
val POLISHED_TUMBLESTONE_SLAB = this.create("polished_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_TUMBLESTONE_WALL = this.create("polished_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_POLISHED_TUMBLESTONE = this.create("chiseled_polished_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val TUMBLESTONE_BRICKS = this.create("tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val TUMBLESTONE_BRICK_STAIRS = this.create("tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(TUMBLESTONE_BRICKS)))
@JvmField
val TUMBLESTONE_BRICK_SLAB = this.create("tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val TUMBLESTONE_BRICK_WALL = this.create("tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_TUMBLESTONE_BRICKS = this.create("chiseled_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_SKY_TUMBLESTONE = this.create("polished_sky_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_SKY_TUMBLESTONE_STAIRS = this.create("polished_sky_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_SKY_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_SKY_TUMBLESTONE)))
@JvmField
val POLISHED_SKY_TUMBLESTONE_SLAB = this.create("polished_sky_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_SKY_TUMBLESTONE_WALL = this.create("polished_sky_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_POLISHED_SKY_TUMBLESTONE = this.create("chiseled_polished_sky_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BRICKS = this.create("sky_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BRICK_STAIRS = this.create("sky_tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(
SKY_TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(SKY_TUMBLESTONE_BRICKS)))
@JvmField
val SKY_TUMBLESTONE_BRICK_SLAB = this.create("sky_tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BRICK_WALL = this.create("sky_tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_SKY_TUMBLESTONE_BRICKS = this.create("chiseled_sky_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_BLACK_TUMBLESTONE = this.create("polished_black_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_BLACK_TUMBLESTONE_STAIRS = this.create("polished_black_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_BLACK_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_BLACK_TUMBLESTONE)))
@JvmField
val POLISHED_BLACK_TUMBLESTONE_SLAB = this.create("polished_black_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val POLISHED_BLACK_TUMBLESTONE_WALL = this.create("polished_black_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_POLISHED_BLACK_TUMBLESTONE = this.create("chiseled_polished_black_tumblestone", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BRICKS = this.create("black_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BRICK_STAIRS = this.create("black_tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(
BLACK_TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(BLACK_TUMBLESTONE_BRICKS)))
@JvmField
val BLACK_TUMBLESTONE_BRICK_SLAB = this.create("black_tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BRICK_WALL = this.create("black_tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val CHISELED_BLACK_TUMBLESTONE_BRICKS = this.create("chiseled_black_tumblestone_bricks", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_APRICORN = apricornBlock("black_apricorn", Apricorn.BLACK)
@JvmField
@@ -441,7 +234,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_pouch",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS)
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f)
.nonOpaque(), true
@@ -452,7 +245,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
"relic_coin_sack",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.RELIC_COIN_SACK_SOUNDS)
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f), false
))
@@ -735,7 +528,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val WEPEAR_BERRY = this.berryBlock("wepear")
val WIKI_BERRY = this.berryBlock("wiki")
val YACHE_BERRY = this.berryBlock("yache")
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(AbstractBlock.Settings.create()))
init {
/**
@@ -23,7 +23,6 @@ import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
import com.cobblemon.mod.common.item.battle.DireHitItem
import com.cobblemon.mod.common.item.battle.GuardSpecItem
import com.cobblemon.mod.common.item.battle.XStatItem
import com.cobblemon.mod.common.item.berry.BerryItem
import com.cobblemon.mod.common.item.berry.FriendshipRaisingBerryItem
import com.cobblemon.mod.common.item.berry.HealingBerryItem
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
@@ -933,67 +932,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val POLISHED_TUMBLESTONE = blockItem("polished_tumblestone", CobblemonBlocks.POLISHED_TUMBLESTONE)
@JvmField
val POLISHED_TUMBLESTONE_STAIRS = blockItem("polished_tumblestone_stairs", CobblemonBlocks.POLISHED_TUMBLESTONE_STAIRS)
@JvmField
val POLISHED_TUMBLESTONE_SLAB = blockItem("polished_tumblestone_slab", CobblemonBlocks.POLISHED_TUMBLESTONE_SLAB)
@JvmField
val POLISHED_TUMBLESTONE_WALL = blockItem("polished_tumblestone_wall", CobblemonBlocks.POLISHED_TUMBLESTONE_WALL)
@JvmField
val CHISELED_POLISHED_TUMBLESTONE = blockItem("chiseled_polished_tumblestone", CobblemonBlocks.CHISELED_POLISHED_TUMBLESTONE)
@JvmField
val TUMBLESTONE_BRICKS = blockItem("tumblestone_bricks", CobblemonBlocks.TUMBLESTONE_BRICKS)
@JvmField
val TUMBLESTONE_BRICK_STAIRS = blockItem("tumblestone_brick_stairs", CobblemonBlocks.TUMBLESTONE_BRICK_STAIRS)
@JvmField
val TUMBLESTONE_BRICK_SLAB = blockItem("tumblestone_brick_slab", CobblemonBlocks.TUMBLESTONE_BRICK_SLAB)
@JvmField
val TUMBLESTONE_BRICK_WALL = blockItem("tumblestone_brick_wall", CobblemonBlocks.TUMBLESTONE_BRICK_WALL)
@JvmField
val CHISELED_TUMBLESTONE_BRICKS = blockItem("chiseled_tumblestone_bricks", CobblemonBlocks.CHISELED_TUMBLESTONE_BRICKS)
@JvmField
val POLISHED_SKY_TUMBLESTONE = blockItem("polished_sky_tumblestone", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE)
@JvmField
val POLISHED_SKY_TUMBLESTONE_STAIRS = blockItem("polished_sky_tumblestone_stairs", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_STAIRS)
@JvmField
val POLISHED_SKY_TUMBLESTONE_SLAB = blockItem("polished_sky_tumblestone_slab", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_SLAB)
@JvmField
val POLISHED_SKY_TUMBLESTONE_WALL = blockItem("polished_sky_tumblestone_wall", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_WALL)
@JvmField
val CHISELED_POLISHED_SKY_TUMBLESTONE = blockItem("chiseled_polished_sky_tumblestone", CobblemonBlocks.CHISELED_POLISHED_SKY_TUMBLESTONE)
@JvmField
val SKY_TUMBLESTONE_BRICKS = blockItem("sky_tumblestone_bricks", CobblemonBlocks.SKY_TUMBLESTONE_BRICKS)
@JvmField
val SKY_TUMBLESTONE_BRICK_STAIRS = blockItem("sky_tumblestone_brick_stairs", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_STAIRS)
@JvmField
val SKY_TUMBLESTONE_BRICK_SLAB = blockItem("sky_tumblestone_brick_slab", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_SLAB)
@JvmField
val SKY_TUMBLESTONE_BRICK_WALL = blockItem("sky_tumblestone_brick_wall", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_WALL)
@JvmField
val CHISELED_SKY_TUMBLESTONE_BRICKS = blockItem("chiseled_sky_tumblestone_bricks", CobblemonBlocks.CHISELED_SKY_TUMBLESTONE_BRICKS)
@JvmField
val POLISHED_BLACK_TUMBLESTONE = blockItem("polished_black_tumblestone", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE)
@JvmField
val POLISHED_BLACK_TUMBLESTONE_STAIRS = blockItem("polished_black_tumblestone_stairs", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_STAIRS)
@JvmField
val POLISHED_BLACK_TUMBLESTONE_SLAB = blockItem("polished_black_tumblestone_slab", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_SLAB)
@JvmField
val POLISHED_BLACK_TUMBLESTONE_WALL = blockItem("polished_black_tumblestone_wall", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_WALL)
@JvmField
val CHISELED_POLISHED_BLACK_TUMBLESTONE = blockItem("chiseled_polished_black_tumblestone", CobblemonBlocks.CHISELED_POLISHED_BLACK_TUMBLESTONE)
@JvmField
val BLACK_TUMBLESTONE_BRICKS = blockItem("black_tumblestone_bricks", CobblemonBlocks.BLACK_TUMBLESTONE_BRICKS)
@JvmField
val BLACK_TUMBLESTONE_BRICK_STAIRS = blockItem("black_tumblestone_brick_stairs", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_STAIRS)
@JvmField
val BLACK_TUMBLESTONE_BRICK_SLAB = blockItem("black_tumblestone_brick_slab", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_SLAB)
@JvmField
val BLACK_TUMBLESTONE_BRICK_WALL = blockItem("black_tumblestone_brick_wall", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_WALL)
@JvmField
val CHISELED_BLACK_TUMBLESTONE_BRICKS = blockItem("chiseled_black_tumblestone_bricks", CobblemonBlocks.CHISELED_BLACK_TUMBLESTONE_BRICKS)
@JvmField
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
"automaton_armor_trim_smithing_template",
@@ -1047,8 +985,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val STEEL_GEM = noSettingsItem("steel_gem")
@JvmField
val FAIRY_GEM = noSettingsItem("fairy_gem")
//@JvmField
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
@@ -235,7 +235,6 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(FormUpdatePacket.ID, FormUpdatePacket::decode, PokemonUpdatePacketHandler())
// Evolution start
this.createClientBound(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler())
@@ -122,18 +122,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossil_machine.unprotected")
@JvmField
val RELIC_COIN_SACK_BREAK = this.create("relic_coin_sack.break")
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
@JvmField
val RELIC_COIN_SACK_HIT = this.create("relic_coin_sack.hit")
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
@JvmField
val RELIC_COIN_SACK_STEP = this.create("relic_coin_sack.step")
val COIN_POUCH_STEP = this.create("coin_pouch.step")
@JvmField
val RELIC_COIN_SACK_PLACE = this.create("relic_coin_sack.place")
@JvmField
val RELIC_COIN_POUCH_BREAK = this.create("relic_coin_pouch.break")
@JvmField
val RELIC_COIN_POUCH_PLACE = this.create("relic_coin_pouch.place")
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
@@ -194,22 +189,13 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
@JvmField
val RELIC_COIN_SACK_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_SACK_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_SACK_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
COIN_POUCH_BREAK,
COIN_POUCH_STEP,
COIN_POUCH_PLACE,
COIN_POUCH_HIT,
COIN_POUCH_STEP
)
@JvmField
val RELIC_COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
RELIC_COIN_POUCH_BREAK,
RELIC_COIN_SACK_STEP,
RELIC_COIN_POUCH_PLACE,
RELIC_COIN_SACK_HIT,
RELIC_COIN_SACK_STEP
)
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@JvmField
@@ -226,7 +226,7 @@ open class PokemonBattle(
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
val pokemon = opponentPokemon.effectedPokemon
if (facedFainted) {
pokemon.lockedEvolutions.forEach { evolution ->
pokemon.evolutions.forEach { evolution ->
evolution.requirements.filterIsInstance<DefeatRequirement>().forEach { defeatRequirement ->
if (defeatRequirement.target.matches(faintedPokemon.effectedPokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatEvolutionProgress() }
@@ -16,7 +16,7 @@ import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
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.item.berry.BerryItem
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.readBox
import com.cobblemon.mod.common.util.writeBox
@@ -45,8 +45,9 @@ import net.minecraft.world.biome.Biome
* @property identifier The [Identifier] of this berry.
* @property baseYield The [IntRange] possible for the berry tree before [bonusYield] is calculated.
* @property preferredBiomeTags The [TagKey]s of the berries preffered biomes. Determines spawning and yield
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow frome age 0 to age 3
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to grow from age 3 to age 5
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow.
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to regrow its berries once it has been harvested.
* @depreciated @property lifeCycles The [IntRange] possible for the berry to live for between harvests.
* @property favoriteMulches The types of mulches that benefit this berries yield
* @property growthFactors An array of [GrowthFactor]s that will affect this berry. The client is not aware of these.
* @property mutations A map of the partner berry as the key and the value as the resulting mutation with this berry.
@@ -59,7 +60,6 @@ import net.minecraft.world.biome.Biome
* @property flowerTexture The [Identifier] for the texture of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitModelIdentifier The [Identifier] for the model of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitTexture The [Identifier] for the texture of the berry in flower form.
* @property stageOnePositioning Transformation of the berry model for age 0
*
* @throws IllegalArgumentException if the any yield range argument is not a positive range.
*/
@@ -69,6 +69,7 @@ class Berry(
val preferredBiomeTags: List<TagKey<Biome>>,
val growthTime: IntRange,
val refreshRate: IntRange,
//val lifeCycles: IntRange,
val favoriteMulches: EnumSet<MulchVariant>,
val growthFactors: Collection<GrowthFactor>,
val spawnConditions: List<BerrySpawnCondition>,
@@ -87,9 +88,7 @@ class Berry(
@SerializedName("fruitModel")
val fruitModelIdentifier: Identifier,
val fruitTexture: Identifier,
val stageOnePositioning: GrowthPoint,
val weight: Float,
val boneMealChance: Float
val weight: Float
) {
@Transient
@@ -162,9 +161,8 @@ class Berry(
val bonus = this.bonusYield(world, state, pos)
var yield = base + bonus.first
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (BerryBlock.getMulch(treeEntity) == MulchVariant.RICH) {
//I hope the upper bound isnt exclusive, especially when there's a method called nextBetweenExclusive
yield += world.random.nextBetween(1, 3)
if (BerryBlock.getMulch(state) == MulchVariant.RICH) {
yield = min(yield + 1, maxYield())
treeEntity.decrementMulchDuration(world, pos, state)
}
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
@@ -281,13 +279,6 @@ class Berry(
buffer.writeIdentifier(this.flowerTexture)
buffer.writeIdentifier(this.fruitModelIdentifier)
buffer.writeIdentifier(this.fruitTexture)
buffer.writeDouble(stageOnePositioning.position.x)
buffer.writeDouble(stageOnePositioning.position.y)
buffer.writeDouble(stageOnePositioning.position.z)
buffer.writeDouble(stageOnePositioning.rotation.x)
buffer.writeDouble(stageOnePositioning.rotation.y)
buffer.writeDouble(stageOnePositioning.rotation.z)
buffer.writeFloat(boneMealChance)
}
/**
@@ -301,8 +292,7 @@ class Berry(
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
var bonus = 0
val passed = arrayListOf<GrowthFactor>()
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return 0 to passed
val mulchVariant = BerryBlock.getMulch(treeEntity)
val mulchVariant = BerryBlock.getMulch(state)
val hasBiomeMulch = mulchVariant in favoriteMulches
this.growthFactors.forEach { factor ->
if (factor.isValid(world, state, pos)) {
@@ -349,9 +339,7 @@ class Berry(
val flowerTexture = buffer.readIdentifier()
val fruitModelIdentifier = buffer.readIdentifier()
val fruitTexture = buffer.readIdentifier()
val stageOneYPos = GrowthPoint(Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()), Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()))
val boneMealChance = buffer.readFloat()
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, stageOneYPos, 0F, boneMealChance)
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, 0F)
}
}
@@ -78,8 +78,6 @@ object CobblemonEvents {
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
@JvmField
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
@JvmField
val FOSSIL_REVIVED = EventObservable<FossilRevivedEvent>()
// @JvmField
// val EGG_HATCH = EventObservable<HatchEggEvent>()
@JvmField
@@ -1,17 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.network.ServerPlayerEntity
data class FossilRevivedEvent (
val pokemon: Pokemon,
val player: ServerPlayerEntity?
)
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common.api.moves.animations
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import java.util.concurrent.CompletableFuture
@@ -41,9 +43,6 @@ class ActionEffectTimeline(
// .toList copy because I'm paranoid about iterators being trying to share between identical effects playing
chainKeyframes(context, timeline.toList().iterator(), finalFuture)
finalFuture
}.exceptionallyCompose {
it.printStackTrace()
CompletableFuture.completedFuture(Unit)
}
}
@@ -56,7 +55,6 @@ class ActionEffectTimeline(
keyframe.play(context)
.thenRun { context.currentKeyframes.remove(keyframe) }
.thenApply { chainKeyframes(context, iterator, finalFuture) }
.exceptionally { finalFuture.completeExceptionally(it) }
}
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
@@ -39,7 +40,7 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val effectIdentifier = try {
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: effect
effect?.asExpressionLike()?.resolveString(context.runtime)
} catch (e: Exception) {
effect
}?.asIdentifierDefaultingNamespace() ?: return skip()
@@ -8,30 +8,24 @@
package com.cobblemon.mod.common.api.mulch
import com.cobblemon.mod.common.BakingOverride
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
import net.minecraft.util.StringIdentifiable
/**
* Represents the different types of Mulch implemented in the mod.
*
*/
enum class MulchVariant(val model: BakingOverride?, val duration: Int = -1) : StringIdentifiable {
COARSE(CobblemonBakingOverrides.COARSE_MULCH),
GROWTH(CobblemonBakingOverrides.GROWTH_MULCH, 5),
HUMID(CobblemonBakingOverrides.HUMID_MULCH),
LOAMY(CobblemonBakingOverrides.LOAMY_MULCH),
PEAT(CobblemonBakingOverrides.PEAT_MULCH),
RICH(CobblemonBakingOverrides.RICH_MULCH, 5),
SANDY(CobblemonBakingOverrides.SANDY_MULCH),
SURPRISE(CobblemonBakingOverrides.SURPRISE_MULCH, 3),
NONE(null);
enum class MulchVariant(val duration: Int = -1) : StringIdentifiable {
COARSE,
GROWTH(3),
HUMID,
LOAMY,
PEAT,
RICH(3),
SANDY,
SURPRISE(3),
NONE;
override fun asString(): String {
return name.lowercase()
}
companion object {
const val GROWTH_TIME_MULTIPLIER = 0.5
}
}
@@ -60,14 +60,13 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
super.onBreak(world, pos, state, player)
if (!world.isClient) {
val entity = world.getBlockEntity(pos)
if (entity is MultiblockEntity && entity.multiblockStructure != null) {
entity.multiblockStructure!!.onBreak(world, pos, state, player)
}
entity?.markRemoved()
}
super.onBreak(world, pos, state, player)
}
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
@@ -79,7 +78,6 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
return BlockRenderType.MODEL
}
//This is done so a block picked with NBT doesnt absolutely DESTROY multiblocks
override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState): ItemStack {
val blockEntity = world.getBlockEntity(pos) as? MultiblockEntity ?: return ItemStack.EMPTY
return if (blockEntity.multiblockStructure == null) super.getPickStack(world, pos, state) else ItemStack.EMPTY
@@ -47,10 +47,6 @@ fun interface PokemonPropertyExtractor {
val NICKNAME = add { pokemon, properties -> properties.nickname = pokemon.nickname }
@JvmField
val STATUS = add { pokemon, properties -> properties.status = pokemon.status?.status?.showdownName }
@JvmField
val IVS = add { pokemon, properties -> properties.ivs = pokemon.ivs }
@JvmField
val EVS = add { pokemon, properties -> properties.evs = pokemon.evs }
@JvmField
val ILLUSION = mutableListOf(SPECIES, FORM, ASPECTS, GENDER, NICKNAME, SHINY)
@@ -8,11 +8,7 @@
package com.cobblemon.mod.common.api.pokemon.evolution
import com.cobblemon.mod.common.Cobblemon.playerData
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
@@ -21,9 +17,7 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
@@ -31,7 +25,6 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.party
import net.minecraft.item.ItemStack
import net.minecraft.sound.SoundCategory
@@ -51,11 +44,6 @@ interface Evolution : EvolutionLike {
*/
val result: PokemonProperties
/**
* The shed result of this evolution.
*/
val shedder: PokemonProperties?
/**
* If this evolution allows the user to choose when to start it or not.
*/
@@ -107,36 +95,6 @@ interface Evolution : EvolutionLike {
return true
}
fun shed(pokemon: Pokemon): Boolean {
val innerShedder = shedder ?: return false
val owner = pokemon.getOwnerPlayer() ?: return false
// If the player has at least one Pokeball in their inventory.
var pokeballStack: ItemStack? = null
if (!owner.isCreative) {
for (i in 0 until owner.inventory.size()) {
val stackI = owner.inventory.getStack(i)
if (stackI.isIn(CobblemonItemTags.POKE_BALLS)) {
pokeballStack = stackI
}
}
if (pokeballStack == null) return false
}
// If the player has at least one empty spot in their party.
val emptySlot = owner.party().getFirstAvailablePosition() ?: return false
// Add shed Pokemon to player's party
val shedPokemon = pokemon.clone()
shedPokemon.removeHeldItem()
innerShedder.apply(shedPokemon)
shedPokemon.caughtBall = ((pokeballStack?.item ?: CobblemonItems.POKE_BALL) as PokeBallItem).pokeBall
pokemon.storeCoordinates.get()?.store?.add(shedPokemon)
CobblemonCriteria.EVOLVE_POKEMON.trigger(owner, EvolvePokemonContext(pokemon.preEvolution!!.species.resourceIdentifier, shedPokemon.species.resourceIdentifier, playerData.get(owner).advancementData.totalEvolvedCount))
// Consume one of the balls
pokeballStack?.decrement(1)
return true
}
/**
* Starts this evolution as soon as possible.
@@ -195,9 +153,8 @@ interface Evolution : EvolutionLike {
pokemon.getOwnerPlayer()?.sendMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
}
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLVING, SoundCategory.NEUTRAL, 1F, 1F)
this.shed(pokemon)
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
}
@@ -101,7 +101,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
val level = buffer.readSizedInt(IntSize.U_SHORT)
val moves = mutableListOf<MoveTemplate>()
repeat(times = buffer.readSizedInt(IntSize.U_SHORT)) {
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
Moves.getByNumericalId(buffer.readInt())?.let(moves::add)
}
levelUpMoves[level] = moves
@@ -112,7 +112,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
buffer.writeSizedInt(IntSize.U_BYTE, levelUpMoves.size)
for ((level, moves) in levelUpMoves) {
buffer.writeSizedInt(IntSize.U_SHORT, level)
buffer.writeSizedInt(IntSize.U_SHORT, moves.size)
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
for (move in moves) {
buffer.writeInt(move.num)
}
@@ -51,8 +51,8 @@ enum class Stats(override val identifier: Identifier, override val displayName:
/** Gets the [Stat] from the respective Showdown id. */
fun getStat(statKey: String) = when(statKey) {
"atk", "Attack" -> ATTACK // Hyper Cutter states the full stat name "Attack"
"def", "Defense" -> DEFENCE // Big Pecks states the full stat name "Defense"
"atk" -> ATTACK
"def" -> DEFENCE
"spa" -> SPECIAL_ATTACK
"spd" -> SPECIAL_DEFENCE
"spe" -> SPEED
@@ -8,9 +8,6 @@
package com.cobblemon.mod.common.api.pokemon.status
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.util.Identifier
/**
@@ -23,6 +20,4 @@ open class Status(
val showdownName: String = "",
val applyMessage: String,
val removeMessage: String
) {
fun getActionEffect() = ActionEffects.actionEffects[name]
}
)
@@ -9,29 +9,21 @@
package com.cobblemon.mod.common.api.pokemon.status
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.pokemon.status.VolatileStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.nonpersistent.ConfuseStatus
import com.cobblemon.mod.common.pokemon.status.statuses.nonpersistent.AttractStatus
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import net.minecraft.util.Identifier
/**
* Main API point for Statuses
* Get or register Statuses
*
* NOTE: May seem weird to have so many things called volatile statuses but the package is called nonpersistent.
* Its because volatile is a reserved keyword in Java. Cant use it in a package name
*
* @author Deltric
*/
object Statuses {
private val persistentStatuses = mutableListOf<Status>()
private val volatileStatuses = mutableListOf<Status>()
private val allStatuses = mutableListOf<Status>()
val POISON = registerStatus(PoisonStatus())
@@ -40,23 +32,13 @@ object Statuses {
val SLEEP = registerStatus(SleepStatus())
val FROZEN = registerStatus(FrozenStatus())
val BURN = registerStatus(BurnStatus())
val ATTRACT = registerStatus(AttractStatus())
val CONFUSE = registerStatus(ConfuseStatus())
fun <T: Status> registerStatus(status: T) : T {
if (status is PersistentStatus) {
persistentStatuses.add(status)
}
else if (status is VolatileStatus) {
volatileStatuses.add(status)
}
allStatuses.add(status)
return status
}
fun getStatus(name: Identifier) = allStatuses.find { status -> status.name == name }
fun getStatus(showdownName: String) = allStatuses.find { it.showdownName == showdownName }
fun getPersistentStatuses() = persistentStatuses
fun getPersistentStatuses() = allStatuses.filterIsInstance<PersistentStatus>()
}
@@ -79,15 +79,15 @@ class ParticleEvent(
particleEffect?.let { effect ->
val bedrockParticleEffect = BedrockParticleEffectRepository.getEffect(effect.effect) ?: return@let
val rootMatrix = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER,// -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
EventParticleEffect.EventParticleType.EMITTER_BOUND,// -> storm.matrixWrapper
EventParticleEffect.EventParticleType.EMITTER -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
EventParticleEffect.EventParticleType.PARTICLE,
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
}
val sourceVelocity = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER,// -> storm.sourceVelocity().let { { it } }
EventParticleEffect.EventParticleType.EMITTER_BOUND,// -> storm.sourceVelocity
EventParticleEffect.EventParticleType.EMITTER -> storm.sourceVelocity().let { { it } }
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.sourceVelocity
EventParticleEffect.EventParticleType.PARTICLE -> { { Vec3d.ZERO } }
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getVelocityX(), it.getVelocityY(), it.getVelocityZ()) } ?: Vec3d.ZERO).let { { it } }
}
@@ -15,12 +15,10 @@ import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.codec.CodecMapped
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.math.geometry.transformDirection
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveVec3d
import com.mojang.serialization.Codec
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.PrimitiveCodec
@@ -37,79 +35,20 @@ interface ParticleMotion : CodecMapped {
init {
registerSubtype(ParticleMotionType.DYNAMIC, DynamicParticleMotion::class.java, DynamicParticleMotion.CODEC)
registerSubtype(ParticleMotionType.STATIC, StaticParticleMotion::class.java, StaticParticleMotion.CODEC)
registerSubtype(ParticleMotionType.PARAMETRIC, ParametricParticleMotion::class.java, ParametricParticleMotion.CODEC)
}
}
val type: ParticleMotionType
fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d): Vec3d
fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d
fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float): Vec3d
fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d
}
enum class ParticleMotionType {
DYNAMIC,
PARAMETRIC,
PARAMETRIC, // TODO figure out how that even works
STATIC
}
class ParametricParticleMotion(
var offset: Triple<Expression, Expression, Expression> = Triple(
NumberExpression(0.0),
NumberExpression(0.0),
NumberExpression(0.0)
),
var direction: Triple<Expression, Expression, Expression> = Triple(
NumberExpression(0.0),
NumberExpression(0.0),
NumberExpression(0.0)
)
) : ParticleMotion {
companion object {
val CODEC: Codec<ParametricParticleMotion> = RecordCodecBuilder.create { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
EXPRESSION_CODEC.listOf().fieldOf("offset").forGetter { listOf(it.offset.first, it.offset.second, it.offset.third) },
EXPRESSION_CODEC.listOf().fieldOf("direction").forGetter { listOf(it.direction.first, it.direction.second, it.direction.third) }
).apply(instance) { _, offset, direction -> ParametricParticleMotion(Triple(offset[0], offset[1], offset[2]), Triple(direction[0], direction[1], direction[2])) }
}
}
override val type = ParticleMotionType.PARAMETRIC
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d {
val stormPosition = Vec3d(particle.storm.getX(), particle.storm.getY(), particle.storm.getZ())
val offset = runtime.resolveVec3d(this.offset)
val particlePosition = Vec3d(particle.getX(), particle.getY(), particle.getZ())
val desiredPosition = stormPosition.add(offset)
return desiredPosition.subtract(particlePosition)
}
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = runtime.resolveVec3d(direction).normalize()
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {
offset = Triple(
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression()
)
direction = Triple(
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression(),
MoLang.createParser(buffer.readString()).parseExpression()
)
}
override fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeString(offset.first.getString())
buffer.writeString(offset.second.getString())
buffer.writeString(offset.third.getString())
buffer.writeString(direction.first.getString())
buffer.writeString(direction.second.getString())
buffer.writeString(direction.third.getString())
}
}
class DynamicParticleMotion(
var direction: ParticleMotionDirection = InwardsMotionDirection(),
var speed: Expression = NumberExpression(0.0),
@@ -140,23 +79,14 @@ class DynamicParticleMotion(
return direction.getDirectionVector(runtime, storm, emitterPos, particlePos).normalize().multiply(runtime.resolveDouble(speed))
}
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d {
val acceleration = Vec3d(
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d {
return Vec3d(
runtime.resolveDouble(acceleration.first),
runtime.resolveDouble(acceleration.second),
runtime.resolveDouble(acceleration.third)
)
.subtract(velocity.multiply(20 * runtime.resolveDouble(drag)))
.multiply(1 / 20.0).multiply(1 / 20.0) // blocks per second per second -> blocks per tick per tick
return Vec3d(
velocity.x + acceleration.x,
velocity.y + acceleration.y,
velocity.z + acceleration.z
)
).subtract(velocity.multiply(runtime.resolveDouble(drag)))
}
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = velocity.normalize()
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {
@@ -304,8 +234,7 @@ class StaticParticleMotion : ParticleMotion {
override val type = ParticleMotionType.STATIC
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d) = velocity
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = velocity.normalize()
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d) = Vec3d.ZERO
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {}
override fun writeToBuffer(buffer: PacketByteBuf) {}
@@ -32,7 +32,6 @@ interface ParticleRotation : CodecMapped {
) {
init {
registerSubtype(ParticleRotationType.DYNAMIC, DynamicParticleRotation::class.java, DynamicParticleRotation.CODEC)
registerSubtype(ParticleRotationType.PARAMETRIC, ParametricParticleRotation::class.java, ParametricParticleRotation.CODEC)
}
}
@@ -40,34 +39,7 @@ interface ParticleRotation : CodecMapped {
fun getInitialRotation(runtime: MoLangRuntime): Double
fun getInitialAngularVelocity(runtime: MoLangRuntime): Double
fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double
}
class ParametricParticleRotation(var expression: Expression = NumberExpression(0.0)): ParticleRotation {
companion object {
val CODEC: Codec<ParametricParticleRotation> = RecordCodecBuilder.create { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
EXPRESSION_CODEC.fieldOf("expression").forGetter { it.expression }
).apply(instance) { _, expression ->
ParametricParticleRotation(expression)
}
}
}
override val type = ParticleRotationType.PARAMETRIC
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(expression)
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = 0.0
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double) = runtime.resolveDouble(expression) - angle
override fun readFromBuffer(buffer: PacketByteBuf) {
expression = MoLang.createParser(buffer.readString()).parseExpression()
}
override fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeString(expression.getString())
}
fun getAngularAcceleration(runtime: MoLangRuntime, angularVelocity: Double): Double
}
class DynamicParticleRotation(
@@ -94,15 +66,15 @@ class DynamicParticleRotation(
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(startRotation)
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = runtime.resolveDouble(speed) / 20
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double {
override fun getAngularAcceleration(runtime: MoLangRuntime, angularVelocity: Double): Double {
val acceleration = runtime.resolveDouble(acceleration)
val nextVelocity = angularVelocity * 20 + acceleration
val nextVelocity = angularVelocity + acceleration
val drag = nextVelocity * runtime.resolveDouble(drag)
return angularVelocity + (if (abs(drag) > abs(nextVelocity)) {
return if (abs(drag) > abs(nextVelocity)) {
0.0
} else {
nextVelocity - drag - angularVelocity * 20
})
nextVelocity - drag - angularVelocity
}
}
override fun readFromBuffer(buffer: PacketByteBuf) {
@@ -10,7 +10,12 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.spawning.condition.*
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
@@ -86,7 +91,6 @@ object BestSpawner {
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
SpawningCondition.register(SeafloorSpawningCondition.NAME, SeafloorSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
@@ -96,7 +100,7 @@ object BestSpawner {
SpawningContextCalculator.register(SurfaceSpawningContextCalculator)
SpawningContext.register(name = "grounded", clazz = GroundedSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = SeafloorSpawningCondition.NAME)
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
import com.cobblemon.mod.common.util.Merger
import net.minecraft.block.Block
/**
* Base type for a spawning condition that applies to some kind of [SeafloorSpawningContext]. This
* can be extended for subclasses of [SeafloorSpawningContext].
*
* Borrowed the Grounded context code since it was unintentionally spawning Pokémon on the sea floor
* already. Seems to be working fine /shrug
*
* @author FrankTheFarmer
* @since May 22nd, 2024
*/
abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawningContext> : AreaTypeSpawningCondition<T>() {
var neededBaseBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: T): Boolean {
return if (!super.fits(ctx)) {
false
} else if (minHeight != null && ctx.height < minHeight!!) {
return false
} else if (maxHeight != null && ctx.height > maxHeight!!) {
return false
} else !(neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlock.block, ctx.blockRegistry) })
}
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
super.copyFrom(other, merger)
if (other is SeafloorTypeSpawningCondition) {
neededBaseBlocks = merger.merge(neededBaseBlocks, other.neededBaseBlocks)?.toMutableList()
}
}
}
/**
* A spawning condition for a [SeafloorSpawningContext].
*
* @author FrankTheFarmer
* @since May 22nd, 2024
*/
open class SeafloorSpawningCondition : SeafloorTypeSpawningCondition<SeafloorSpawningContext>() {
override fun contextClass() = SeafloorSpawningContext::class.java
companion object {
const val NAME = "seafloor"
}
}
@@ -78,7 +78,7 @@ abstract class SpawningCondition<T : SpawningContext> {
return false
} else if (ctx.position.z < minZ.orMin() || ctx.position.z > maxZ.orMax()) {
return false
} else if (dimensions != null && dimensions!!.isNotEmpty() && ctx.world.dimensionKey.value !in dimensions!!) {
} else if (dimensions != null && dimensions!!.isNotEmpty() && ctx.world.dimension.effects !in dimensions!!) {
return false
} else if (moonPhase != null && ctx.moonPhase !in moonPhase!!) {
return false
@@ -49,7 +49,7 @@ open class AreaSpawningContext(
* This is not considering the provided state as what the entity would be on top of,
* but rather the space its hitbox would fill.
*/
open fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState): Boolean = !state.isSolid
open fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState): Boolean = !state.isFullCube(world, pos)
override fun postFilter(detail: SpawnDetail): Boolean {
if (!super.postFilter(detail)) {
@@ -34,19 +34,15 @@ abstract class SpawnAction<R>(
/**
* Runs the spawn action and processes any non-null result.
*/
open fun complete(): R? {
if (future.isDone) {
return null
}
open fun complete(): Boolean {
ctx.influences.forEach { it.affectAction(this) }
val result = run()
if (result != null) {
return if (result != null) {
future.complete(result)
true
} else {
future.completeExceptionally(Exception("Nothing was spawned."))
false
}
return result
}
}
@@ -34,8 +34,7 @@ class BestSpawnerConfig {
val contextWeights = mutableMapOf(
"grounded" to 1F,
"submerged" to 0.99F,
"surface" to 0.01F,
"seafloor" to 1F
"surface" to 0.01F
)
val buckets = mutableListOf(
SpawnBucket("common", 93.8F),
@@ -124,7 +124,7 @@ open class PlayerPartyStore(
}
// Passive evolutions
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
val removeList = mutableListOf<Evolution>()
pokemon.evolutionProxy.server().forEach {
if (!it.test(pokemon) && it is LevelUpEvolution && !it.permanent)
@@ -99,7 +99,7 @@ open class PCStore(
this.lockedSize = lockNewSize
if (boxes.size > newSize) {
// reduce
val slicedBoxes = boxes.slice(newSize until boxes.size)
val slicedBoxes = boxes.slice(boxes.size until newSize)
boxes.removeAll(slicedBoxes)
slicedBoxes.flatMap { it.asIterable() }.forEach(overflowHandler)
} else {
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.battles.dispatch
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* An [InterpreterInstruction] that triggers an [ActionEffectTimeline]
* I think it might be better to have this as an abstract class so we can default the fields
*
* @author Apion
* @since May 19, 2024
*/
interface ActionEffectInstruction : InterpreterInstruction {
var future: CompletableFuture<*>
var holds: MutableSet<String>
//To expose via molang, so action effects can do different stuff in different instructions
//e.g. "x is confused" vs "x hit itself in confusion"
val id: Identifier
override fun invoke(battle: PokemonBattle) {
preActionEffect(battle)
val runtime = MoLangRuntime()
battle.addQueryFunctions(runtime.environment.getQueryStruct())
runtime.environment.getQueryStruct().addStandardFunctions()
addMolangQueries(runtime)
runActionEffect(battle, runtime)
postActionEffect(battle)
}
fun preActionEffect(battle: PokemonBattle)
fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime)
fun postActionEffect(battle: PokemonBattle)
fun addMolangQueries(runtime: MoLangRuntime) {
runtime.environment.getQueryStruct()
.addFunction("instruction_id") { StringValue(id.toString()) }
}
}
@@ -8,28 +8,16 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.CauserInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* Format: |-activate|POKEMON|EFFECT
@@ -40,50 +28,19 @@ import java.util.concurrent.CompletableFuture
* @author Hunter
* @since September 25th, 2022
*/
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : ActionEffectInstruction, CauserInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("activate")
override fun preActionEffect(battle: PokemonBattle) {
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : InterpreterInstruction, CauserInstruction {
override fun invoke(battle: PokemonBattle) {
val pokemon = message.battlePokemon(0, battle) ?: return
val effect = message.effectAt(1) ?: return
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemon)
battle.dispatch{
ShowdownInterpreter.lastCauser[battle.battleId] = message
battle.minorBattleActions[pokemon.uuid] = message
GO
}
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
val effect = message.effectAt(1) ?: return@dispatch GO
val status = Statuses.getStatus(effect.id)
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
val providers = mutableListOf<Any>(battle)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
val effect = message.effectAt(1) ?: return@dispatch GO
val pokemonName = pokemon.getName()
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
ShowdownInterpreter.lastCauser[battle.battleId] = message
battle.minorBattleActions[pokemon.uuid] = message
val lang = when (effect.id) {
// Includes a 3rd argument being the magnitude level as a number
"magnitude" -> battleLang("activate.magnitude", message.argumentAt(2)?.toIntOrNull() ?: 1)
@@ -102,8 +59,7 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
}
battle.broadcastChatMessage(lang)
//We check holds here so the chat msg + particle effect happen more concurrently, instead of sequentially
UntilDispatch { "effects" !in holds}
WaitDispatch(1F)
}
}
}
@@ -8,25 +8,14 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* Format: |-boost|POKEMON|STAT|AMOUNT or |-unboost|POKEMON|STAT|AMOUNT
@@ -36,33 +25,9 @@ import java.util.concurrent.CompletableFuture
* @author Hiroku
* @since August 20th, 2022
*/
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): ActionEffectInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("boost")
override fun preActionEffect(battle: PokemonBattle) {
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): InterpreterInstruction {
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
val actionEffect = if (isBoost) BOOST_EFFECT else UNBOOST_EFFECT
val providers = mutableListOf<Any>(battle)
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
override fun invoke(battle: PokemonBattle) {
val pokemon = message.battlePokemon(0, battle) ?: return
val statKey = message.argumentAt(1) ?: return
val stages = message.argumentAt(2)?.toInt() ?: return
@@ -70,7 +35,7 @@ class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMe
val severity = Stats.getSeverity(stages)
val rootKey = if (isBoost) "boost" else "unboost"
battle.dispatch {
battle.dispatchWaiting {
val lang = when {
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
@@ -82,13 +47,6 @@ class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMe
// TODO: replace with context that tracks detailed information such as # of stages
repeat(stages) { pokemon.contextManager.add(context) }
battle.minorBattleActions[pokemon.uuid] = message
return@dispatch UntilDispatch { "effects" !in holds }
}
}
companion object {
val BOOST_EFFECT = ActionEffects.actionEffects[cobblemonResource("boost")]!!
val UNBOOST_EFFECT = ActionEffects.actionEffects[cobblemonResource("unboost")]!!
}
}
@@ -8,27 +8,14 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.util.Identifier
import java.util.concurrent.CompletableFuture
/**
* Formats: |cant|POKEMON|REASON and |cant|POKEMON|REASON|MOVE
@@ -37,40 +24,12 @@ import java.util.concurrent.CompletableFuture
* @author Deltric
* @since January 22nd, 2022
*/
class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("cant")
override fun preActionEffect(battle: PokemonBattle) {
class CantInstruction(val message: BattleMessage): InterpreterInstruction {
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
val name = pokemon.getName()
val status = Statuses.getStatus(effectID)
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
val providers = mutableListOf<Any>(battle)
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
battle.dispatch {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
override fun invoke(battle: PokemonBattle) {
battle.dispatchWaiting {
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
val name = pokemon.getName()
// Move may be null as it's not always given
val moveName = message.moveAt(2)?.displayName ?: run { "(Unrecognized: ${message.argumentAt(2)})".text() }
@@ -79,7 +38,7 @@ class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
"armortail", "damp", "dazzling", "queenlymajesty" -> battleLang("cant.generic", name, moveName)
"par", "slp", "frz" -> {
val status = Statuses.getStatus(effectID)?.name?.path ?: return@dispatch GO
val status = Statuses.getStatus(effectID)?.name?.path ?: return@dispatchWaiting
lang("status.$status.is", name)
}
else -> battleLang("cant.$effectID", name, moveName)
@@ -87,10 +46,6 @@ class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
battle.broadcastChatMessage(lang.red())
battle.minorBattleActions[pokemon.uuid] = message
UntilDispatch { "effects" !in holds }
}
}
}
@@ -8,21 +8,15 @@
package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.battles.ShowdownInterpreter
import com.cobblemon.mod.common.battles.ShowdownInterpreter.lastCauser
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
@@ -31,13 +25,11 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePac
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import java.util.concurrent.CompletableFuture
import kotlin.math.roundToInt
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* Format: |-damage|POKEMON|HP STATUS
@@ -51,87 +43,45 @@ class DamageInstruction(
val actor: BattleActor,
val publicMessage: BattleMessage,
val privateMessage: BattleMessage
) : ActionEffectInstruction {
) : InterpreterInstruction {
var future = CompletableFuture.completedFuture(Unit)
val expectedTarget = publicMessage.battlePokemon(0, actor.battle)
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
override var holds = mutableSetOf<String>()
override val id = cobblemonResource("damage")
override fun preActionEffect(battle: PokemonBattle) {
override fun invoke(battle: PokemonBattle) {
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
val recoiling = privateMessage.optionalArgument("from")?.equals("recoil", true) == true
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
if (recoiling) {
doRecoilEvoChecks(battlePokemon)
if (lastCauser is MoveInstruction) {
battle.dispatch {
//Is the recoil hold currently used anywhere?
UntilDispatch { "recoil" !in lastCauser.holds }
battlePokemon.effectedPokemon.let { pokemon ->
if (RecoilEvolutionProgress.supports(pokemon)) {
val newPercentage = privateMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toIntOrNull() ?: 0
val newHealth = (pokemon.hp * (newPercentage / 100.0)).roundToInt()
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
}
}
}
}
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
val effect = privateMessage.effect()
val source = privateMessage.battlePokemonFromOptional(battle)
source?.let { ShowdownInterpreter.broadcastOptionalAbility(battle, effect, it) }
}
private fun doRecoilEvoChecks(battlePokemon: BattlePokemon) {
battlePokemon.effectedPokemon.let { pokemon ->
if (RecoilEvolutionProgress.supports(pokemon)) {
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException(
"Cant get recoil string"
)
val newHealth =
"([0-9]+).*".toRegex().find(healthStr)?.groups?.get(1)?.value?.toIntOrNull()
?: throw UnsupportedOperationException("Cant get recoil string")
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
val progress = pokemon.evolutionProxy.current()
.progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
}
}
}
}
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
val effect = privateMessage.effect()
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
var status = effect?.id?.let { Statuses.getStatus(it) }
battle.dispatch {
val pokemon = privateMessage.battlePokemon(0, battle) ?: return@dispatch GO
//Showdown doesnt tell us on damage if its poison or toxic so we gotta consult the entity
if (status is PoisonStatus) {
status = pokemon.effectedPokemon.status?.status ?: status
}
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
val providers = mutableListOf<Any>(battle)
battlePokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
val context = ActionEffectContext(
actionEffect = actionEffect,
runtime = runtime,
providers = providers
)
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
future.thenApply { holds.clear() }
return@dispatch GO
}
}
override fun postActionEffect(battle: PokemonBattle) {
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
var causedFaint = newHealth == "0"
val effect = privateMessage.effect()
val source = privateMessage.battlePokemonFromOptional(battle)
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
battle.dispatch {
if (lastCauser is MoveInstruction && recoiling) {
UntilDispatch { "recoil" !in lastCauser.holds }
} else {
GO
}
}
source?.let { ShowdownInterpreter.broadcastOptionalAbility(battle, effect, it) }
battle.dispatch {
val pokemonName = battlePokemon.getName()
val pokemonEntity = battlePokemon.entity
//Play recoil animation if the pokemon recoiling isnt dead
if (!causedFaint && pokemonEntity != null) {
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
pkt.sendToPlayersAround(
@@ -142,7 +92,7 @@ class DamageInstruction(
distance = 50.0
)
}
//Play hit particle
if (pokemonEntity != null) {
RunPosableMoLangPacket(pokemonEntity.id, setOf("q.particle('cobblemon:hit', 'target')")).sendToPlayersAround(
x = pokemonEntity.x,
@@ -207,9 +157,8 @@ class DamageInstruction(
} else if (causedFaint) {
GO
} else {
UntilDispatch {"effects" !in holds}
WaitDispatch(1F)
}
}
}
}
@@ -10,10 +10,10 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.interpreter.Effect
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.Moves
@@ -29,7 +29,6 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.concurrent.CompletableFuture
/**
@@ -122,7 +121,6 @@ class MoveInstruction(
}
runtime.environment.getQueryStruct().addFunction("move") { move.struct }
runtime.environment.getQueryStruct().addFunction("instruction_id") { StringValue(cobblemonResource("move").toString()) }
this.future = actionEffect.run(context)
holds = context.holds // Reference so future things can check on this action effect's holds
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.mulch.Mulchable
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler.pos
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
@@ -88,31 +87,22 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
init {
defaultState = this.stateManager.defaultState
.with(WAS_GENERATED, false)
.with(MULCH, MulchVariant.NONE)
.with(AGE, 0)
.with(IS_ROOTED, false)
.with(MULCH, MulchVariant.NONE)
}
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
growHelper(world, random, pos, state, true)
}
//grow, but cooler
fun growHelper(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState, boneMealed: Boolean = false) {
val berry = berry() ?: return
if (boneMealed && random.nextFloat() > berry.boneMealChance) return
val curAge = state.get(AGE)
val newAge = curAge + 1
if (newAge > FRUIT_AGE) return
val newState = state.with(AGE, newAge)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (curAge == MATURE_AGE) {
treeEntity.generateGrowthPoints(world, newState, pos, null)
determineMutation(world, random, pos, newState)
treeEntity.generateGrowthPoints(world, state, pos, null)
determineMutation(world, random, pos, state)
}
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
convertMulchToEntity(world, newState, pos)
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
treeEntity.markDirty()
}
@@ -132,7 +122,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
CobblemonEvents.BERRY_MUTATION_OFFER.post(BerryMutationOfferEvent(berry, world, state, pos, mutations)) { berryMutationOffer ->
if (berryMutationOffer.mutations.isNotEmpty()) {
var mutateChance = 125
if (getMulch(treeEntity) == MulchVariant.SURPRISE) {
if (getMulch(state) == MulchVariant.SURPRISE) {
mutateChance *= 4
treeEntity.decrementMulchDuration(world, pos, state)
}
@@ -153,10 +143,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
state: BlockState,
variant: MulchVariant
): Boolean {
val underBlockState = world.getBlockState(pos.down())
val validSoil = state.get(IS_ROOTED) || underBlockState.isIn(CobblemonBlockTags.BERRY_SOIL)
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return false
return getMulch(treeEntity) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && validSoil
return getMulch(state) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && world.getBlockState(pos.down()).isOf(Blocks.FARMLAND)
}
override fun applyMulch(
@@ -167,15 +154,18 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
variant: MulchVariant
) {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
treeEntity.setMulch(variant, world, state, pos)
setMulch(world, pos, state, variant)
treeEntity.mulchDuration = variant.duration
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS, 0.6F, 1F)
treeEntity.refreshTimers(pos)
}
@Deprecated("Deprecated in Java")
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (player.getStackInHand(hand).item is ShovelItem && getMulch(treeEntity) != MulchVariant.NONE) {
treeEntity.setMulch(MulchVariant.NONE, world, state, pos)
if (player.getStackInHand(hand).item is ShovelItem && getMulch(state) != MulchVariant.NONE) {
setMulch(world, pos, state, MulchVariant.NONE)
treeEntity.markDirty()
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS, 0.6F, 1F)
this.spawnBreakParticles(world, player, pos, state.with(AGE, 0))
return ActionResult.SUCCESS
@@ -199,7 +189,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
val below = world.getBlockState(pos.down())
return (state.get(WAS_GENERATED) && below.isIn(CobblemonBlockTags.BERRY_WILD_SOIL))
|| below.isIn(CobblemonBlockTags.BERRY_SOIL)
|| state.get(IS_ROOTED)
|| below.block is FarmlandBlock
}
@Deprecated("Deprecated in Java")
@@ -218,7 +208,6 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
builder.add(AGE)
builder.add(WAS_GENERATED)
builder.add(MULCH)
builder.add(IS_ROOTED)
}
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
@@ -250,7 +239,6 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
val AGE: IntProperty = IntProperty.of("age", 0, FRUIT_AGE)
val MULCH: EnumProperty<MulchVariant> = EnumProperty.of("mulch", MulchVariant::class.java)
val WAS_GENERATED: BooleanProperty = BooleanProperty.of("generated")
val IS_ROOTED: BooleanProperty = BooleanProperty.of("rooted")
// val PLANTED_SHAPE = VoxelShapes.union(
// VoxelShapes.cuboid(0.3125, -0.0625, 0.3125, 0.6875, 0.0, 0.6875),
// VoxelShapes.cuboid(0.375, 0.0, 0.375, 0.625, 0.0625, 0.625)
@@ -313,14 +301,15 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
val TALL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
fun getMulch(entity: BerryBlockEntity) = entity.mulchVariant
fun convertMulchToEntity(world: ServerWorld, state: BlockState, pos: BlockPos) {
val entity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
if (state.get(MULCH) != MulchVariant.NONE && state.get(MULCH) != entity.mulchVariant) {
entity.mulchVariant = state.get(MULCH)
world.setBlockState(pos, state.with(MULCH, MulchVariant.NONE))
fun getMulch(state: BlockState): MulchVariant {
if (!state.contains(MULCH)) {
return MulchVariant.NONE
}
return state.get(MULCH)
}
fun setMulch(world: World, pos: BlockPos, state: BlockState, mulch: MulchVariant) {
world.setBlockState(pos, state.with(MULCH, mulch))
}
}
}
@@ -1,49 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.item.berry.BerryItem
import net.minecraft.block.AbstractBlock
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* @author Apion
* @since February 27, 2024
*/
class BindingSoilBlock(settings: Settings) : Block(settings) {
@Deprecated("Deprecated in Java")
override fun onUse(
state: BlockState,
world: World,
pos: BlockPos,
player: PlayerEntity,
hand: Hand,
hit: BlockHitResult
): ActionResult {
if (!world.isClient) {
val itemStack = player.getStackInHand(hand)
val item = itemStack.item
if (item is BerryItem) {
world.setBlockState(pos, item.block.defaultState.with(BerryBlock.IS_ROOTED, true))
if (!player.isCreative) {
itemStack.decrement(1)
}
}
}
return ActionResult.SUCCESS
}
}
@@ -19,7 +19,6 @@ import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.SidedInventory
import net.minecraft.inventory.SimpleInventory
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.server.world.ServerWorld
@@ -39,7 +38,6 @@ import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldEvents
import net.minecraft.world.WorldView
@@ -74,12 +72,10 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
super.onBreak(world, pos, state, player)
if (!world.isClient) {
val otherPart = world.getBlockState(getPositionOfOtherPart(state, pos))
if (otherPart.block is RestorationTankBlock) {
world.setBlockState(getPositionOfOtherPart(state, pos), Blocks.AIR.defaultState, Block.NOTIFY_ALL)
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, getPositionOfOtherPart(state, pos), getRawIdFromState(otherPart))
}
val otherPart = world.getBlockState(getPositionOfOtherPart(state, pos))
if (otherPart.block is RestorationTankBlock) {
world.setBlockState(getPositionOfOtherPart(state, pos), Blocks.AIR.defaultState, 35)
world.syncWorldEvent(player, 2001, getPositionOfOtherPart(state, pos), getRawIdFromState(otherPart))
}
}
@@ -91,9 +87,9 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
itemStack: ItemStack?
) {
//Place the full block before we call to super to validate the multiblock
world.setBlockState(pos.up(), state.with(PART, TankPart.TOP) as BlockState, Block.NOTIFY_ALL)
world.setBlockState(pos.up(), state.with(PART, TankPart.TOP) as BlockState, 3)
world.updateNeighbors(pos, Blocks.AIR)
state.updateNeighbors(world, pos, Block.NOTIFY_ALL)
state.updateNeighbors(world, pos, 3)
super.onPlaced(world, pos, state, placer, itemStack)
}
@@ -104,7 +100,7 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
val tankBottomPos = pos.down()
val tankBottomState = world.getBlockState(tankBottomPos)
if(tankBottomState.block.equals(CobblemonBlocks.RESTORATION_TANK.asBlock()) && tankBottomState.get(PART) == TankPart.BOTTOM) {
return onUse(tankBottomState, world, tankBottomPos, player, hand, hit)
return super.onUse(tankBottomState, world, tankBottomPos, player, hand, hit)
}
}
return super.onUse(state, world, pos, player, hand, hit)
@@ -210,8 +206,9 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
): SidedInventory {
val tankEntity =
(if (state.get(PART) == TankPart.TOP) world.getBlockEntity(pos.down())
else world.getBlockEntity(pos))
return if(tankEntity != null && tankEntity is RestorationTankBlockEntity) tankEntity.inv else DummyInventory()
else world.getBlockEntity(pos)) as RestorationTankBlockEntity
return tankEntity.inv
}
enum class TankPart(private val label: String) : StringIdentifiable {
@@ -229,18 +226,5 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
val PART = EnumProperty.of("part", TankPart::class.java)
val TRIGGERED = Properties.TRIGGERED
val ON = BooleanProperty.of("on")
class DummyInventory : SimpleInventory(0), SidedInventory {
override fun getAvailableSlots(side: Direction): IntArray {
return IntArray(0)
}
override fun canInsert(slot: Int, stack: ItemStack, dir: Direction?): Boolean {
return false
}
override fun canExtract(slot: Int, stack: ItemStack, dir: Direction): Boolean {
return false
}
}
}
}
@@ -27,7 +27,6 @@ import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
@@ -38,7 +37,6 @@ import net.minecraft.util.*
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.math.random.Random
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
@@ -127,12 +125,11 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
fun isFake() = (type == Type.FAKE)
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
if (!world.isClient) {
if (isFake() && (player is ServerPlayerEntity)) {
if (isFake()) {
if (player is ServerPlayerEntity) {
spawnPokemon(world, pos, state, player)
}
val bEntity = world.getBlockEntity(pos) as? GildedChestBlockEntity
bEntity?.markRemoved()
world.setBlockState(pos, if (state.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState)
} else super.onBreak(world, pos, state, player)
}
@@ -186,6 +183,10 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
if (!player.world.isClient) {
PiglinBrain.onGuardedBlockInteracted(player, true)
}
val state = entity.poseableState
state.currentModel?.let {
it.moveToPose(null, state, it.getPose("OPEN")!!)
}
return ActionResult.SUCCESS
}
@@ -196,11 +197,9 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
newState: BlockState,
moved: Boolean
) {
if (!state.isOf(newState.block) && !world.isClient) {
val chest = world.getBlockEntity(pos) as? GildedChestBlockEntity
chest?.let {
ItemScatterer.spawn(world, pos, chest.inventoryContents)
}
if (!state.isOf(newState.block)) {
val chest = world.getBlockEntity(pos) as GildedChestBlockEntity
ItemScatterer.spawn(world, pos, chest.inventoryContents)
}
}
@@ -262,14 +261,4 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
}
}
override fun scheduledTick(
state: BlockState?,
world: ServerWorld?,
pos: BlockPos?,
random: Random?
) {
val blockEntity = world?.getBlockEntity(pos) as? GildedChestBlockEntity ?: return
blockEntity.onScheduledTick()
}
}
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.BerryBlock.Companion.getMulch
import com.cobblemon.mod.common.block.BerryBlock.Companion.setMulch
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
@@ -42,8 +43,8 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
lateinit var berryIdentifier: Identifier
private val ticksPerMinute = 1200
var renderState: RenderState? = null
//The time left for the tree until its either age 3 or age 5 (check block state, if we are at age 0<x<3 unti 3 otherwise until 5)
var growthTimer: Int = 0
//The time left for the tree to grow to stage 5
var growthTimer: Int = 72000
set(value) {
if (value < 0) {
throw IllegalArgumentException("You cannot set the growth time to less than zero")
@@ -54,7 +55,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
field = value
}
//The time left for the tree to grow to the next stage
var stageTimer: Int = 0
var stageTimer: Int = growthTimer / BerryBlock.MATURE_AGE
set(value) {
if (field != value) {
this.markDirty()
@@ -62,7 +63,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
field = value
}
private val growthPoints = arrayListOf<Identifier>()
var mulchVariant = MulchVariant.NONE
/**
* The idea behind the growth point sequence is it's a 16-long string of
@@ -85,6 +85,20 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
markDirty()
}
fun decrementMulchDuration(world: World, pos: BlockPos, state: BlockState) {
val currentMulch = getMulch(state)
if (currentMulch == MulchVariant.NONE || currentMulch.duration == -1) {
return
}
val newDuration = mulchDuration - 1
mulchDuration = if (newDuration <= 0) {
setMulch(world, pos, state, MulchVariant.NONE)
0
} else {
newDuration
}
}
constructor(pos: BlockPos, state: BlockState, berryIdentifier: Identifier): this(pos, state) {
this.berryIdentifier = berryIdentifier
resetGrowTimers(pos, state)
@@ -93,31 +107,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
}
}
fun decrementMulchDuration(world: World, pos: BlockPos, state: BlockState) {
if (mulchVariant == MulchVariant.NONE || mulchVariant.duration == -1) {
return
}
val newDuration = mulchDuration - 1
mulchDuration = if (newDuration <= 0) {
mulchVariant = MulchVariant.NONE
this.markDirty()
world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS)
0
} else {
newDuration
}
}
fun setMulch(mulch: MulchVariant, world: World, state: BlockState, pos: BlockPos) {
this.mulchVariant = mulch
this.mulchDuration = mulch.duration
refreshTimers(pos)
world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS)
this.markDirty()
}
//This function basically runs twice when the berry tree is growing, once when it starts, once when it hits age 3
//Why dont we just go 0-5? Design thinks its annoying to balance the numbers using 0-5 and 3-5
fun resetGrowTimers(pos: BlockPos, state: BlockState) {
val curAge = state.get(BerryBlock.AGE)
if (curAge == 5) {
@@ -130,15 +119,12 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
val upperGrowthLimit = (if (curAge == 0) berry.growthTime.last else berry.refreshRate.last) * multiplier / 10
val growthRange = lowerGrowthLimit..upperGrowthLimit
this.growthTimer = this.applyMulchModifier(pos, growthRange.random() * ticksPerMinute, true)
val stagesLeft = if (curAge < 3) BerryBlock.MATURE_AGE - curAge else BerryBlock.FRUIT_AGE - curAge
this.goToNextStageTimer(stagesLeft)
this.growthTimer = this.applyMulchModifier(pos, growthRange.random() * ticksPerMinute)
this.goToNextStageTimer(BerryBlock.FRUIT_AGE - curAge)
}
fun goToNextStageTimer(stagesLeft: Int) {
val avgStageTime = growthTimer / stagesLeft
//A number between 80% and 100% the average stage time
//So stages have some variance
stageTimer = this.world?.random?.nextBetween((avgStageTime * 8) / 10, avgStageTime) ?:
(((Math.random() * 0.2) + 0.8) * avgStageTime).toInt()
growthTimer -= stageTimer
@@ -151,7 +137,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
* @param timer Timer to be modified
* @return The modified timer
*/
private fun applyMulchModifier(pos: BlockPos, timer: Int, decrementMulch: Boolean = false): Int {
private fun applyMulchModifier(pos: BlockPos, timer: Int): Int {
val state = world?.getBlockState(pos) ?: return timer
val curAge = state.get(BerryBlock.AGE)
@@ -159,15 +145,11 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
return timer
}
if (mulchVariant != MulchVariant.GROWTH) {
if (getMulch(state) != MulchVariant.GROWTH) {
return timer
}
if (decrementMulch) {
world?.let {
decrementMulchDuration(it, pos, state)
}
}
return (timer * MulchVariant.GROWTH_TIME_MULTIPLIER).toInt()
return (timer * 0.66).toInt()
}
/**
@@ -175,8 +157,8 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
* @param pos The position of the block
*/
fun refreshTimers(pos: BlockPos) {
this.growthTimer = this.applyMulchModifier(pos, growthTimer, false)
this.stageTimer = this.applyMulchModifier(pos, stageTimer, false)
this.growthTimer = this.applyMulchModifier(pos, growthTimer)
this.stageTimer = this.applyMulchModifier(pos, stageTimer)
}
/**
@@ -266,9 +248,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
if (nbt.contains(GROWTH_POINTS_SEQUENCE)) {
growthPointSequence = nbt.getString(GROWTH_POINTS_SEQUENCE)
}
if (nbt.contains(MULCH_VARIANT)) {
mulchVariant = MulchVariant.valueOf(nbt.getString(MULCH_VARIANT))
}
this.renderState?.needsRebuild = true
}
@@ -281,7 +260,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
nbt.putString(BERRY, berryIdentifier.toString())
nbt.putInt(MULCH_DURATION, mulchDuration)
nbt.putString(GROWTH_POINTS_SEQUENCE, growthPointSequence)
nbt.putString(MULCH_VARIANT, mulchVariant.toString())
}
override fun markDirty() {
@@ -345,12 +323,11 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
companion object {
internal val TICKER = BlockEntityTicker<BerryBlockEntity> { world, pos, state, blockEntity ->
if (world.isClient) return@BlockEntityTicker
if (state.get(BerryBlock.IS_ROOTED)) return@BlockEntityTicker
if (blockEntity.stageTimer >= 0) {
blockEntity.stageTimer--
}
if (blockEntity.stageTimer == 0) {
(state.block as BerryBlock).growHelper(world as ServerWorld, world.random, pos, state)
(state.block as BerryBlock).grow(world as ServerWorld, world.random, pos, state)
}
}
//private const val LIFE_CYCLES = "life_cycles"
@@ -360,7 +337,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
private const val STAGE_TIMER = "StageTimer"
private const val BERRY = "Berry"
private const val MULCH_DURATION = "MulchDuration"
private const val MULCH_VARIANT = "MulchVariant"
}
}
@@ -56,12 +56,23 @@ class FossilAnalyzerBlockEntity(
return ItemStack.EMPTY
}
//Unimplemented since no extract
override fun removeStack(slot: Int, amount: Int): ItemStack {
return removeStack(slot)
if (analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory[slot]
}
}
return ItemStack.EMPTY
}
override fun removeStack(slot: Int): ItemStack {
if(analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
if (fossilMultiblockStructure.fossilInventory.size > slot) {
return fossilMultiblockStructure.fossilInventory.removeAt(slot)
}
}
return ItemStack.EMPTY
}
@@ -88,7 +99,7 @@ class FossilAnalyzerBlockEntity(
if(analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
return intArrayOf(0,1,2)
}
return IntArray(0)
return IntArray(1)
}
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
@@ -105,5 +116,6 @@ class FossilAnalyzerBlockEntity(
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
return false
}
}
}
@@ -38,14 +38,18 @@ open class FossilMultiblockEntity(
}
}
get() {
if(masterBlockPos != null && masterBlockPos != pos) {
return if (field != null) {
field
} else if (masterBlockPos != null && masterBlockPos != pos) {
val chunkPos = ChunkPos(masterBlockPos)
if (world?.chunkManager?.isChunkLoaded(chunkPos.x, chunkPos.z) == true) {
val entity: FossilMultiblockEntity? = world?.getBlockEntity(masterBlockPos) as FossilMultiblockEntity?
field = entity?.multiblockStructure
}
field
} else {
null
}
return field
}
override fun markRemoved() {
@@ -20,7 +20,6 @@ import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.DoubleInventory
import net.minecraft.inventory.Inventories
import net.minecraft.inventory.Inventory
import net.minecraft.inventory.SidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
@@ -59,7 +58,9 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
override fun isPlayerViewing(player: PlayerEntity): Boolean {
if (player.currentScreenHandler is GenericContainerScreenHandler) {
val inventory = (player.currentScreenHandler as GenericContainerScreenHandler).inventory
return inventory === this@GildedChestBlockEntity
return inventory === this@GildedChestBlockEntity || inventory is DoubleInventory && inventory.isPart(
this@GildedChestBlockEntity
)
}
return false
}
@@ -98,6 +99,8 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
return dir == Direction.DOWN
}
override fun canPlayerUse(player: PlayerEntity) = !player.isSpectator
override fun getInvStackList(): DefaultedList<ItemStack> = inventoryContents
override fun setInvStackList(list: DefaultedList<ItemStack>) {
@@ -161,10 +164,4 @@ class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type =
Inventories.readNbt(nbt, inventoryContents)
}
}
fun onScheduledTick() {
if (!this.removed) {
stateManager.updateViewerCount(this.getWorld(), this.getPos(), this.cachedState)
}
}
}
@@ -12,11 +12,13 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.Inventories
import net.minecraft.inventory.SidedInventory
import net.minecraft.inventory.SimpleInventory
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtList
@@ -53,9 +55,7 @@ class RestorationTankBlockEntity(
constructor(tankEntity: RestorationTankBlockEntity) : this(tankEntity, *Array(8) { ItemStack.EMPTY })
override fun getMaxCountPerStack(): Int {
return 1
}
override fun markDirty() {
super.markDirty()
@@ -64,6 +64,7 @@ class RestorationTankBlockEntity(
if(!itemStack.isEmpty) {
val struct = tankEntity.multiblockStructure as? FossilMultiblockStructure
val returnIdentifier = NaturalMaterials.getReturnItem(itemStack)
if(tankEntity.world != null) {
if(struct?.insertOrganicMaterial(itemStack, tankEntity.world!!) == true) {
removeStack(i)
@@ -71,15 +72,16 @@ class RestorationTankBlockEntity(
val returnItem = Registries.ITEM.get(returnIdentifier)
val returnStack = ItemStack(returnItem, itemStack.count)
var done = false
for(j in 1 until size()) {
if (returnStack.maxCount > 1) { // Only search for a merge if merging could be possible
for(j in 0 until size()) {
// Look for a inventory slot to merge with
val existingStack : ItemStack = getStack(j)
if (existingStack.isEmpty
|| (Registries.ITEM.getId(existingStack.item) == returnIdentifier && existingStack.count + returnStack.count < existingStack.maxCount)) {
if (Registries.ITEM.getId(existingStack.item) == returnIdentifier && existingStack.count + returnStack.count < existingStack.maxCount) {
setStack(j, ItemStack(returnItem, existingStack.count + returnStack.count))
done = true
}
}
}
if (!done) {
setStack(i, ItemStack(returnItem, itemStack.count))
}
@@ -102,7 +104,7 @@ class RestorationTankBlockEntity(
}
override fun getAvailableSlots(side: Direction?): IntArray {
return if (side == Direction.DOWN) intArrayOf(0,1,2,3,4,5,6,7) else intArrayOf(0)
return intArrayOf(0,1,2,3,4,5,6,7)
}
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
@@ -111,16 +113,16 @@ class RestorationTankBlockEntity(
val structure = tankEntity.multiblockStructure as FossilMultiblockStructure
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
&& !structure.hasCreatedPokemon
&& structure.createdPokemon == null
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
val returnStack = ItemStack(Registries.ITEM.get(returnItem), stack.count)
if(canUtilize && super.canInsert(returnStack)) {
if(returnStack == ItemStack.EMPTY && getStack(0) == ItemStack.EMPTY) {
// See if there's room for the return
if(returnStack == ItemStack.EMPTY) {
return true
}
// See if there's room for the return item
var emptyCount = 0
for(i in 1 until size()) {
for(i in 0 until size()) {
val existingStack : ItemStack = getStack(i)
if(existingStack == ItemStack.EMPTY) {
emptyCount += 1
@@ -128,8 +130,14 @@ class RestorationTankBlockEntity(
// 2 empty slots, safe to insert
return true
}
} else if(returnStack.maxCount > 1) {
if(Registries.ITEM.getId(existingStack.item) == returnItem && existingStack.count + returnStack.count < existingStack.maxCount) {
// Return item can merge with an item already in the inventory
return true
}
}
}
return false
}
}
}
@@ -10,8 +10,7 @@ package com.cobblemon.mod.common.block.multiblock
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.FossilRevivedEvent
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.fossil.Fossil
import com.cobblemon.mod.common.api.fossil.Fossils
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
@@ -73,7 +72,7 @@ class FossilMultiblockStructure (
var organicMaterialInside = 0
private set
var hasCreatedPokemon: Boolean = false
var createdPokemon: Pokemon? = null
private set
var timeRemaining = -1
private set
@@ -103,7 +102,8 @@ class FossilMultiblockStructure (
if (player !is ServerPlayerEntity) {
return ActionResult.SUCCESS
}
if (this.hasCreatedPokemon) {
if (this.createdPokemon != null) {
if (this.fossilOwnerUUID != null && player.uuid != this.fossilOwnerUUID) {
var ownerName : String = "UNKNOWN_USER" // TODO: lang agnostic fallback
server()?.userCache?.getByUuid(this.fossilOwnerUUID)?.orElse(null)?.name?.let {
@@ -114,28 +114,25 @@ class FossilMultiblockStructure (
}
val ballType = (stack.item as PokeBallItem).pokeBall
if (!player.isCreative) {
stack?.decrement(1)
}
val pokemon = this.resultingFossil?.result?.create()
if(pokemon != null) {
pokemon.caughtBall = ballType
player.party().add(pokemon)
this.fossilState.growthState = "Taken"
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, player))
}
this.createdPokemon!!.caughtBall = ballType
player.party().add(this.createdPokemon!!)
this.fossilState.growthState = "Taken"
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
CobblemonCriteria.RESURRECT_POKEMON.trigger(player, createdPokemon!!)
// Turn the monitor off
val monitorState = world.getBlockState(monitorPos)
if(monitorState.contains(MonitorBlock.SCREEN) && !monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
if(!monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, MonitorBlock.MonitorScreen.OFF))
}
this.hasCreatedPokemon = false
this.createdPokemon = null
this.fossilOwnerUUID = null
this.protectionTime = -1
this.updateFossilType(world)
@@ -147,14 +144,15 @@ class FossilMultiblockStructure (
// Reclaim the last fossil from the machine if their hand is empty
if (player.getStackInHand(interactionHand).isEmpty) {
if(!this.isRunning() && !this.hasCreatedPokemon) {
if(!this.isRunning() && this.createdPokemon == null) {
if (fossilInventory.isEmpty()) {
return ActionResult.CONSUME
}
if(player is ServerPlayerEntity) {
player.setStackInHand(interactionHand, fossilInventory.last())
// remove last fossil in the fossil machine stack when grabbed out of the machine
this.fossilInventory.removeAt(fossilInventory.size - 1)
player.setStackInHand(interactionHand, fossilInventory.last())
// remove last fossil in the fossil machine stack when grabbed out of the machine
this.fossilInventory.removeAt(fossilInventory.size - 1)
if(!world.isClient) {
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_FOSSIL, SoundCategory.BLOCKS)
this.updateFossilType(world)
this.syncToClient(world)
@@ -166,17 +164,19 @@ class FossilMultiblockStructure (
// Check if the player is holding a fossil and if so insert it into the machine.
if (Fossils.isFossilIngredient(stack)) {
if (!this.isRunning() && !this.hasCreatedPokemon) {
if(!this.isRunning() && this.createdPokemon == null) {
if (fossilInventory.size > Cobblemon.config.maxInsertedFossilItems) {
return ActionResult.FAIL
}
if (player is ServerPlayerEntity) {
val copyFossilStack = stack.copyWithCount(1)
if (!player.isCreative) {
stack?.decrement(1)
}
fossilOwnerUUID = player.uuid
fossilInventory.add(copyFossilStack)
val copyFossilStack = stack.copyWithCount(1)
if (!player.isCreative) {
stack?.decrement(1)
}
fossilOwnerUUID = player.uuid
fossilInventory.add(copyFossilStack)
if(!world.isClient) {
this.updateFossilType(world)
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
this.syncToClient(world)
@@ -188,11 +188,7 @@ class FossilMultiblockStructure (
// Check if the player is holding a natural material and if so, feed it to the machine.
if (NaturalMaterials.isNaturalMaterial(stack)) {
if (player is ServerPlayerEntity
&& !this.isRunning()
&& !this.hasCreatedPokemon
&& this.organicMaterialInside < MATERIAL_TO_START
&& insertOrganicMaterial(ItemStack(stack.item, 1), world)) {
if (!this.isRunning() && this.createdPokemon == null && this.organicMaterialInside < MATERIAL_TO_START && insertOrganicMaterial(ItemStack(stack.item, 1), world)) {
this.lastInteraction = world.time
if (!player.isCreative) {
val returnItem = NaturalMaterials.getReturnItem(stack)
@@ -224,7 +220,6 @@ class FossilMultiblockStructure (
entity.setPosition(fixedPosition.toCenterPos().subtract(0.0, 0.5, 0.0))
// TODO: Find a correct way to set the new entity's Yaw rotation. (Face away from the machine)
if (world.spawnEntity(entity)) {
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, null))
return true
} else {
Cobblemon.LOGGER.warn("Couldn't spawn resurrected Pokémon for some reason")
@@ -276,7 +271,7 @@ class FossilMultiblockStructure (
return 0
}
if(monitorPos == pos) {
if(hasCreatedPokemon) {
if(createdPokemon != null) {
return 15
}
if(!isRunning()) {
@@ -292,13 +287,13 @@ class FossilMultiblockStructure (
override fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?) {
// instantiate the pokemon as a new entity and spawn it at the location of the machine
if(this.protectionTime <= 0) {
val wildPokemon: Pokemon = if (hasCreatedPokemon) resultingFossil?.result?.create() ?: return else return
val wildPokemon: Pokemon = this.createdPokemon ?: return
val direction = state?.get(HorizontalFacingBlock.FACING)?.opposite
if(pos != null && direction != null && world != null) {
val success = this.spawn(world, pos, direction, wildPokemon)
if(success) {
this.fossilState.growthState = "Taken"
this.hasCreatedPokemon = false
this.createdPokemon = null
this.fossilOwnerUUID = null
this.protectionTime = -1
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS)
@@ -315,9 +310,9 @@ class FossilMultiblockStructure (
val analyzerEntity = world.getBlockEntity(analyzerPos) as? MultiblockEntity
val tankBaseEntity = world.getBlockEntity(tankBasePos) as? MultiblockEntity
val tankTopEntity = world.getBlockEntity(tankBasePos.up()) as? MultiblockEntity
val tankBaseBlockState = world.getBlockState(tankBaseEntity?.pos)
val direction = tankBaseBlockState.get(HorizontalFacingBlock.FACING).opposite
val wildPokemon: Pokemon? = if(hasCreatedPokemon) resultingFossil?.result?.create() else null
val state = world.getBlockState(tankBaseEntity?.pos)
val direction = state.get(HorizontalFacingBlock.FACING).getOpposite()
val wildPokemon: Pokemon? = this.createdPokemon
monitorEntity?.multiblockStructure = null
analyzerEntity?.multiblockStructure = null
@@ -377,7 +372,7 @@ class FossilMultiblockStructure (
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_UNPROTECTED, SoundCategory.BLOCKS)
}
if (this.hasCreatedPokemon) {
if (this.createdPokemon != null) {
return
}
@@ -406,7 +401,10 @@ class FossilMultiblockStructure (
if (this.timeRemaining == 0) {
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_FINISHED, SoundCategory.BLOCKS)
fossilInventory.clear()
this.hasCreatedPokemon = true
this.resultingFossil?.let {
this.createdPokemon = it.result.create()
}
if(this.fossilOwnerUUID != null) {
protectionTime = PROTECTION_TIME
}
@@ -477,26 +475,20 @@ class FossilMultiblockStructure (
val upperTankPos = tankBasePos.up()
val analyzerState = world.getBlockState(analyzerPos)
val tankState = world.getBlockState(tankBasePos.up())
if (analyzerState.contains(FossilAnalyzerBlock.ON)) {
world.setBlockState(analyzerPos, analyzerState.with(FossilAnalyzerBlock.ON, timeRemaining >= 0))
}
if (tankState.contains(RestorationTankBlock.ON)) {
world.setBlockState(upperTankPos, tankState.with(RestorationTankBlock.ON, timeRemaining >= 0))
}
world.setBlockState(analyzerPos, analyzerState.with(FossilAnalyzerBlock.ON, timeRemaining >= 0))
world.setBlockState(upperTankPos, tankState.with(RestorationTankBlock.ON, timeRemaining >= 0))
}
fun updateProgress(world: World) {
val monitorState = world.getBlockState(monitorPos)
if (monitorState.contains(MonitorBlock.SCREEN)) {
val screenID = if (protectionTime > 0F) {
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
} else if (timeRemaining <= 0) {
MonitorBlock.MonitorScreen.OFF
} else {
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
}
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
val screenID = if(protectionTime > 0F ) {
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
} else if (timeRemaining <= 0) {
MonitorBlock.MonitorScreen.OFF
} else {
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
}
val monitorState = world.getBlockState(monitorPos)
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
}
fun getProgressScreen(progress:Int) : MonitorBlock.MonitorScreen {
@@ -611,7 +603,9 @@ class FossilMultiblockStructure (
result.putString(DataKeys.INSERTED_FOSSIL, this.resultingFossil!!.asString())
}
result.putBoolean(DataKeys.HAS_CREATED_POKEMON, hasCreatedPokemon)
if (this.createdPokemon != null) {
result.put(DataKeys.CREATED_POKEMON, createdPokemon!!.saveToNBT(NbtCompound()))
}
return result
}
@@ -660,10 +654,7 @@ class FossilMultiblockStructure (
}
if (nbt.contains(DataKeys.CREATED_POKEMON)) {
// migration of instances that saved the created pokeon in the nbt
result.hasCreatedPokemon = true
} else if (nbt.contains(DataKeys.HAS_CREATED_POKEMON)){
result.hasCreatedPokemon = nbt.getBoolean(DataKeys.HAS_CREATED_POKEMON)
result.createdPokemon = Pokemon.loadFromNBT(nbt.getCompound(DataKeys.CREATED_POKEMON))
}
result.fillLevel = result.organicMaterialInside * 8 / MATERIAL_TO_START
return result
@@ -14,7 +14,6 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.client.MinecraftClient
import net.minecraft.client.sound.PositionedSoundInstance
@@ -32,13 +31,6 @@ object ClientMoLangFunctions {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, pitch))
}
},
"is_time" to java.util.function.Function { params ->
val time = (MinecraftClient.getInstance().world?.timeOfDay ?: 0) % 24000
val min = params.getInt(0)
val max = params.getInt(1)
time in min..max
},
"say" to java.util.function.Function { params -> MinecraftClient.getInstance().player?.sendMessage(params.getString(0).text()) ?: Unit },
)
fun MoLangRuntime.setupClient(): MoLangRuntime {
@@ -62,46 +62,6 @@ object CobblemonBakingOverrides {
cobblemonModel("restoration_tank_connector", "none")
)
val COARSE_MULCH = registerOverride(
cobblemonResource("block/coarse_mulch"),
cobblemonModel("coarse_mulch", "none")
)
val GROWTH_MULCH = registerOverride(
cobblemonResource("block/growth_mulch"),
cobblemonModel("growth_mulch", "none")
)
val HUMID_MULCH = registerOverride(
cobblemonResource("block/humid_mulch"),
cobblemonModel("humid_mulch", "none")
)
val LOAMY_MULCH = registerOverride(
cobblemonResource("block/loamy_mulch"),
cobblemonModel("loamy_mulch", "none")
)
val PEAT_MULCH = registerOverride(
cobblemonResource("block/peat_mulch"),
cobblemonModel("peat_mulch", "none")
)
val RICH_MULCH = registerOverride(
cobblemonResource("block/rich_mulch"),
cobblemonModel("rich_mulch", "none")
)
val SANDY_MULCH = registerOverride(
cobblemonResource("block/sandy_mulch"),
cobblemonModel("sandy_mulch", "none")
)
val SURPRISE_MULCH = registerOverride(
cobblemonResource("block/surprise_mulch"),
cobblemonModel("surprise_mulch", "none")
)
fun registerOverride(modelLocation: Identifier, modelIdentifier: ModelIdentifier): BakingOverride {
val result = BakingOverride(modelLocation, modelIdentifier)
models.add(result)
@@ -272,9 +272,6 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
},
"weight" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokemon.species.weight.toDouble())
},
"friendship" to java.util.function.Function {
return@Function DoubleValue(currentEntity.pokemon.friendship.toDouble())
}
))
}
@@ -15,6 +15,9 @@ import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.math.RotationAxis.NEGATIVE_X
import net.minecraft.util.math.RotationAxis.POSITIVE_X
import net.minecraft.util.math.RotationAxis.POSITIVE_Y
object SpawnSnowstormParticleHandler : ClientNetworkPacketHandler<SpawnSnowstormParticlePacket> {
override fun handle(packet: SpawnSnowstormParticlePacket, client: MinecraftClient) {
@@ -10,20 +10,16 @@ package com.cobblemon.mod.common.client.particle
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies.species
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleEffect
import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterAction
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.particle.SnowstormParticleEffect
import com.cobblemon.mod.common.util.math.geometry.transformDirection
import kotlin.random.Random
import net.minecraft.client.MinecraftClient
import net.minecraft.client.particle.NoRenderParticle
import net.minecraft.client.util.ParticleUtil.spawnParticle
import net.minecraft.client.world.ClientWorld
import net.minecraft.entity.Entity
import net.minecraft.util.math.Vec3d
@@ -52,22 +48,12 @@ class ParticleStorm(
.addFunction("entity_height") { DoubleValue(entity.boundingBox.yLength) }
.addFunction("entity_size") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength }) }
.addFunction("entity_radius") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength } / 2) }
.addFunction("entity_scale") {
val pokeEntity = entity as? PokemonEntity
val pokemon = pokeEntity?.pokemon
//Use form data if available, species as fall back
val baseScale = pokemon?.form?.baseScale ?: pokemon?.species?.baseScale ?: 1.0F
val pokemonScale = pokemon?.scaleModifier ?: 1.0F
val entityScale = pokeEntity?.scaleFactor ?: 1.0F
DoubleValue(baseScale * pokemonScale * entityScale)
}
// TODO replace with a generified call to if (entity is MoLangEntity) entity.applyVariables(env) or w/e
runtime.environment.setSimpleVariable("entity_width", DoubleValue(entity.boundingBox.xLength))
runtime.environment.setSimpleVariable("entity_height", DoubleValue(entity.boundingBox.yLength))
val longerDiameter = entity.boundingBox.run { if (xLength > yLength) xLength else yLength }
runtime.environment.setSimpleVariable("entity_size", DoubleValue(longerDiameter))
runtime.environment.setSimpleVariable("entity_radius", DoubleValue(longerDiameter / 2))
runtime.environment.setSimpleVariable("entity_scale", DoubleValue((entity as? PokemonEntity)?.scaleFactor ?: 1.0))
}
MinecraftClient.getInstance().particleManager.addParticle(this)
}
@@ -118,7 +118,7 @@ class SnowstormParticle(
init {
setVelocity(initialVelocity.x, initialVelocity.y, initialVelocity.z)
angle = -storm.effect.particle.rotation.getInitialRotation(storm.runtime).toFloat()
angle = storm.effect.particle.rotation.getInitialRotation(storm.runtime).toFloat()
prevAngle = angle
angularVelocity = storm.effect.particle.rotation.getInitialAngularVelocity(storm.runtime)
velocityMultiplier = 1F
@@ -256,20 +256,24 @@ class SnowstormParticle(
setParticleAgeInRuntime()
storm.effect.curves.forEach { it.apply(storm.runtime) }
storm.runtime.execute(storm.effect.particle.updateExpressions)
angularVelocity = storm.effect.particle.rotation.getAngularVelocity(storm.runtime, -angle.toDouble(), angularVelocity) / 20
angularVelocity += storm.effect.particle.rotation.getAngularAcceleration(storm.runtime, angularVelocity) / 20
if (age >= maxAge || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
if (age > maxAge || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
runExpirationEvents()
markDead()
return
} else {
val velocity = storm.effect.particle.motion.getVelocity(storm.runtime, this, Vec3d(velocityX, velocityY, velocityZ))
velocityX = velocity.x
velocityY = velocity.y
velocityZ = velocity.z
val acceleration = storm.effect.particle.motion.getAcceleration(
storm.runtime,
Vec3d(velocityX, velocityY, velocityZ).multiply(20.0) // Uses blocks per second, not blocks per tick
).multiply(1 / 20.0).multiply(1 / 20.0) // blocks per second per second -> blocks per tick per tick
velocityX += acceleration.x
velocityY += acceleration.y
velocityZ += acceleration.z
prevAngle = angle
// Subtract because Bedrock particles are counter-clockwise and Java Edition is clockwise.
angle = prevAngle - angularVelocity.toFloat()
angle = prevAngle + angularVelocity.toFloat()
}
viewDirection = storm.effect.particle.viewDirection.getDirection(
@@ -16,7 +16,6 @@ class BerryBlockEntityRenderState : BerryBlockEntity.RenderState {
override var needsRebuild = true
val vbo: VertexBuffer = VertexBuffer(VertexBuffer.Usage.STATIC)
var vboLightLevel: Int = 0
var drawVbo: Boolean = false
override fun close() {
vbo.close()
@@ -23,7 +23,6 @@ import net.minecraft.client.gl.ShaderProgram
import net.minecraft.client.gl.VertexBuffer
import net.minecraft.client.render.GameRenderer
import net.minecraft.client.render.LightmapTextureManager
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.render.Tessellator
import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.VertexFormat
@@ -34,7 +33,6 @@ import net.minecraft.client.util.math.MatrixStack
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import org.joml.Matrix4f
import org.joml.Vector3f
class BerryBlockRenderer(private val context: BlockEntityRendererFactory.Context) : BlockEntityRenderer<BerryBlockEntity> {
@@ -47,88 +45,33 @@ class BerryBlockRenderer(private val context: BlockEntityRendererFactory.Context
override fun render(entity: BerryBlockEntity, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int) {
if (!isInRenderDistance(entity, entity.pos.toVec3d())) return
val blockState = entity.cachedState
val age = blockState.get(BerryBlock.AGE)
if (age <= BerryBlock.MATURE_AGE) {
return
}
if (entity.renderState == null) {
entity.renderState = BerryBlockEntityRenderState()
}
val renderState = entity.renderState as BerryBlockEntityRenderState
if (renderState.needsRebuild || renderState.vboLightLevel != light) {
renderToBuffer(entity, light, overlay, renderState)
renderToBuffer(entity, light, overlay, renderState.vbo)
renderState.vboLightLevel = light
(entity.renderState as BerryBlockEntityRenderState).needsRebuild = false
}
if (renderState.drawVbo) {
matrices.push()
CobblemonRenderLayers.BERRY_LAYER.startDrawing()
renderState.vbo.bind()
renderState.vbo.draw(
matrices.peek().positionMatrix.mul(RenderSystem.getModelViewMatrix()),
RenderSystem.getProjectionMatrix(),
GameRenderer.getRenderTypeCutoutProgram()
)
VertexBuffer.unbind()
CobblemonRenderLayers.BERRY_LAYER.endDrawing()
matrices.pop()
}
drawMulch(matrices, vertexConsumers, entity, light, overlay)
}
private fun drawMulch(
matrices: MatrixStack,
vertexConsumers: VertexConsumerProvider,
entity: BerryBlockEntity,
light: Int,
overlay: Int
) {
matrices.push()
//Mulch is rendered on a different layer than the actual berries so
val mulchBuf = vertexConsumers.getBuffer(RenderLayer.getCutout())
val model = entity.mulchVariant.model
model?.let {
it.getModel().getQuads(entity.cachedState, null, entity.world?.random).forEach { quad ->
mulchBuf.quad(matrices.peek(), quad, 1F, 1F, 1F, light, overlay)
}
}
CobblemonRenderLayers.BERRY_LAYER.startDrawing()
renderState.vbo.bind()
renderState.vbo.draw(
matrices.peek().positionMatrix.mul(RenderSystem.getModelViewMatrix()),
RenderSystem.getProjectionMatrix(),
GameRenderer.getRenderTypeCutoutProgram()
)
VertexBuffer.unbind()
CobblemonRenderLayers.BERRY_LAYER.endDrawing()
matrices.pop()
}
fun renderToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, renderState: BerryBlockEntityRenderState) {
if (entity.cachedState.get(BerryBlock.AGE) == 0) {
renderBabyToBuffer(entity, light, overlay, renderState.vbo)
renderState.drawVbo = true
}
else if (entity.cachedState.get(BerryBlock.AGE) > BerryBlock.MATURE_AGE){
renderAdultToBuffer(entity, light, overlay, renderState.vbo)
renderState.drawVbo = true
}
else {
renderState.drawVbo = false
}
}
fun renderBabyToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
val bufferBuilder = Tessellator.getInstance().buffer
bufferBuilder.begin(CobblemonRenderLayers.BERRY_LAYER.drawMode, CobblemonRenderLayers.BERRY_LAYER.vertexFormat)
val berry = entity.berry() ?: return
val model = BerryModelRepository.modelOf(berry.fruitModelIdentifier) ?: return
val pos = berry.stageOnePositioning.position
model.setPosition(Axis.X_AXIS.ordinal, pos.x.toFloat())
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
model.setPosition(Axis.Z_AXIS.ordinal, pos.z.toFloat())
val rot = berry.stageOnePositioning.rotation
model.setAngles(
Math.toRadians(180 - rot.x).toFloat(),
Math.toRadians(180 + rot.y).toFloat(),
Math.toRadians(rot.z).toFloat()
)
model.render(MatrixStack(), bufferBuilder, light, overlay)
val bufferBuilderFinal = bufferBuilder.end()
buffer.bind()
buffer.upload(bufferBuilderFinal)
VertexBuffer.unbind()
}
fun renderAdultToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
fun renderToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
val blockState = entity.cachedState
val age = blockState.get(BerryBlock.AGE)
if (age <= BerryBlock.MATURE_AGE) {
@@ -61,17 +61,17 @@ class RestorationTankRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEn
matrices.pop()
}
val fillLevel = struct.fillLevel
if (fillLevel == 0 && !struct.hasCreatedPokemon) {
if (fillLevel == 0 && struct.createdPokemon == null) {
return
}
if (struct.isRunning() or (struct.hasCreatedPokemon)) renderFetus(entity, tickDelta, matrices, vertexConsumers, light, overlay)
if (struct.isRunning() or (struct.createdPokemon != null)) renderFetus(entity, tickDelta, matrices, vertexConsumers, light, overlay)
matrices.push()
val transparentBuffer = vertexConsumers.getBuffer(RenderLayer.getTranslucent())
val fluidModel = if (struct.isRunning()) FLUID_MODELS[8]
else if (struct.hasCreatedPokemon) FLUID_MODELS[7]
else if (struct.createdPokemon != null) FLUID_MODELS[7]
else FLUID_MODELS[fillLevel.coerceAtMost(FLUID_MODELS.size - 1) - 1]
fluidModel.getQuads(entity.cachedState, null, entity.world?.random).forEach { quad ->
transparentBuffer?.quad(matrices.peek(), quad, 0.75f, 0.75f, 0.75f, light, OverlayTexture.DEFAULT_UV)
@@ -20,10 +20,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFram
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.SimpleQuirk
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.normalizeToArray
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveObject
import com.cobblemon.mod.common.util.singularToPluralList
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
@@ -37,8 +34,6 @@ class JsonPose<T : Entity>(model: PoseableEntityModel<T>, json: JsonObject) {
it.environment.getQueryStruct().addFunctions(model.functions.functions)
}
val condition: ExpressionLike = json.singularToPluralList("condition").get("conditions")?.normalizeToArray()?.map { it.asString }?.asExpressionLike() ?: "true".asExpressionLike()
val poseName = json.get("poseName")?.asString ?: "pose"
val poseTypes = (json.get("poseTypes")?.asJsonArray?.map { name ->
PoseType.values().find { it.name.lowercase() == name.asString.lowercase() }
@@ -86,9 +81,8 @@ class JsonPose<T : Entity>(model: PoseableEntityModel<T>, json: JsonObject) {
}
json as JsonObject
json.singularToPluralList("animation")
val animations: (state: PoseableEntityState<T>) -> List<StatefulAnimation<T, *>> = { _ ->
(json.get("animations")?.normalizeToArray()?.asJsonArray ?: JsonArray()).map { animJson ->
(json.get("animations")?.asJsonArray ?: JsonArray()).map { animJson ->
try {
val expr = animJson.asString.asExpressionLike()
runtime.resolveObject(expr).obj as StatefulAnimation<T, *>
@@ -103,11 +97,10 @@ class JsonPose<T : Entity>(model: PoseableEntityModel<T>, json: JsonObject) {
val loopTimes = json.get("loopTimes")?.asInt ?: 1
val minSeconds = json.get("minSecondsBetweenOccurrences")?.asFloat ?: 8F
val maxSeconds = json.get("maxSecondsBetweenOccurrences")?.asFloat ?: 30F
val condition = json.get("condition")?.asString?.asExpressionLike() ?: "true".asExpressionLike()
model.quirkMultiple(
secondsBetweenOccurrences = minSeconds to maxSeconds,
condition = { it.runtime.resolveBoolean(condition) },
condition = { true },
loopTimes = 1..loopTimes,
animations = animations
)
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.Entity
/**
* A storage of locator information for a model, attached to its [joint].
@@ -69,45 +68,20 @@ class LocatorAccess(
* Updates all of the locator states with the position at this current frame.
* This is the same logic as ModelPart uses, that's why we reuse ModelPart#rotate.
*/
fun update(matrixStack: MatrixStack, entity: Entity, scale: Float, state: MutableMap<String, MatrixWrapper>, isRoot: Boolean = false) {
fun update(matrixStack: MatrixStack, state: MutableMap<String, MatrixWrapper>) {
matrixStack.push()
joint.transform(matrixStack)
if (isRoot) {
matrixStack.push()
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
// Put in an approximation of the target locator. If the model has one defined,
// this will be overridden.
matrixStack.push()
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale, -entity.width * 0.6 / scale)
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut("target") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
state.getOrPut("special_attack") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
// If we have the entity, put in a "middle" locator for center of mass
// this will be overridden.
matrixStack.push()
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale, 0.0)
matrixStack.scale(-1F, -1F, 1F)
state.getOrPut("middle") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
}
for ((name, locator) in locators) {
matrixStack.push()
locator.transform(matrixStack)
matrixStack.scale(-1F, -1F, 1F)
matrixStack.scale(1F, -1F, 1F)
state.getOrPut(name) { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
}
children.forEach {
it.update(matrixStack, entity, scale, state, isRoot = false)
it.update(matrixStack, state)
}
matrixStack.pop()
@@ -46,7 +46,6 @@ import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.getDoubleOrNull
import com.cobblemon.mod.common.util.getStringOrNull
import com.cobblemon.mod.common.util.plus
import com.cobblemon.mod.common.util.resolveBoolean
import net.minecraft.client.model.ModelPart
import net.minecraft.client.render.OverlayTexture
import net.minecraft.client.render.RenderLayer
@@ -261,41 +260,6 @@ abstract class PoseableEntityModel<T : Entity>(
)
)
}
.addFunction("bedrock_primary_quirk") { params ->
val animationGroup = params.getString(0)
val animationNames = params.get<MoValue>(1)?.let { if (it is ArrayStruct) it.map.values.map { it.asString() } else listOf(it.asString()) } ?: listOf()
val minSeconds = params.getDoubleOrNull(2) ?: 8F
val maxSeconds = params.getDoubleOrNull(3) ?: 30F
val loopTimes = params.getDoubleOrNull(4)?.toInt() ?: 1
val excludedLabels = mutableSetOf<String>()
var curve: WaveFunction = { t ->
if (t < 0.1) {
t * 10
} else if (t < 0.9) {
1F
} else {
1F
}
}
for (index in 5 until params.params.size) {
val param = params.get<MoValue>(index)
if (param is ObjectValue<*>) {
curve = param.obj as WaveFunction
continue
}
val label = params.getString(index) ?: continue
excludedLabels.add(label)
}
ObjectValue(
quirk(
secondsBetweenOccurrences = minSeconds.toFloat() to maxSeconds.toFloat(),
condition = { true },
loopTimes = 1..loopTimes,
animation = { PrimaryAnimation(bedrockStateful(animationGroup, animationNames.random()), excludedLabels = excludedLabels, curve = curve) }
)
)
}
@Transient
val runtime = MoLangRuntime().setup().setupClient().also { it.environment.getQueryStruct().addFunctions(functions.functions) }
@@ -823,6 +787,7 @@ abstract class PoseableEntityModel<T : Entity>(
matrixStack.push()
matrixStack.scale(-1F, -1F, 1F)
scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
matrixStack.scale(scale, scale, scale)
} else if (entity is EmptyPokeBallEntity) {
matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(entity.yaw))
@@ -837,14 +802,27 @@ abstract class PoseableEntityModel<T : Entity>(
matrixStack.scale(1F, -1F, 1F)
}
matrixStack.push()
matrixStack.scale(1F, -1F, 1F)
val states = state.locatorStates
states.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
if (isForLivingEntityRenderer) {
// Standard living entity offset, only God knows why Mojang did this.
matrixStack.translate(0.0, -1.5, 0.0)
}
locatorAccess.update(matrixStack, entity, scale, states, isRoot = true)
// If we have the entity, put in an approximation of the target locator. If the model has one defined,
// this will be overridden.
matrixStack.push()
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale + 1.5F, -entity.width * 0.6 / scale)
matrixStack.scale(1F, -1F, 1F)
states.getOrPut("target") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
states.getOrPut("special_attack") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
matrixStack.pop()
locatorAccess.update(matrixStack, states)
}
fun ModelPart.translation(
@@ -900,18 +878,6 @@ abstract class PoseableEntityModel<T : Entity>(
animations = { listOf(animation(it)) }
)
fun quirkMoLangCondition(
secondsBetweenOccurrences: Pair<Float, Float> = 8F to 30F,
loopTimes: IntRange = 1..1,
conditionExpression: ExpressionLike = "true".asExpressionLike(),
animation: (state: PoseableEntityState<T>) -> StatefulAnimation<T, *>
) = SimpleQuirk<T>(
secondsBetweenOccurrences = secondsBetweenOccurrences,
loopTimes = loopTimes,
condition = { it.runtime.resolveBoolean(conditionExpression) },
animations = { listOf(animation(it)) }
)
fun quirkMultiple(
secondsBetweenOccurrences: Pair<Float, Float> = 8F to 30F,
loopTimes: IntRange = 1..1,
@@ -78,7 +78,7 @@ abstract class PoseableEntityState<T : Entity> : Schedulable {
"anim_time" to java.util.function.Function { return@Function reusableAnimTime.also { it.value = animationSeconds.toDouble() } },
"pose_type" to java.util.function.Function { return@Function StringValue((getEntity() as Poseable).getCurrentPoseType().name) },
"pose" to java.util.function.Function { _ -> return@Function StringValue(currentPose ?: "") },
"has_entity" to java.util.function.Function { _ -> return@Function DoubleValue(getEntity() != null) },
"say" to java.util.function.Function { params -> MinecraftClient.getInstance().player?.sendMessage(params.getString(0).text()) ?: Unit },
"sound" to java.util.function.Function { params ->
val entity = getEntity() ?: return@Function Unit
if (params.get<MoValue>(0) !is StringValue) {
@@ -135,7 +135,6 @@ abstract class PoseableEntityState<T : Entity> : Schedulable {
val storm = ParticleStorm(
effect = effect,
entity = entity,
matrixWrapper = matrixWrapper,
world = world,
runtime = particleRuntime,
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.JsonPose
import com.cobblemon.mod.common.client.render.models.blockbench.JsonPoseableEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
@@ -25,7 +24,6 @@ import com.cobblemon.mod.common.util.isDusk
import com.cobblemon.mod.common.util.isStandingOnRedSand
import com.cobblemon.mod.common.util.isStandingOnSand
import com.cobblemon.mod.common.util.isStandingOnSandOrRedSand
import com.cobblemon.mod.common.util.resolveBoolean
import com.google.gson.ExclusionStrategy
import com.google.gson.FieldAttributes
import com.google.gson.GsonBuilder
@@ -39,7 +37,6 @@ import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import java.util.function.Supplier
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
/**
@@ -50,8 +47,7 @@ import net.minecraft.util.math.Vec3d
* @author Hiroku
* @since August 7th, 2022
*/
class JsonPokemonPoseableModel(rootPart: Bone) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = (rootPart as ModelPart).children.entries.first().let { rootPart.registerChildWithAllChildren(it.key) }
class JsonPokemonPoseableModel(override val rootPart: Bone) : PokemonPoseableModel(), HeadedFrame {
companion object {
val gson = GsonBuilder()
.setPrettyPrinting()
@@ -88,12 +84,30 @@ class JsonPokemonPoseableModel(rootPart: Bone) : PokemonPoseableModel(), HeadedF
override val head: Bone by lazy { headJoint?.let { getPart(it) } ?: rootPart }
@SerializedName("portraitScale")
private val _portraitScale: Float? = null
@SerializedName("portraitTranslation")
private val _portraitTranslation: Vec3d? = null
@SerializedName("profileScale")
private val _profileScale: Float? = null
@SerializedName("profileTranslation")
private val _profileTranslation: Vec3d? = null
//Some weirdness going on here, vars have to be initialized, gson doesn't like multiple fields with the same name
//Idk, works fine with Blastoise
@Transient
override var portraitScale = 1F
get() = _portraitScale ?: 1F
@Transient
override var portraitTranslation = Vec3d.ZERO
get() = _portraitTranslation ?: Vec3d(0.0, 0.0, 0.0)
@Transient
override var profileScale = 1F
get() = _profileScale ?: 1F
@Transient
override var profileTranslation = Vec3d.ZERO
get() = _profileTranslation ?: Vec3d(0.0, 0.0, 0.0)
val faint: Supplier<StatefulAnimation<PokemonEntity, ModelFrame>>? = null
@@ -132,7 +146,6 @@ class JsonPokemonPoseableModel(rootPart: Bone) : PokemonPoseableModel(), HeadedF
override fun createInstance(type: Type): JsonPokemonPoseableModel {
return JsonPokemonPoseableModel(modelPart!!).also {
model = it
modelPart as ModelPart
it.loadAllNamedChildren(modelPart!!)
}
}
@@ -179,8 +192,6 @@ class JsonPokemonPoseableModel(rootPart: Bone) : PokemonPoseableModel(), HeadedF
conditionsList.add { mustBeDusk == it.isDusk() }
}
conditionsList.add { (it.delegate as PokemonClientDelegate).runtime.resolveBoolean(pose.condition) }
val poseCondition: ((PokemonEntity) -> Boolean)? = if (conditionsList.isEmpty()) null else conditionsList.reduce { acc, function -> { acc(it) && function(it) } }
return Pose(
@@ -8,14 +8,13 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
@@ -57,7 +56,7 @@ class KabutopsModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bipe
standing = registerPose(
poseName = "standing",
poseTypes = UI_POSES + STATIONARY_POSES,
poseTypes = UI_POSES + PoseType.STAND,
condition = { !it.isBattling },
quirks = arrayOf(blink),
idleAnimations = arrayOf(
@@ -68,7 +67,7 @@ class KabutopsModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Bipe
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
poseType = PoseType.WALK,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
@@ -8,14 +8,21 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.parabolaFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.SHOULDER_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
@@ -59,7 +66,7 @@ class PidgeyModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
stand = registerPose(
poseName = "standing",
poseTypes = SHOULDER_POSES + UI_POSES + PoseType.STATIONARY_POSES - PoseType.HOVER,
poseTypes = SHOULDER_POSES + UI_POSES + PoseType.STANDING_POSES,
transformTicks = 10,
transformedParts = arrayOf(
wingClosedLeft.createTransformation().withVisibility(visibility = true),
@@ -1,62 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.ModelPart
import net.minecraft.util.math.Vec3d
class SmeargleModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame {
override val rootPart = root.registerChildWithAllChildren("smeargle")
override val head = getPart("head")
/* No arm swinging because it's holding its tail */
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
override var portraitScale = 1.29F
override var portraitTranslation = Vec3d(-0.03, 0.96, 0.0)
override var profileScale = 0.67F
override var profileTranslation = Vec3d(0.05, 0.8, 0.0)
lateinit var standing: PokemonPose
lateinit var walk: PokemonPose
//override val cryAnimation = CryProvider { _, _ -> bedrockStateful("smeargle", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("smeargle", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES + PoseType.SLEEP,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("smeargle", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
idleAnimations = arrayOf(
singleBoneLook(),
bedrock("smeargle", "ground_idle"),
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
)
)
}
}
@@ -47,6 +47,7 @@ class ShedinjaModel (root: ModelPart) : PokemonPoseableModel() {
poseName = "standing",
poseTypes = PoseType.UI_POSES + PoseType.STAND,
transformTicks = 10,
condition = { !it.isBattling },
quirks = arrayOf(blink),
idleAnimations = arrayOf(
bedrock("shedinja", "ground_idle")
@@ -20,7 +20,7 @@ import net.minecraft.util.math.Vec3d
class WailordModel(root: ModelPart) : PokemonPoseableModel() {
override val rootPart = root.registerChildWithAllChildren("wailord")
override var portraitScale = 0.45F
override var portraitTranslation = Vec3d(-0.38, 0.8, 6.69)
override var portraitTranslation = Vec3d(-0.38, 0.8, 0.0)
override var profileScale = 0.25F
override var profileTranslation = Vec3d(0.0, 1.2, -10.0)
@@ -25,8 +25,8 @@ class PetililHisuiBiasModel (root: ModelPart) : PokemonPoseableModel(), HeadedFr
val leaf_left = getPart("leaf_left_rotation")
val leaf_right = getPart("leaf_right_rotation")
override var portraitScale = 1.52F
override var portraitTranslation = Vec3d(0.1, -0.25, 0.0)
override var portraitScale = 1.75F
override var portraitTranslation = Vec3d(0.01, -0.39, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3d(0.0, 0.5, 0.0)
@@ -22,8 +22,8 @@ class PetililModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("petilil")
override val head = getPart("head")
override var portraitScale = 1.52F
override var portraitTranslation = Vec3d(0.1, -0.25, 0.0)
override var portraitScale = 2.0F
override var portraitTranslation = Vec3d(-0.3, -1.2, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3d(0.0, 0.5, 0.0)
@@ -21,7 +21,7 @@ class TyrantrumModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
override val head = getPart("head")
override var portraitTranslation = Vec3d(-0.86, 2.0, 5.0)
override var portraitTranslation = Vec3d(-0.86, 2.0, 0.0)
override var portraitScale = 0.59F
override var profileTranslation = Vec3d(0.02, 1.28, -10.0)
@@ -28,8 +28,8 @@ class DubwoolModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Quadr
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
val wool = getPart("wool_shearable")
val neckWool = getPart("neck_wool_shearable")
val wool = getPart("wool")
val neckWool = getPart("neck_wool")
override var portraitScale = 3.1F
override var portraitTranslation = Vec3d(-1.2, -0.7, 0.0)
@@ -28,7 +28,7 @@ class WoolooModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Quadru
override val foreRightLeg = getPart("leg_front_right")
override val hindLeftLeg = getPart("leg_back_left")
override val hindRightLeg = getPart("leg_back_right")
val wool = getPart("wool_shearable")
val wool = getPart("wool")
override var portraitScale = 3.1F
override var portraitTranslation = Vec3d(-0.85, -1.8, 0.0)
@@ -714,7 +714,6 @@ object PokemonModelRepository : VaryingModelRepository<PokemonEntity, PokemonPos
inbuilt("rowlet_hisui_bias", ::RowletHisuiBiasModel)
inbuilt("dartrix_hisui_bias", ::DartrixHisuiBiasModel)
inbuilt("decidueye_hisuian", ::DecidueyeHisuianModel)
inbuilt("smeargle", ::SmeargleModel)
}
override fun loadJsonPoser(json: String): (Bone) -> PokemonPoseableModel {
@@ -24,6 +24,7 @@ import com.mojang.brigadier.context.CommandContext
import net.minecraft.server.command.CommandManager
import net.minecraft.server.command.CommandManager.literal
import net.minecraft.server.command.ServerCommandSource
import net.minecraft.text.Text
/**
* Spawn Pokemon From Surrounding Pool
@@ -83,15 +84,14 @@ object SpawnPokemonFromPool {
val spawnAction = result.second.doSpawn(ctx = result.first)
spawnAction.future
.thenApply {
if (it is EntitySpawnResult) {
for (entity in it.entities) {
player.sendMessage(commandLang("spawnpokemonfrompool", entity.displayName).green())
}
spawnAction.future.thenApply {
if (it is EntitySpawnResult) {
for (entity in it.entities) {
player.sendMessage(commandLang("spawnpokemonfrompool.success", entity.displayName).green())
}
}
spawnAction.complete()
}
spawnsTriggered++
}
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker.after
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.scheduling.taskBuilder
import com.cobblemon.mod.common.api.text.green
@@ -26,13 +25,10 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.trade.ActiveTrade
import com.cobblemon.mod.common.trade.DummyTradeParticipant
import com.cobblemon.mod.common.trade.PlayerTradeParticipant
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.toPokemon
import com.google.gson.GsonBuilder
@@ -65,22 +61,6 @@ object TestCommand {
}
try {
val player = context.source.entity as ServerPlayerEntity
val evolutionEntity = GenericBedrockEntity(world = player.world)
evolutionEntity?.apply {
category = cobblemonResource("evolution")
colliderHeight = 1.5F
colliderWidth = 1.5F
scale = 1F
syncAge = true // Otherwise particle animation will be starting from zero even if you come along partway through
setPosition(player.x, player.y, player.z + 4)
}
player.world.spawnEntity(evolutionEntity)
after(seconds = 0.5F) {
player.sendPacket(PlayPoseableAnimationPacket(evolutionEntity.id, setOf("evolution:animation.evolution.evolution"), emptySet()))
}
// readBerryDataFromCSV()
// this.testClosestBattle(context)
@@ -171,6 +171,9 @@ class CobblemonConfig {
@NodeCategory(Category.Spawning)
var baseApricornTreeGenerationChance = 0.1F
@NodeCategory(Category.Pokemon)
var ninjaskCreatesShedinja = true
@NodeCategory(Category.Pokemon)
var displayEntityLevelLabel = true
@@ -20,7 +20,6 @@ import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scheduling.Schedulable
@@ -58,7 +57,6 @@ import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket
import net.minecraft.particle.ParticleTypes
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundEvents
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.EntityHitResult
import net.minecraft.util.hit.HitResult
@@ -191,7 +189,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
val owner = owner
if (!pokemonEntity.pokemon.isWild()) {
owner?.sendMessage(lang("capture.not_wild", pokemonEntity.exposedSpecies.translatedName).red())
owner?.sendMessage(lang("capture.not_wild", pokemonEntity.pokemon.species.translatedName).red())
return drop()
}
@@ -206,7 +204,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
val hitBattlePokemon = hitActor?.activePokemon?.find { it.battlePokemon?.effectedPokemon?.entity == pokemonEntity }
if (throwerActor == null) {
owner.sendMessage(lang("capture.in_battle", pokemonEntity.exposedSpecies.translatedName).red())
owner.sendMessage(lang("capture.in_battle", pokemonEntity.pokemon.species.translatedName).red())
return drop()
}
@@ -232,13 +230,13 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
"capture.attempted_capture",
throwerActor.getName(),
pokeBall.item().name,
pokemonEntity.exposedSpecies.translatedName
pokemonEntity.pokemon.species.translatedName
).yellow()
)
battle.sendUpdate(BattleCaptureStartPacket(pokeBall.name, aspects, hitBattlePokemon.getPNX()))
throwerActor.forceChoose(ForcePassActionResponse())
} else if (pokemonEntity.isBusy) {
owner?.sendMessage(lang("capture.busy", pokemonEntity.exposedSpecies.translatedName).red())
owner?.sendMessage(lang("capture.busy", pokemonEntity.pokemon.species.translatedName).red())
return drop()
} else if (owner is ServerPlayerEntity && BattleRegistry.getBattleByParticipatingPlayer(owner) != null) {
owner.sendMessage(lang("you_in_battle").red())
@@ -21,10 +21,7 @@ import com.cobblemon.mod.common.api.interaction.PokemonEntityInteraction
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.StringSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import com.cobblemon.mod.common.api.reactive.SimpleObservable
@@ -62,6 +59,7 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolut
import com.cobblemon.mod.common.pokemon.misc.GimmighoulStashHandler
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.block.Blocks
import java.util.EnumSet
import java.util.Optional
import java.util.UUID
@@ -84,7 +82,6 @@ import net.minecraft.entity.passive.PassiveEntity
import net.minecraft.entity.passive.TameableShoulderEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.item.DyeItem
import net.minecraft.item.ItemStack
import net.minecraft.item.ItemUsage
import net.minecraft.item.Items
@@ -105,11 +102,8 @@ import net.minecraft.sound.SoundEvents
import net.minecraft.text.Text
import net.minecraft.text.TextContent
import net.minecraft.util.ActionResult
import net.minecraft.util.Colors
import net.minecraft.util.DyeColor
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.Util
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
@@ -142,7 +136,6 @@ open class PokemonEntity(
@JvmStatic val UNBATTLEABLE = DataTracker.registerData(PokemonEntity::class.java, TrackedDataHandlerRegistry.BOOLEAN)
@JvmStatic val COUNTS_TOWARDS_SPAWN_CAP = DataTracker.registerData(PokemonEntity::class.java, TrackedDataHandlerRegistry.BOOLEAN)
@JvmStatic val SPAWN_DIRECTION = DataTracker.registerData(PokemonEntity::class.java, TrackedDataHandlerRegistry.FLOAT)
@JvmStatic val FRIENDSHIP = DataTracker.registerData(PokemonEntity::class.java, TrackedDataHandlerRegistry.INTEGER)
const val BATTLE_LOCK = "battle"
@@ -188,8 +181,6 @@ open class PokemonEntity(
set(value) = dataTracker.set(BATTLE_ID, Optional.ofNullable(value))
val isBattling: Boolean
get() = dataTracker.get(BATTLE_ID).isPresent
val friendship: Int
get() = dataTracker.get(FRIENDSHIP)
var drops: DropTable? = null
@@ -255,9 +246,7 @@ open class PokemonEntity(
dataTracker.startTracking(LABEL_LEVEL, 1)
dataTracker.startTracking(HIDE_LABEL, false)
dataTracker.startTracking(UNBATTLEABLE, false)
dataTracker.startTracking(COUNTS_TOWARDS_SPAWN_CAP, true)
dataTracker.startTracking(SPAWN_DIRECTION, world.random.nextFloat() * 360F)
dataTracker.startTracking(FRIENDSHIP, 0)
}
override fun onTrackedDataSet(data: TrackedData<*>) {
@@ -337,21 +326,6 @@ open class PokemonEntity(
this.tethering = null
this.pokemon.recall()
}
//This is so that pokemon in the pasture block are ALWAYS in sync with the pokemon box
//Before, pokemon entities in pastures would hold an old ref to a pokemon obj and changes to that would not appear to the underlying file
if (this.tethering != null) {
//Only for online players
if (world.getPlayerByUuid(ownerUuid) != null){
this.ownerUuid?.let {
val actualPokemon = Cobblemon.storage.getPC(it)[this.pokemon.uuid]
actualPokemon?.let {
if (it !== pokemon) {
pokemon = it
}
}
}
}
}
schedulingTracker.update(1/20F)
}
@@ -616,152 +590,113 @@ open class PokemonEntity(
override fun interactMob(player: PlayerEntity, hand: Hand) : ActionResult {
val itemStack = player.getStackInHand(hand)
val colorFeatureType = SpeciesFeatures.getFeaturesFor(pokemon.species).find { it is ChoiceSpeciesFeatureProvider && DataKeys.CAN_BE_COLORED in it.keys }
val colorFeature = pokemon.getFeature<StringSpeciesFeature>(DataKeys.CAN_BE_COLORED)
if (ownerUuid == player.uuid || ownerUuid == null) {
if (itemStack.isOf(Items.SHEARS) && this.isShearable) {
this.sheared(SoundCategory.PLAYERS)
this.emitGameEvent(GameEvent.SHEAR, player)
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
return ActionResult.SUCCESS
} else if (itemStack.isOf(Items.BUCKET)) {
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.CAN_BE_MILKED) != null) {
player.playSound(SoundEvents.ENTITY_GOAT_MILK, 1.0f, 1.0f)
val milkBucket = ItemUsage.exchangeStack(itemStack, player, Items.MILK_BUCKET.defaultStack)
player.setStackInHand(hand, milkBucket)
return ActionResult.success(world.isClient)
}
} else if (itemStack.isOf(Items.BOWL)) {
if (pokemon.aspects.any { it.contains("mooshtank") }) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_MILK, 1.0f, 1.0f)
// if the Mooshtank ate a Flower beforehand
if (pokemon.lastFlowerFed != ItemStack.EMPTY && pokemon.aspects.any() { it.contains("mooshtank-brown") }) {
var effect: StatusEffect? = null
var duration = 0
if (pokemon.lastFlowerFed.isOf(Items.ALLIUM)) {
effect = StatusEffect.byRawId(12) // Fire Resistance
duration = 80 // 4 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.AZURE_BLUET)) {
effect = StatusEffect.byRawId(15) // Blindness
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.BLUE_ORCHID) || pokemon.lastFlowerFed.isOf(Items.DANDELION)) {
effect = StatusEffect.byRawId(23) // Saturation
duration = 7 // .35 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.CORNFLOWER)) {
effect = StatusEffect.byRawId(8) // Jump Boost
duration = 120 // 6 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.LILY_OF_THE_VALLEY)) {
effect = StatusEffect.byRawId(19) // Poison
duration = 240 // 12 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.OXEYE_DAISY)) {
effect = StatusEffect.byRawId(10) // Regeneration
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.POPPY) || pokemon.lastFlowerFed.isOf(Items.TORCHFLOWER)) {
effect = StatusEffect.byRawId(16) // Night Vision
duration = 100 // 5 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.PINK_TULIP) || pokemon.lastFlowerFed.isOf(Items.RED_TULIP) || pokemon.lastFlowerFed.isOf(
Items.WHITE_TULIP
) || pokemon.lastFlowerFed.isOf(Items.ORANGE_TULIP)
) {
effect = StatusEffect.byRawId(18) // Weakness
duration = 180 // 9 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.WITHER_ROSE)) {
effect = StatusEffect.byRawId(20) // Wither
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(CobblemonItems.PEP_UP_FLOWER)) {
effect = StatusEffect.byRawId(25) // Levitation
duration = 160 // 8 seconds
}
// modify the suspicious stew with the effect
val susStewStack = Items.SUSPICIOUS_STEW.defaultStack
SuspiciousStewItem.addEffectToStew(susStewStack, effect, duration)
val susStewEffect = ItemUsage.exchangeStack(itemStack, player, susStewStack)
//give player modified Suspicious Stew
player.setStackInHand(hand, susStewEffect)
// reset the flower fed state
pokemon.lastFlowerFed = ItemStack.EMPTY
return ActionResult.success(world.isClient)
} else {
val mushroomStew = ItemUsage.exchangeStack(itemStack, player, Items.MUSHROOM_STEW.defaultStack)
player.setStackInHand(hand, mushroomStew)
return ActionResult.success(world.isClient)
}
}
}
// Flowers used on brown MooshTanks
else if (itemStack.isOf(Items.ALLIUM) ||
itemStack.isOf(Items.AZURE_BLUET) ||
itemStack.isOf(Items.BLUE_ORCHID) ||
itemStack.isOf(Items.DANDELION) ||
itemStack.isOf(Items.CORNFLOWER) ||
itemStack.isOf(Items.LILY_OF_THE_VALLEY) ||
itemStack.isOf(Items.OXEYE_DAISY) ||
itemStack.isOf(Items.POPPY) ||
itemStack.isOf(Items.TORCHFLOWER) ||
itemStack.isOf(Items.PINK_TULIP) ||
itemStack.isOf(Items.RED_TULIP) ||
itemStack.isOf(Items.WHITE_TULIP) ||
itemStack.isOf(Items.ORANGE_TULIP) ||
itemStack.isOf(Items.WITHER_ROSE) ||
itemStack.isOf(CobblemonItems.PEP_UP_FLOWER)
) {
if (pokemon.aspects.any { it.contains("mooshtank") }) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_EAT, 1.0f, 1.0f)
pokemon.lastFlowerFed = itemStack
return ActionResult.success(world.isClient)
}
} else if (!player.isSneaking && player.uuid == ownerUuid && (itemStack.isOf(CobblemonItems.RELIC_COIN)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_POUCH)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_SACK)
|| itemStack.isOf(Items.NETHERITE_SCRAP)
|| itemStack.isOf(Items.NETHERITE_INGOT)
|| itemStack.isOf(Items.NETHERITE_BLOCK))
) {
if (GimmighoulStashHandler.interactMob(player, hand, pokemon)) {
return ActionResult.SUCCESS
}
} else if (itemStack.item is DyeItem && colorFeatureType != null) {
val currentColor = colorFeature?.value ?: ""
val item = itemStack.item as DyeItem
if (!item.color.name.equals(currentColor, ignoreCase = true)) {
if (player is ServerPlayerEntity) {
if (colorFeature != null) {
colorFeature.value = item.color.name.lowercase()
this.pokemon.markFeatureDirty(colorFeature)
} else {
val newColorFeature =
StringSpeciesFeature(DataKeys.CAN_BE_COLORED, item.color.name.lowercase())
this.pokemon.features.add(newColorFeature)
this.pokemon.anyChangeObservable.emit(pokemon)
}
this.pokemon.updateAspects()
if (!player.isCreative) {
itemStack.decrement(1)
}
}
return ActionResult.success(world.isClient)
}
} else if (itemStack.item.equals(Items.WATER_BUCKET) && colorFeatureType != null) {
if (player is ServerPlayerEntity) {
if (colorFeature != null) {
if (!player.isCreative) {
itemStack.decrement(1)
player.giveOrDropItemStack(Items.BUCKET.defaultStack)
}
colorFeature.value = ""
this.pokemon.markFeatureDirty(colorFeature)
this.pokemon.updateAspects()
}
}
if (itemStack.isOf(Items.SHEARS) && this.isShearable) {
this.sheared(SoundCategory.PLAYERS)
this.emitGameEvent(GameEvent.SHEAR, player)
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
return ActionResult.SUCCESS
}
else if (itemStack.isOf(Items.BUCKET)) {
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.CAN_BE_MILKED) != null) {
player.playSound(SoundEvents.ENTITY_GOAT_MILK, 1.0f, 1.0f)
val milkBucket = ItemUsage.exchangeStack(itemStack, player, Items.MILK_BUCKET.defaultStack)
player.setStackInHand(hand, milkBucket)
return ActionResult.success(world.isClient)
}
} else if (itemStack.isOf(Items.BOWL)) {
if (pokemon.aspects.any() {it.contains("mooshtank")}) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_MILK, 1.0f, 1.0f)
// if the Mooshtank ate a Flower beforehand
if (pokemon.lastFlowerFed != ItemStack.EMPTY && pokemon.aspects.any() {it.contains("mooshtank-brown")}) {
var effect: StatusEffect? = null
var duration = 0
if (pokemon.lastFlowerFed.isOf(Items.ALLIUM)) {
effect = StatusEffect.byRawId(12) // Fire Resistance
duration = 80 // 4 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.AZURE_BLUET)) {
effect = StatusEffect.byRawId(15) // Blindness
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.BLUE_ORCHID) || pokemon.lastFlowerFed.isOf(Items.DANDELION)) {
effect = StatusEffect.byRawId(23) // Saturation
duration = 7 // .35 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.CORNFLOWER)) {
effect = StatusEffect.byRawId(8) // Jump Boost
duration = 120 // 6 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.LILY_OF_THE_VALLEY)) {
effect = StatusEffect.byRawId(19) // Poison
duration = 240 // 12 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.OXEYE_DAISY)) {
effect = StatusEffect.byRawId(10) // Regeneration
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.POPPY) || pokemon.lastFlowerFed.isOf(Items.TORCHFLOWER)) {
effect = StatusEffect.byRawId(16) // Night Vision
duration = 100 // 5 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.PINK_TULIP) || pokemon.lastFlowerFed.isOf(Items.RED_TULIP) || pokemon.lastFlowerFed.isOf(Items.WHITE_TULIP) || pokemon.lastFlowerFed.isOf(Items.ORANGE_TULIP)) {
effect = StatusEffect.byRawId(18) // Weakness
duration = 180 // 9 seconds
} else if (pokemon.lastFlowerFed.isOf(Items.WITHER_ROSE)) {
effect = StatusEffect.byRawId(20) // Wither
duration = 160 // 8 seconds
} else if (pokemon.lastFlowerFed.isOf(CobblemonItems.PEP_UP_FLOWER)) {
effect = StatusEffect.byRawId(25) // Levitation
duration = 160 // 8 seconds
}
// modify the suspicious stew with the effect
val susStewStack = Items.SUSPICIOUS_STEW.defaultStack
SuspiciousStewItem.addEffectToStew(susStewStack, effect, duration)
val susStewEffect = ItemUsage.exchangeStack(itemStack, player, susStewStack)
//give player modified Suspicious Stew
player.setStackInHand(hand, susStewEffect)
// reset the flower fed state
pokemon.lastFlowerFed = ItemStack.EMPTY
return ActionResult.success(world.isClient)
}
else {
val mushroomStew = ItemUsage.exchangeStack(itemStack, player, Items.MUSHROOM_STEW.defaultStack)
player.setStackInHand(hand, mushroomStew)
return ActionResult.success(world.isClient)
}
}
}
// Flowers used on brown MooshTanks
else if (itemStack.isOf(Items.ALLIUM) ||
itemStack.isOf(Items.AZURE_BLUET) ||
itemStack.isOf(Items.BLUE_ORCHID) ||
itemStack.isOf(Items.DANDELION) ||
itemStack.isOf(Items.CORNFLOWER) ||
itemStack.isOf(Items.LILY_OF_THE_VALLEY) ||
itemStack.isOf(Items.OXEYE_DAISY) ||
itemStack.isOf(Items.POPPY) ||
itemStack.isOf(Items.TORCHFLOWER) ||
itemStack.isOf(Items.PINK_TULIP) ||
itemStack.isOf(Items.RED_TULIP) ||
itemStack.isOf(Items.WHITE_TULIP) ||
itemStack.isOf(Items.ORANGE_TULIP) ||
itemStack.isOf(Items.WITHER_ROSE) ||
itemStack.isOf(CobblemonItems.PEP_UP_FLOWER)) {
if (pokemon.aspects.any() {it.contains("mooshtank")}) {
player.playSound(SoundEvents.ENTITY_MOOSHROOM_EAT, 1.0f, 1.0f)
pokemon.lastFlowerFed = itemStack
return ActionResult.success(world.isClient)
}
} else if(!player.isSneaking && (itemStack.isOf(CobblemonItems.RELIC_COIN)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_POUCH)
|| itemStack.isOf(CobblemonItems.RELIC_COIN_SACK)
|| itemStack.isOf(Items.NETHERITE_SCRAP)
|| itemStack.isOf(Items.NETHERITE_INGOT)
|| itemStack.isOf(Items.NETHERITE_BLOCK))) {
if(GimmighoulStashHandler.interactMob(player, hand, pokemon)) {
return ActionResult.SUCCESS
}
}
if (hand == Hand.MAIN_HAND && player is ServerPlayerEntity && pokemon.getOwnerPlayer() == player) {
if (player.isSneaking) {
@@ -889,7 +824,7 @@ open class PokemonEntity(
// Check evolution item interaction
if (pokemon.getOwnerPlayer() == player) {
val context = ItemInteractionEvolution.ItemInteractionContext(stack, player.world)
pokemon.lockedEvolutions
pokemon.evolutions
.filterIsInstance<ItemInteractionEvolution>()
.forEach { evolution ->
if (evolution.attemptEvolution(pokemon, context)) {
@@ -1187,26 +1122,8 @@ open class PokemonEntity(
this.pokemon.markFeatureDirty(feature)
this.pokemon.updateAspects()
val i = this.random.nextInt(3) + 1
for (j in 0 .. i) {
val color = this.pokemon.getFeature<StringSpeciesFeature>(DataKeys.CAN_BE_COLORED)?.value ?: "white"
val woolItem = when (color) {
"black" -> Items.BLACK_WOOL
"blue" -> Items.BLUE_WOOL
"brown" -> Items.BROWN_WOOL
"cyan" -> Items.CYAN_WOOL
"gray" -> Items.GRAY_WOOL
"green" -> Items.GREEN_WOOL
"light-blue" -> Items.LIGHT_BLUE_WOOL
"light-gray" -> Items.LIGHT_GRAY_WOOL
"lime" -> Items.LIME_WOOL
"magenta" -> Items.MAGENTA_WOOL
"orange" -> Items.ORANGE_WOOL
"purple" -> Items.PURPLE_WOOL
"red" -> Items.RED_WOOL
"yellow" -> Items.YELLOW_WOOL
else -> Items.WHITE_WOOL
}
val itemEntity = this.dropItem(woolItem, 1) ?: return
for (j in 0 until i) {
val itemEntity = this.dropItem(Items.WHITE_WOOL, 1) ?: return
itemEntity.velocity = itemEntity.velocity.add(
((this.random.nextFloat() - this.random.nextFloat()) * 0.1f).toDouble(),
(this.random.nextFloat() * 0.05f).toDouble(),
@@ -1220,8 +1137,6 @@ open class PokemonEntity(
return !this.isBusy && !this.pokemon.isFainted() && !feature.enabled
}
override fun canUsePortals() = false
override fun setAir(air: Int) {
@@ -92,28 +92,10 @@ class PokemonServerDelegate : PokemonSideDelegate {
speed = 0.1F
entity.despawner.beginTracking(this)
}
updateTrackedValues()
}
fun getBattle() = entity.battleId?.let(BattleRegistry::getBattle)
fun updateTrackedValues() {
val trackedSpecies = mock?.species ?: entity.pokemon.species.resourceIdentifier.toString()
val trackedNickname = mock?.nickname ?: entity.pokemon.nickname ?: Text.empty()
val trackedAspects = mock?.aspects ?: entity.pokemon.aspects
entity.dataTracker.set(PokemonEntity.SPECIES, trackedSpecies)
if (entity.dataTracker.get(PokemonEntity.NICKNAME) != trackedNickname) {
entity.dataTracker.set(PokemonEntity.NICKNAME, trackedNickname)
}
entity.dataTracker.set(PokemonEntity.ASPECTS, trackedAspects)
entity.dataTracker.set(PokemonEntity.LABEL_LEVEL, entity.pokemon.level)
entity.dataTracker.set(PokemonEntity.MOVING, entity.velocity.multiply(1.0, if (entity.isOnGround) 0.0 else 1.0, 1.0).length() > 0.005F)
entity.dataTracker.set(PokemonEntity.FRIENDSHIP, entity.pokemon.friendship)
updatePoseType()
}
override fun onTrackedDataSet(data: TrackedData<*>) {
super.onTrackedDataSet(data)
if (this::entity.isInitialized) {
@@ -180,7 +162,19 @@ class PokemonServerDelegate : PokemonSideDelegate {
entity.remove(Entity.RemovalReason.DISCARDED)
}
updateTrackedValues()
val trackedSpecies = mock?.species ?: entity.pokemon.species.resourceIdentifier.toString()
val trackedNickname = mock?.nickname ?: entity.pokemon.nickname ?: Text.empty()
val trackedAspects = mock?.aspects ?: entity.pokemon.aspects
entity.dataTracker.set(PokemonEntity.SPECIES, trackedSpecies)
if (entity.dataTracker.get(PokemonEntity.NICKNAME) != trackedNickname) {
entity.dataTracker.set(PokemonEntity.NICKNAME, trackedNickname)
}
entity.dataTracker.set(PokemonEntity.ASPECTS, trackedAspects)
entity.dataTracker.set(PokemonEntity.LABEL_LEVEL, entity.pokemon.level)
entity.dataTracker.set(PokemonEntity.MOVING, entity.velocity.multiply(1.0, if (entity.isOnGround) 0.0 else 1.0, 1.0).length() > 0.005F)
updatePoseType()
}
fun updatePoseType() {
@@ -235,7 +229,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
if (entity.deathTime == 60) {
if (entity.pokemon.isWild()) {
if (entity.owner == null) {
entity.world.sendEntityStatus(entity, 60.toByte()) // Sends smoke effect
if(entity.world.gameRules.getBoolean(CobblemonGameRules.DO_POKEMON_LOOT)) {
(entity.drops ?: entity.pokemon.form.drops).drop(entity, entity.world as ServerWorld, entity.pos, entity.killer)
@@ -87,7 +87,9 @@ object AdvancementHandler {
event.battle.actors.forEach { battleActor ->
if (!event.winners.contains(battleActor) && battleActor.type == ActorType.WILD) {
battleActor.pokemonList.forEach { battlePokemon ->
advancementData.updateTotalDefeatedCount(battlePokemon.originalPokemon)
battlePokemon.entity?.pokemon?.let {
advancementData.updateTotalDefeatedCount(it)
}
}
}
}
@@ -10,9 +10,10 @@ package com.cobblemon.mod.common.integration.jei.berry
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.integration.jei.CobblemonJeiProvider
import com.cobblemon.mod.common.item.berry.BerryItem
import com.cobblemon.mod.common.item.BerryItem
import mezz.jei.api.registration.IRecipeCategoryRegistration
import mezz.jei.api.registration.IRecipeRegistration
import net.minecraft.item.ItemStack
class BerryMutationProvider : CobblemonJeiProvider {
override fun registerCategory(registration: IRecipeCategoryRegistration) {
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.integration.jei.berry
import com.cobblemon.mod.common.item.berry.BerryItem
import com.cobblemon.mod.common.item.BerryItem
//Dont make these ItemStacks, they do not have consistent hash codes, breaks stuff
data class BerryMutationRecipe(
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.berry
package com.cobblemon.mod.common.item
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.item.AliasedBlockItem
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
import com.cobblemon.mod.common.util.resolveInt
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.item.PokemonAndMoveSelectingItem
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.genericRuntime
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.player.PlayerEntity
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.item.ItemStack
@@ -253,37 +253,6 @@ object CobblemonItemGroups {
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BLOCK)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BLOCK)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE_STAIRS)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE_SLAB)
entries.add(CobblemonItems.POLISHED_TUMBLESTONE_WALL)
entries.add(CobblemonItems.CHISELED_POLISHED_TUMBLESTONE)
entries.add(CobblemonItems.TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.TUMBLESTONE_BRICK_STAIRS)
entries.add(CobblemonItems.TUMBLESTONE_BRICK_SLAB)
entries.add(CobblemonItems.TUMBLESTONE_BRICK_WALL)
entries.add(CobblemonItems.CHISELED_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE_STAIRS)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE_SLAB)
entries.add(CobblemonItems.POLISHED_BLACK_TUMBLESTONE_WALL)
entries.add(CobblemonItems.CHISELED_POLISHED_BLACK_TUMBLESTONE)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICK_STAIRS)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICK_SLAB)
entries.add(CobblemonItems.BLACK_TUMBLESTONE_BRICK_WALL)
entries.add(CobblemonItems.CHISELED_BLACK_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE_STAIRS)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE_SLAB)
entries.add(CobblemonItems.POLISHED_SKY_TUMBLESTONE_WALL)
entries.add(CobblemonItems.CHISELED_POLISHED_SKY_TUMBLESTONE)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICK_STAIRS)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICK_SLAB)
entries.add(CobblemonItems.SKY_TUMBLESTONE_BRICK_WALL)
entries.add(CobblemonItems.CHISELED_SKY_TUMBLESTONE_BRICKS)
entries.add(CobblemonItems.DAWN_STONE_ORE)
entries.add(CobblemonItems.DEEPSLATE_DAWN_STONE_ORE)
entries.add(CobblemonItems.DUSK_STONE_ORE)
@@ -19,7 +19,7 @@ class LinkCableItem : CobblemonItem(Settings()), PokemonEntityInteraction {
override val accepted = setOf(PokemonEntityInteraction.Ownership.OWNER)
override fun processInteraction(player: ServerPlayerEntity, entity: PokemonEntity, stack: ItemStack): Boolean {
val pokemon = entity.pokemon
pokemon.lockedEvolutions.filterIsInstance<TradeEvolution>().forEach { evolution ->
pokemon.evolutions.filterIsInstance<TradeEvolution>().forEach { evolution ->
// If an evolution is possible non-optional or has been successfully queued we will consume the item and stop
// validate requirements to respect required held items and such.
if (evolution.requirements.all { it.check(pokemon) } && evolution.evolve(pokemon)) {
@@ -1,38 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.pokemon.update
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class FormUpdatePacket(pokemon: () -> Pokemon, form: FormData) : SingleUpdatePacket<FormData, FormUpdatePacket>(pokemon, form) {
override fun encodeValue(buffer: PacketByteBuf) {
this.value.encode(buffer)
}
override fun set(pokemon: Pokemon, value: FormData) {
pokemon.form = value
}
override val id: Identifier = ID
companion object {
val ID: Identifier = cobblemonResource("packets/form-update")
fun decode(buffer: PacketByteBuf): FormUpdatePacket {
val pokemon = decodePokemon(buffer)
val form = FormData()
form.species = pokemon().species
form.decode(buffer)
return FormUpdatePacket(pokemon, form)
}
}
}
@@ -39,7 +39,6 @@ class SpawnPokemonPacket(
private val hideLabel: Boolean,
private val caughtBall: Identifier,
private val spawnYaw: Float,
private val friendship: Int,
vanillaSpawnPacket: EntitySpawnS2CPacket
) : SpawnExtraDataEntityPacket<SpawnPokemonPacket, PokemonEntity>(vanillaSpawnPacket) {
@@ -61,7 +60,6 @@ class SpawnPokemonPacket(
entity.dataTracker.get(PokemonEntity.HIDE_LABEL),
entity.pokemon.caughtBall.name,
entity.dataTracker.get(PokemonEntity.SPAWN_DIRECTION),
entity.dataTracker.get(PokemonEntity.FRIENDSHIP),
vanillaSpawnPacket
)
@@ -81,7 +79,6 @@ class SpawnPokemonPacket(
buffer.writeBoolean(this.hideLabel)
buffer.writeIdentifier(this.caughtBall)
buffer.writeFloat(this.spawnYaw)
buffer.writeInt(this.friendship)
}
override fun applyData(entity: PokemonEntity) {
@@ -104,7 +101,6 @@ class SpawnPokemonPacket(
entity.dataTracker.set(PokemonEntity.UNBATTLEABLE, unbattlable)
entity.dataTracker.set(PokemonEntity.HIDE_LABEL, hideLabel)
entity.dataTracker.set(PokemonEntity.SPAWN_DIRECTION, spawnYaw)
entity.dataTracker.set(PokemonEntity.FRIENDSHIP, friendship)
}
override fun checkType(entity: Entity): Boolean = entity is PokemonEntity
@@ -128,10 +124,9 @@ class SpawnPokemonPacket(
val hideLabel = buffer.readBoolean()
val caughtBall = buffer.readIdentifier()
val spawnAngle = buffer.readFloat()
val friendship = buffer.readInt()
val vanillaPacket = decodeVanillaPacket(buffer)
return SpawnPokemonPacket(ownerId, scaleModifier, species, form, aspects, battleId, phasingTargetId, beamModeEmitter, nickname, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, friendship, vanillaPacket)
return SpawnPokemonPacket(ownerId, scaleModifier, species, form, aspects, battleId, phasingTargetId, beamModeEmitter, nickname, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, vanillaPacket)
}
}
@@ -43,7 +43,6 @@ object SnowstormParticleReader {
val emitterShapeEntityBoundingBoxJson = componentsJson.get("minecraft:emitter_shape_entity_aabb")?.asJsonObject
val emitterLifetimeEventsJson = componentsJson.get("minecraft:emitter_lifetime_events")?.asJsonObject
val dynamicMotionJson = componentsJson.get("minecraft:particle_motion_dynamic")?.asJsonObject
val parametricMotionJson = componentsJson.get("minecraft:particle_motion_parametric")?.asJsonObject
val particleAppearanceJson = componentsJson.get("minecraft:particle_appearance_billboard").asJsonObject
val sizeJson = particleAppearanceJson.get("size")?.asJsonArray
val particleLifetimeJson = componentsJson.get("minecraft:particle_lifetime_expression")?.asJsonObject
@@ -235,22 +234,13 @@ object SnowstormParticleReader {
val motion = if (dynamicMotionJson != null) {
val accelerationExpressions = dynamicMotionJson.get("linear_acceleration")?.asJsonArray?.map { it.asString.asExpression() }
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
val drag = dynamicMotionJson.get("linear_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
val drag= dynamicMotionJson.get("linear_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
DynamicParticleMotion(
direction = direction!!,
speed = speed,
acceleration = Triple(accelerationExpressions[0], accelerationExpressions[1], accelerationExpressions[2]),
drag = drag
)
} else if (parametricMotionJson != null) {
val offsetExpressions = parametricMotionJson.get("relative_position")?.asJsonArray?.map { it.asString.asExpression() }
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
val directionExpressions = parametricMotionJson.get("direction")?.asJsonArray?.map { it.asString.asExpression() }
?: listOf(0.0.asExpression(), 0.0.asExpression(), 0.0.asExpression())
ParametricParticleMotion(
offset = Triple(offsetExpressions[0], offsetExpressions[1], offsetExpressions[2]),
direction = Triple(directionExpressions[0], directionExpressions[1], directionExpressions[2])
)
} else {
StaticParticleMotion()
}
@@ -309,19 +299,12 @@ object SnowstormParticleReader {
textureSizeY = uvModeJson.get("texture_height")?.asInt ?: 128
)
}
val motionJson = dynamicMotionJson ?: parametricMotionJson
val parametricParticleRotation = motionJson?.get("rotation")?.asString?.asExpression()
val rotation = if (parametricParticleRotation != null) {
ParametricParticleRotation(expression = parametricParticleRotation)
} else {
DynamicParticleRotation(
startRotation = startRotation,
speed = rotationSpeed,
acceleration = dynamicMotionJson?.get("rotation_acceleration")?.asString?.asExpression() ?: 0.0.asExpression(),
drag = dynamicMotionJson?.get("rotation_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
)
}
val rotation = DynamicParticleRotation(
startRotation = startRotation,
speed = rotationSpeed,
acceleration = dynamicMotionJson?.get("rotation_acceleration")?.asString?.asExpression() ?: 0.0.asExpression(),
drag = dynamicMotionJson?.get("rotation_drag_coefficient")?.asString?.asExpression() ?: 0.0.asExpression()
)
val tinting = if (colourJson is JsonObject) {
GradientParticleTinting(
interpolant = colourJson.get("interpolant").asString.asExpression(),
@@ -18,12 +18,12 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
@@ -14,12 +14,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalcula
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import kotlin.math.roundToInt
import kotlin.random.Random
import net.minecraft.entity.LivingEntity
@@ -12,12 +12,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.random.Random
@@ -13,12 +13,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import kotlin.math.roundToInt
import kotlin.math.sqrt
import kotlin.random.Random
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.api.pokeball.catching.calculators.CriticalCaptur
import com.cobblemon.mod.common.api.pokeball.catching.calculators.PokedexProgressCaptureMultiplierProvider
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
import kotlin.math.roundToInt
import kotlin.math.sqrt
import kotlin.random.Random

Some files were not shown because too many files have changed in this diff Show More