Compare commits

...
Author SHA1 Message Date
Licious aeedec0b6d Make starters work from datapacks instead of config. 2024-04-03 12:24:06 +01:00
Paul a37fdf709a Fixed Inkay evo method. Fixes #787 2024-04-01 18:51:55 +10:00
Paul 5023f28d04 Fixed underwalking the target position leading to walking in place. Fixes #790 2024-04-01 18:40:57 +10:00
Hiroku 67de28386a Merge branch 'ball-animation-changes' into 'main'
Breakout Animation, Overhauled Sendout Logic

See merge request cable-mc/cobblemon!943
2024-04-01 01:03:16 +00:00
Hiroku cd37d3538e Formatting 2024-04-01 00:39:38 +00:00
Hiroku 3d03ee5eb3 Merge branch 'gilded_chest_names' into 'main'
Preserve and display custom names for gilded chests

Closes #792

See merge request cable-mc/cobblemon!945
2024-03-31 12:11:57 +00:00
Paul 0b685ed453 Actually that one field is nullable 2024-03-31 21:47:10 +10:00
Hiroku 115e990df6 Update GildedChestBlock.kt 2024-03-31 10:19:16 +00:00
Hiroku 2ee38f0a29 Merge branch '1.5-Structures' into 'main'
fixed spelling error in gilded chest loot table

See merge request cable-mc/cobblemon!948
2024-03-31 10:18:03 +00:00
Slippy 756ef48ba2 coins are more common in sus blocks 2024-03-31 04:52:02 -05:00
Slippy 2c4df6622a fixed spelling error in gilded chest loot table 2024-03-31 04:30:48 -05:00
Hiroku 425c41bc4b Merge branch 'waterloggable-tumblestones' into 'main'
Make tumblestones waterloggable

Closes #793

See merge request cable-mc/cobblemon!946
2024-03-31 07:22:48 +00:00
Hiroku 9f7a99b583 Merge branch '1.5-Structures' into 'main'
1.5 structures

See merge request cable-mc/cobblemon!947
2024-03-31 07:22:22 +00:00
Slippy f1213abdf4 candy adjustment 2024-03-31 01:04:02 -05:00
Slippy 9b1d847f59 added absorb bulb to some fossil loot tables 2024-03-31 00:09:28 -05:00
Slippy bf900a0167 Just an enormous amount of changes to loot tables and structures 2024-03-30 23:57:54 -05:00
miasmus d60d828e93 make tumblestones waterloggable 2024-03-30 23:00:08 -04:00
Slippy 703282eca1 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into 1.5-Structures 2024-03-30 19:39:12 -05:00
miasmus 5d7e6f6487 Preserve and display custom names for gilded chests 2024-03-30 19:38:38 -04:00
Hiroku 4e3e6405d0 Merge branch 'FossilMachineBalancing01' into 'main'
Fossil machine balancing01

See merge request cable-mc/cobblemon!944
2024-03-30 23:01:44 +00:00
JazzMcNade 1e161a52ee Fixed an index out of bounds issue related to having a extreme excess of material in the tank 2024-03-30 02:27:04 -05:00
JazzMcNade 65b2204783 Decreased required material to 128 (from 256). Decreased restoration time to 12min (from 15min). 2024-03-30 02:14:06 -05:00
Flarkinater 76d9137ae4 Merge branch 'main' into ball-animation-changes 2024-03-29 22:09:21 -04:00
Flarkinater 9f6c44fd75 Sendout location is only shifted vertically when necessary 2024-03-29 22:08:40 -04:00
Flarkinater 6c086f5555 Fixed merge conflict goofs 2024-03-29 21:58:26 -04:00
Slippy a9115bfb15 XL RCs are less common 2024-03-29 01:27:30 -05:00
Hiroku f2c5113f0b Merge branch 'json-addict' into 'main'
Advancement and Tag Updates

See merge request cable-mc/cobblemon!942
2024-03-29 05:31:33 +00:00
Hiroku e29a2402e9 Merge branch 'spawning-gamerule-change' into 'main'
Move doPokemonSpawning gamerule logic

See merge request cable-mc/cobblemon!941
2024-03-29 05:30:53 +00:00
Hiroku aa4857abbb Merge branch 'Quick-Move-Stacked-PokePotion-Bases' into 'main'
Add the ability to quick move stacked PokePotion bases into Brewing Stands.

See merge request cable-mc/cobblemon!935
2024-03-29 05:29:13 +00:00
Hiroku d29653233d Merge branch 'main' into 'Quick-Move-Stacked-PokePotion-Bases'
# Conflicts:
#   CHANGELOG.md
2024-03-29 05:28:22 +00:00
Hiroku 1b51a128d7 Merge branch 'OldAmberSpriteUpdate' into 'main'
Corrected Old Amber Sprite

See merge request cable-mc/cobblemon!939
2024-03-29 05:27:10 +00:00
Hiroku 245011cf5e Merge branch '1.5-Structures' into 'main'
Oh boy that's a lot of changes and fixes to structures

See merge request cable-mc/cobblemon!940
2024-03-29 05:26:30 +00:00
Flarkinater 100bf7fee4 Merge remote-tracking branch 'origin/main' into ball-animation-changes
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
2024-03-29 00:05:58 -04:00
Flarkinater be043a0ea1 Fixed minor issue with shifting Pokemon vertically during suffocation sendout check 2024-03-28 23:41:24 -04:00
Flarkinater a902f4a8a3 Fixed suffocation avoidance when sending out Pokemon adjacent to a wall, other related fixes 2024-03-28 23:05:30 -04:00
Flarkinater 442f54839f Improved spacing when targeting walls and above blocks 2024-03-28 22:09:41 -04:00
dhi_holo 20a97984a2 Misc Structures
Just a couple tags containing all structures not represented in a sub-tag, if someone wants to go in and make more tags to reduce the ones in here feel free, just make sure it adds up.
2024-03-29 10:54:10 +10:00
dhi_holo d1517548bd Update en_us.json
Gamerule Translation so it's not just a shitty string anymore
2024-03-29 10:09:22 +10:00
dhi_holo 3e1f9dc9b0 2 Shiny
Updated shiny icon for summary and advancement
2024-03-29 09:54:52 +10:00
miasmus fd19e65f79 move doPokemonSpawing gamerule logic 2024-03-28 16:42:42 -04:00
dhi_holo 6b10d81bc8 Update en_us.json
Capital fixes because it wasn't right
2024-03-28 23:53:56 +10:00
dhi_holo f3b9c03fc2 Advancement Updates
Reorgainised the advancements Healing Power, Consult the Fossil, and Free Roam: Enabled, renamed Tumbling Down to be correct, and changed the Shiny advancement to use Shiny icon.
2024-03-28 21:39:56 +10:00
Flarkinater 92653c1307 When targeting a wall, Pokemon are sent out far enough away to avoid suffocation, based on their size. Wailord rejoice! 2024-03-27 23:42:24 -04:00
Flarkinater c372114da5 Improved avoidance of unsafe locations when sending out Pokemon 2024-03-27 23:26:07 -04:00
Slippy 2b95138c46 Oh boy that's a lot of changes and fixes to structures 2024-03-27 20:02:02 -05:00
JazzMcNade 864e6be461 Corrected Old Amber Sprite 2024-03-27 19:53:10 -05:00
Apion c02fa1f85e Merge branch '1.5-Drops-and-Fixes' into 'main'
New drops and some tag/script fixes.

See merge request cable-mc/cobblemon!938
2024-03-27 20:29:11 +00:00
Apion 503b1069fd Merge branch 'MultiBlockEntityClientCrashFix' into 'main'
MultiBlockEntity Safer Casting

See merge request cable-mc/cobblemon!937
2024-03-27 20:27:01 +00:00
JazzMcNade f1d593d53c MultiBlockEntity Safer Casting 2024-03-27 20:27:01 +00:00
Veraxiel 55dd486044 Updated Changelog. 2024-03-27 01:10:26 -04:00
Hiroku 3263837137 Merge branch '1.5-Structures' into 'main'
Structure fixes and generation rate adjustments

See merge request cable-mc/cobblemon!936
2024-03-25 21:14:19 +00:00
Slippy a471fcd948 Increased rates for underground ruin structures, forest ruin structures and gimmi towers 2024-03-25 12:29:03 -05:00
Slippy a14bfd55d1 Jungle Fossil rates were increased 2024-03-25 12:22:25 -05:00
Slippy 725e819e89 Snow Forest structures rates were increased 2024-03-25 12:21:23 -05:00
Slippy b52b843206 Ocean fossils structures rates were increased 2024-03-25 12:20:13 -05:00
Slippy 74c65e22cb Decaying Crypt generation rates increased 2024-03-25 12:19:20 -05:00
Slippy c348520a9b Increased the generation rate for Mossy Oubliette 2024-03-25 12:15:42 -05:00
Slippy 78dd5f334a Decrease how common desert ruins are and adjusted the weights at which each will generate 2024-03-25 12:12:54 -05:00
Slippy 67318c9307 Sandy Den's were way too common. Adjusted their rates of generation 2024-03-25 12:05:17 -05:00
Slippy 31c48407df Slightly lowered the amount of sus sand in the sandy den structure 2024-03-25 11:43:45 -05:00
Flarkinater 43da7ecece First attempt at canceling sendouts into unsafe locations (cactus, fire if the Pokemon isn't immune) 2024-03-24 23:41:22 -04:00
Charzard4261 0957de86c7 Add the ability to quick move stacked PokePotion bases into Brewing Stands. 2024-03-25 00:05:19 +00:00
Slippy daae444fa9 Prehistoric Rooted Pits are more common 2024-03-23 22:51:39 -05:00
Slippy 79423ef5ee Prehistoric_Birch_Tree Fix 2024-03-23 22:13:07 -05:00
Veraxiel 6255f7dc31 Removed spawn that shouldn't be in. 2024-03-23 22:29:43 -04:00
Hiroku ff10306372 Merge branch 'GiveGimmighoulRightClick' into 'main'
Easier Gimmighoul Giving

See merge request cable-mc/cobblemon!930
2024-03-23 22:07:16 +00:00
Hiroku aaf7daa45b Merge branch 'sound' into 'main'
A lot of audio asset updates

See merge request cable-mc/cobblemon!933
2024-03-23 22:06:29 +00:00
Waldleufer ae550f78c4 Now the order of selection of input and output does not matter anymore. [skip-ci] 2024-03-23 16:19:40 +01:00
Waldleufer 66e4194a3e Made public spawn writer read Dex No. as int for consistency [skip-ci] 2024-03-23 16:04:53 +01:00
Waldleufer 12e6b76504 chore: added spawn specific drops to a bunch of cobblemon - mainly nether spawns. 2024-03-23 15:54:52 +01:00
Waldleufer 93861af71f dropping drops helps. [skip-ci] 2024-03-23 15:51:34 +01:00
Waldleufer 0c80748012 perf: script speed is over 9000! [skip-ci] 2024-03-23 15:18:30 +01:00
Waldleufer fcf832c22c more sensible script default values [skip-ci] 2024-03-23 13:17:48 +01:00
Sterrezo a065aff744 Added audio assets for fossil Pokemon cries 2024-03-23 13:04:19 +01:00
Sterrezo d77f424a83 Updated battle SFX 2024-03-23 12:49:47 +01:00
Waldleufer d5d2a2cec3 fix: improved hint for rare spreadsheet parsing bug. [skip-ci] 2024-03-23 11:55:02 +01:00
Sterrezo 036e150191 Merge branch 'main' into sound 2024-03-23 11:52:52 +01:00
Waldleufer 0bf1d84675 fix: added hint for rare spreadsheet parsing bug. [skip-ci] 2024-03-23 11:52:52 +01:00
Waldleufer b1b8e658b2 chore: updated drops data and fixed some missing form specific drops. 2024-03-23 11:42:20 +01:00
Waldleufer f984b21929 fix: Drops script now robustly handles form drops and hints at missing data in both spreadsheet and .json files [skip-ci] 2024-03-23 11:34:42 +01:00
Waldleufer 0a5193b209 perf: Increase preformance of drops script to be linear function, added dex_range option [skip-ci] 2024-03-23 10:46:59 +01:00
Veraxiel 43f1f1c30d Update to spawns to use SkyLight. 2024-03-23 04:05:56 -04:00
Waldleufer c16597ae41 chore: CHANGELOG [skip-ci] 2024-03-22 23:31:39 +01:00
Waldleufer f76e7582d2 Reworked our spawn writer script to be compatible with new UI [skip-ci] 2024-03-22 23:28:41 +01:00
Waldleufer 40761d87db Adding DexRange as input [skip-ci] 2024-03-22 20:45:19 +01:00
Waldleufer 89a9ad0bfb Refactoring the hell out of it [skip-ci] 2024-03-22 19:41:36 +01:00
Waldleufer 20291c54a5 First Version of public spawn writer script [skip-ci] 2024-03-22 19:12:13 +01:00
Hiroku d6fdfe6ebc Merge branch 'FetusResizing' into 'main'
Fossil Fetus Rescaling

See merge request cable-mc/cobblemon!927
2024-03-22 01:06:17 +00:00
Nick 9f63b62651 Merge branch '1.5-Structures' into 'main'
Hi Hiro

See merge request cable-mc/cobblemon!928
2024-03-21 16:42:25 +00:00
JazzMcNade 392632b7d4 Added the ability for a player to add to Gimighoul's treasure horde by right clicking directly on a gimmighoul that they own. 2024-03-21 00:48:59 -05:00
Slippy 0d377dcbe0 Changes, fixes, and balancing of 1.5 structures 2024-03-20 22:17:13 -05:00
JazzMcNade 88c31bcb7b Scaled Archen to0.55 (from 0.8). Scaled Kabuto to 0.85 (from 0.75) 2024-03-20 19:36:27 -05:00
Waldleufer 8efea17ae6 added structure: prefix for anticonditions and conditions and refactored script [skip-ci] 2024-03-20 21:03:37 +01:00
Waldleufer cdbf569bbc feat: made spawnwriter handle new preset: tag for spawn specific drops [skip-ci] 2024-03-20 16:26:28 +01:00
Veraxiel 0415cf8a69 Added Carnivine spawn files. 2024-03-19 04:09:58 -04:00
Sterrezo 79af092888 Merge branch 'main' into sound 2024-03-18 11:52:30 +01:00
Sterrezo 812d42a42f Merge branch 'main' into sound 2024-03-16 22:22:26 +01:00
Sterrezo 3f114706fa Implemented sounds for Revival Herb and Vivichoke. Volume adjustments.
Implemented sounds for Revival Herb and Vivichoke. Tweaked assets for Fossil Machine insert dna sounds. Adjusted volumes to Fossil Machine, Tumblestone and Coin Pouch sounds.
2024-03-16 22:21:05 +01:00
Sterrezo 9514a1d470 Fossil Machine sound updates
Updated audio assets. Moved and renamed the files for consistency. Adjusted volumes.
2024-03-12 13:31:02 +01:00
Flarkinater f99168f1c3 Pokeball items no longer drift backwards when thrown directly overhead 2024-02-24 06:33:34 -05:00
Flarkinater adce1d8838 Fixed Pokemon not appearing when sent out from shoulder 2024-02-24 05:58:35 -05:00
Flarkinater e95c82b5af Greatly improved send-out location targeting 2024-02-24 05:16:44 -05:00
Flarkinater dd273ffef2 Added beam mode for sending out Pokemon with no delay 2024-02-23 19:18:11 -05:00
Flarkinater 82548b5383 Pokémon recoil when hit by a ball 2024-02-13 22:48:27 -05:00
Flarkinater ad73202e17 Adjusted timing on breaking free animation + sound 2024-02-13 18:50:08 -05:00
Flarkinater 2965a04742 Implemented existing "break free" animation when capture fails 2024-02-12 23:02:55 -05:00
1068 changed files with 6332 additions and 2841 deletions

No files matched your search

+7 -2
View File
@@ -154,9 +154,14 @@
- Poké Ball recipes now use tags for their center ingredient, rather than specificied Copper Ingot, Iron Ingot, Gold Ingot, and Diamond, allowing for further datapack customisation.
- Starter Toast now closes once corresponding button is pressed once.
- Moved some items in the vanilla Food & Drinks category.
- Stacked Cobblemon Potion Bases can now be quick moved into Brewing Stands.
- Changed Healer recipe to swap the Max Revive for a Revive.
- Upped the rate that Gastly drop Ghast Tears from 2.5% to 5%.
- Pokémon with a low Blaze Powder drop chance now drop it more frequently in the Nether.
- Reorganised a few advancements.
### Fixes
- Resized Snorlax
- Resized Snorlax.
- Re-added the recipe for the Iron Vitamin.
- Cleaned up empty evolutions declaration in species files (no, I'm not listing all 169 by name).
- Fixed Exeggcute not being able to evolve.
@@ -188,13 +193,13 @@
- Made PokemonProperties.parse more interoperable with Java.
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
- API to support dynamic lighting in any mod that implements this feature has been added to all Species & Forms, see LightingData class for details.
- Added a Mixin for the BrewingStandScreenHandler to fix the incorrect handling of quickMove in Fabric.
- Corrections to the ability implementation have been made that make ``Ability.forced`` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
- ``Pokemon.ability`` no longer has a public setter, please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
- ``Ability.forced``, ``Ability.index`` and ``Ability.priority`` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 to 2 regular abilities and 1 hidden.
- ``Pokemon#rollAbility`` has been added, this rerolls a legal ability for the Pokémon.
- `docs/cobblemon-tags` tags and spawn-presets can now be auto-generated from the current tags in the cobblemon mod with the `generateSpawnPresetList.py` and `generateTagList.py` scripts.
- `public_spawns_to_json.py` script has been added to give the powerful spawn.json generator a simplistic UI.
### Datapack & Resourcepack Creators
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
@@ -67,24 +67,13 @@ import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFa
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.*
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.ServerTickHandler
@@ -105,13 +94,7 @@ 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
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -132,13 +115,16 @@ import org.apache.logging.log4j.Logger
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import kotlin.collections.filterIsInstance
import kotlin.collections.forEach
import kotlin.collections.listOf
import kotlin.collections.set
import kotlin.math.roundToInt
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import kotlin.math.roundToInt
object Cobblemon {
const val MODID = "cobblemon"
@@ -166,7 +152,6 @@ object Cobblemon {
var storage = PokemonStoreManager()
var molangData = NbtMoLangDataStoreFactory
lateinit var playerData: PlayerDataStoreManager
lateinit var starterConfig: StarterConfig
val dataProvider: DataProvider = CobblemonDataProvider
var permissionValidator: PermissionValidator by Delegates.observable(LaxPermissionValidator().also { it.initialize() }) { _, _, newValue -> newValue.initialize() }
var statProvider: StatProvider = CobblemonStatProvider
@@ -477,27 +462,6 @@ object Cobblemon {
this.saveConfig()
bestSpawner.loadConfig()
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
}
fun loadStarterConfig(): StarterConfig {
if (config.exportStarterConfig) {
val file = File("config/cobblemon/starters.json")
file.parentFile.mkdirs()
if (!file.exists()) {
val config = StarterConfig()
val pw = PrintWriter(file)
StarterConfig.GSON.toJson(config, pw)
pw.close()
return config
}
val reader = FileReader(file)
val config = StarterConfig.GSON.fromJson(reader, StarterConfig::class.java)
reader.close()
return config
} else {
return StarterConfig()
}
}
fun saveConfig() {
@@ -160,7 +160,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.BIG_ROOT_SOUNDS)))
@JvmField
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
@@ -428,7 +428,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
)
@JvmField
val VIVICHOKE_SEEDS = this.create("vivichoke_seeds", VivichokeBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val VIVICHOKE_SEEDS = this.create("vivichoke_seeds", VivichokeBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.VIVICHOKE_SOUNDS)))
@JvmField
val PEP_UP_FLOWER = this.create("pep_up_flower", FlowerBlock(StatusEffects.LEVITATION, 10, AbstractBlock.Settings.create().mapColor(MapColor.DARK_GREEN).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY)))
@JvmField
@@ -99,27 +99,27 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val MULCH_REMOVE = this.create("mulch.remove")
@JvmField
val FOSSIL_MACHINE_ACTIVATE = this.create("fossilmachine.activate")
val FOSSIL_MACHINE_ACTIVATE = this.create("fossil_machine.activate")
@JvmField
val FOSSIL_MACHINE_ACTIVE_LOOP = this.create("fossilmachine.active_loop")
val FOSSIL_MACHINE_ACTIVE_LOOP = this.create("fossil_machine.active_loop")
@JvmField
val FOSSIL_MACHINE_ASSEMBLE = this.create("fossilmachine.assemble")
val FOSSIL_MACHINE_ASSEMBLE = this.create("fossil_machine.assemble")
@JvmField
val FOSSIL_MACHINE_DNA_FULL = this.create("fossilmachine.dna_full")
val FOSSIL_MACHINE_DNA_FULL = this.create("fossil_machine.dna_full")
@JvmField
val FOSSIL_MACHINE_FINISHED = this.create("fossilmachine.finished")
val FOSSIL_MACHINE_FINISHED = this.create("fossil_machine.finished")
@JvmField
val FOSSIL_MACHINE_INSERT_DNA = this.create("fossilmachine.insert_dna")
val FOSSIL_MACHINE_INSERT_DNA = this.create("fossil_machine.insert_dna")
@JvmField
val FOSSIL_MACHINE_INSERT_DNA_SMALL = this.create("fossilmachine.insert_dna_small")
val FOSSIL_MACHINE_INSERT_DNA_SMALL = this.create("fossil_machine.insert_dna_small")
@JvmField
val FOSSIL_MACHINE_INSERT_FOSSIL = this.create("fossilmachine.insert_fossil")
val FOSSIL_MACHINE_INSERT_FOSSIL = this.create("fossil_machine.insert_fossil")
@JvmField
val FOSSIL_MACHINE_RETRIEVE_FOSSIL = this.create("fossilmachine.retrieve_fossil")
val FOSSIL_MACHINE_RETRIEVE_FOSSIL = this.create("fossil_machine.retrieve_fossil")
@JvmField
val FOSSIL_MACHINE_RETRIEVE_POKEMON = this.create("fossilmachine.retrieve_pokemon")
val FOSSIL_MACHINE_RETRIEVE_POKEMON = this.create("fossil_machine.retrieve_pokemon")
@JvmField
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossilmachine.unprotected")
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossil_machine.unprotected")
@JvmField
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
@@ -155,11 +155,21 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val ENERGY_ROOT_PLACE = this.create("energy_root.place")
@JvmField
val VIVICHOKE_BREAK = this.create("vivichoke.break")
@JvmField
val VIVICHOKE_PLACE = this.create("vivichoke.place")
@JvmField
val MINT_BREAK = this.create("mint.break")
@JvmField
val MINT_PLACE = this.create("mint.place")
@JvmField
val REVIVAL_HERB_BREAK = this.create("revival_herb.break")
@JvmField
val REVIVAL_HERB_PLACE = this.create("revival_herb.place")
@JvmField
val MEDICINAL_LEEK_BREAK = this.create("medicinal_leek.break")
@JvmField
@@ -277,6 +287,15 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val VIVICHOKE_SOUNDS = BlockSoundGroup(1f, 1f,
VIVICHOKE_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
VIVICHOKE_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val MINT_SOUNDS = BlockSoundGroup(1f, 1f,
MINT_BREAK,
@@ -286,6 +305,15 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val REVIVAL_HERB_SOUNDS = BlockSoundGroup(1f, 1f,
REVIVAL_HERB_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
REVIVAL_HERB_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val GILDED_CHEST_SOUNDS = BlockSoundGroup(1f, 1f,
GILDED_CHEST_BREAK,
@@ -35,8 +35,8 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
itemStack: ItemStack?
) {
if (world is ServerWorld) {
val multiblockEntity = world.getBlockEntity(pos) as MultiblockEntity
multiblockEntity.multiblockBuilder?.validate(world)
val multiblockEntity = world.getBlockEntity(pos) as? MultiblockEntity
multiblockEntity?.multiblockBuilder?.validate(world)
}
super.onPlaced(world, pos, state, placer, itemStack)
}
@@ -52,8 +52,8 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
if(hand == Hand.OFF_HAND) {
return ActionResult.SUCCESS
}
val entity = world.getBlockEntity(pos) as MultiblockEntity
if (entity.multiblockStructure != null) {
val entity = world.getBlockEntity(pos) as MultiblockEntity?
if (entity?.multiblockStructure != null) {
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
}
return super.onUse(state, world, pos, player, hand, hit)
@@ -397,7 +397,7 @@ open class PokemonProperties {
return false
}
}
nickname?.takeIf { it != pokemon.nickname }?.let { return false }
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
@@ -437,7 +437,7 @@ open class PokemonProperties {
}
} catch (_: InvalidIdentifierException) {}
}
nickname?.takeIf { it != properties.nickname }?.let { return false }
nickname?.takeIf { it.string != properties.nickname?.string }?.let { return false }
form?.takeIf { !it.equals(properties.form, true) }?.let { return false }
friendship?.takeIf { it != properties.friendship }?.let { return false }
pokeball?.takeIf { it != properties.pokeball }?.let { return false }
@@ -12,6 +12,8 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
@@ -34,7 +36,7 @@ object CobblemonWorldSpawnerManager : SpawnerManager() {
fun onPlayerLogin(player: ServerPlayerEntity) {
// Disables spawning
if (!Cobblemon.config.enableSpawning) {
if (!Cobblemon.config.enableSpawning || server()?.gameRules?.getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING) == false) {
return;
}
@@ -12,6 +12,8 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
/**
* A manager of various spawners. This is a class in which you should register
@@ -49,7 +51,7 @@ open class SpawnerManager {
open fun onServerTick() {
// Disables spawning
if (!Cobblemon.config.enableSpawning) {
if (!Cobblemon.config.enableSpawning || server()?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
return
}
influences.removeIf { it.isExpired() }
@@ -63,9 +63,6 @@ abstract class AreaSpawner(
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
val area = getArea(cause)
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
return null
}
val constrainedArea = if (area != null) constrainArea(area) else null
if (constrainedArea != null) {
@@ -6,24 +6,19 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.config.starter
package com.cobblemon.mod.common.api.starter
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asTranslated
import net.minecraft.text.Text
data class StarterCategory(
val name: String,
val displayName: String,
val displayName: Text,
val pokemon: List<PokemonProperties>
) {
fun asRenderableStarterCategory() = RenderableStarterCategory(name, displayName, pokemon.map { it.asRenderablePokemon() })
}
)
data class RenderableStarterCategory(
val name: String,
val displayName: String,
val displayName: Text,
val pokemon: List<RenderablePokemon>
) {
val displayNameText = displayName.asTranslated()
}
)
@@ -8,12 +8,12 @@
package com.cobblemon.mod.common.api.starter
import com.cobblemon.mod.common.config.starter.StarterCategory
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
interface StarterHandler {
fun getStarterList(player: ServerPlayerEntity): List<StarterCategory>
fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory>
fun handleJoin(player: ServerPlayerEntity)
fun requestStarterChoice(player: ServerPlayerEntity)
fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int)
fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int)
}
@@ -0,0 +1,69 @@
/*
* 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.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.util.adapters.PlainJsonTextAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* The registry for starter Pokémon.
*
* The data here is used by the [CobblemonStarterHandler].
*
* 3rd party is not enforced to respect this registry.
*/
object StarterRegistry : JsonDataRegistry<StarterCategory> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(Text::class.java, PlainJsonTextAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.create()
override val typeToken: TypeToken<StarterCategory> = TypeToken.get(StarterCategory::class.java)
override val id: Identifier = cobblemonResource("starter")
override val resourcePath: String = id.path
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable: SimpleObservable<out DataRegistry> = SimpleObservable()
private val categories = hashMapOf<Identifier, StarterCategory>()
// We let the default starter handler sync the client
// This is to prevent unnecessary packets depending on impl
override fun sync(player: ServerPlayerEntity) {}
override fun reload(data: Map<Identifier, StarterCategory>) {
this.categories.clear()
this.categories += data
Cobblemon.LOGGER.info("Loaded {} starters ({} categories)", this.categories.values.sumOf { it.pokemon.size }, this.categories.size)
}
fun all(): Map<Identifier, StarterCategory> = this.categories
fun keys(): Set<Identifier> = this.categories.keys
fun categories(): Collection<StarterCategory> = this.categories.values
fun categoryOf(key: Identifier): StarterCategory? = this.categories[key]
fun categoryOf(key: String): StarterCategory? = this.categories[key.asIdentifierDefaultingNamespace()]
}
@@ -37,7 +37,7 @@ data class PlayerData(
fun defaultData(forPlayer: UUID) = PlayerData(
uuid = forPlayer,
starterPrompted = false,
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
starterLocked = !Cobblemon.config.allowStarterOnJoin,
starterSelected = false,
starterUUID = null,
keyItems = mutableSetOf(),
@@ -62,8 +62,8 @@ class MonitorBlock(properties: Settings) : MultiblockBlock(properties) {
if(world == null || pos == null) {
return 0
}
val monitorEntity = world.getBlockEntity(pos) as MultiblockEntity
val multiBlockEntity = monitorEntity.multiblockStructure
val monitorEntity = world.getBlockEntity(pos) as? MultiblockEntity
val multiBlockEntity = monitorEntity?.multiblockStructure
if(multiBlockEntity != null) {
return multiBlockEntity.getComparatorOutput(state, world, pos)
}
@@ -149,8 +149,8 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
if(world == null || pos == null) {
return 0
}
val tankEntity = world.getBlockEntity(pos) as MultiblockEntity
val multiBlockEntity = tankEntity.multiblockStructure
val tankEntity = world.getBlockEntity(pos) as? MultiblockEntity
val multiBlockEntity = tankEntity?.multiblockStructure
if(multiBlockEntity != null) {
return multiBlockEntity.getComparatorOutput(state, world, pos)
}
@@ -180,7 +180,7 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
return
}
val tankEntity = world.getBlockEntity(pos) as? MultiblockEntity
tankEntity?.multiblockStructure!!.onTriggerEvent(state, world, pos, random)
tankEntity?.multiblockStructure?.onTriggerEvent(state, world, pos, random)
}
override fun getOutlineShape(
@@ -9,15 +9,23 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.chest.GildedChestBlock
import net.minecraft.block.*
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemPlacementContext
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.Properties
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.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldView
@@ -28,7 +36,18 @@ class TumblestoneBlock(
height: Int,
xzOffset: Int,
nextStage: Block?
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage), Waterloggable {
init {
this.defaultState = this.stateManager.defaultState
.with(FACING, Direction.DOWN)
.with(WATERLOGGED, false)
}
companion object {
val WATERLOGGED = BooleanProperty.of("waterlogged")
}
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
if (stage == MAX_STAGE) return false
val iterator: Iterator<BlockPos> =
@@ -43,4 +62,31 @@ class TumblestoneBlock(
return true
}
override fun getFluidState(state: BlockState): FluidState {
return if (state.get(GildedChestBlock.WATERLOGGED)) {
Fluids.WATER.getStill(false)
} else super.getFluidState(state)
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder)
builder.add(GildedChestBlock.WATERLOGGED)
}
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
return super.getPlacementState(blockPlaceContext)?.with(GildedChestBlock.WATERLOGGED, blockPlaceContext.world.getFluidState(blockPlaceContext.blockPos).fluid == Fluids.WATER)
}
override fun getStateForNeighborUpdate(
state: BlockState,
direction: Direction,
neighborState: BlockState,
world: WorldAccess,
pos: BlockPos,
neighborPos: BlockPos
): BlockState? {
if (state.get(GildedChestBlock.WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
}
@@ -16,20 +16,15 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.Blocks
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.ShapeContext
import net.minecraft.block.Waterloggable
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.mob.PiglinBrain
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
@@ -38,12 +33,7 @@ import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.Properties
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.ItemScatterer
import net.minecraft.util.*
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
@@ -106,7 +96,7 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
neighborPos: BlockPos
): BlockState {
if (state.get(WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
override fun getFluidState(state: BlockState): FluidState {
@@ -258,4 +248,17 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
return false
}
override fun onPlaced(
world: World,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
itemStack: ItemStack
) {
val blockEntity = world.getBlockEntity(pos)
if (itemStack.hasCustomName() && blockEntity is GildedChestBlockEntity) {
blockEntity.customName = itemStack.name
}
}
}
@@ -42,7 +42,7 @@ open class FossilMultiblockEntity(
field
} else if (masterBlockPos != null && masterBlockPos != pos) {
val chunkPos = ChunkPos(masterBlockPos)
if (world?.chunkManager?.isChunkLoaded(chunkPos.x, chunkPos.z) == true) {
if (world?.chunkManager?.isChunkLoaded(chunkPos.x, chunkPos.z) == true) {
val entity: FossilMultiblockEntity? = world?.getBlockEntity(masterBlockPos) as FossilMultiblockEntity?
field = entity?.multiblockStructure
}
@@ -140,7 +140,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, val state: BlockState) : BlockEnt
if (fixedPosition != null) {
entity.setPosition(fixedPosition.toCenterPos().subtract(0.0, 0.5, 0.0))
val pc = Cobblemon.storage.getPC(player.uuid)
entity.beamMode = 1
entity.beamMode = 2
afterOnServer(seconds = SendOutPokemonHandler.SEND_OUT_DURATION) {
entity.beamMode = 0
}
@@ -117,10 +117,10 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
val monitorPos = monitorPositions[fossilTankIndex]
val fossilAnalyzerPos = fossilAnalyzerPositions[fossilTankIndex]!!
val restorationTankPos = restorationTankPositions[fossilTankIndex]!!
val monitorEntity = world.getBlockEntity(monitorPos) as MultiblockEntity
val analyzerEntity = world.getBlockEntity(fossilAnalyzerPos) as MultiblockEntity
val tankBaseEntity = world.getBlockEntity(restorationTankPos) as MultiblockEntity
val tankTopEntity = world.getBlockEntity(restorationTankPos.up()) as MultiblockEntity
val monitorEntity = world.getBlockEntity(monitorPos) as? MultiblockEntity
val analyzerEntity = world.getBlockEntity(fossilAnalyzerPos) as? MultiblockEntity
val tankBaseEntity = world.getBlockEntity(restorationTankPos) as? MultiblockEntity
val tankTopEntity = world.getBlockEntity(restorationTankPos.up()) as? MultiblockEntity
val structure = FossilMultiblockStructure(monitorPos, fossilAnalyzerPos, restorationTankPos)
structure.tankConnectorDirection = dirsToCheck.filter {
@@ -128,20 +128,20 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
return@filter adjPos == fossilAnalyzerPos
}.first()
analyzerEntity.multiblockStructure = structure
tankBaseEntity.multiblockStructure = structure
tankTopEntity.multiblockStructure = structure
monitorEntity.multiblockStructure = structure
analyzerEntity?.multiblockStructure = structure
tankBaseEntity?.multiblockStructure = structure
tankTopEntity?.multiblockStructure = structure
monitorEntity?.multiblockStructure = structure
structure.syncToClient(world)
structure.markDirty(world)
world.playSound(null, centerPos, CobblemonSounds.FOSSIL_MACHINE_ASSEMBLE, SoundCategory.BLOCKS)
//Set these to null so the builders can be freed
analyzerEntity.multiblockBuilder = null
tankBaseEntity.multiblockBuilder = null
tankTopEntity.multiblockBuilder = null
monitorEntity.multiblockBuilder = null
analyzerEntity?.multiblockBuilder = null
tankBaseEntity?.multiblockBuilder = null
tankTopEntity?.multiblockBuilder = null
monitorEntity?.multiblockBuilder = null
}
companion object {
@@ -306,22 +306,22 @@ class FossilMultiblockStructure (
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
val monitorEntity = world.getBlockEntity(monitorPos) as MultiblockEntity
val analyzerEntity = world.getBlockEntity(analyzerPos) as MultiblockEntity
val tankBaseEntity = world.getBlockEntity(tankBasePos) as MultiblockEntity
val tankTopEntity = world.getBlockEntity(tankBasePos.up()) as MultiblockEntity
val state = world.getBlockState(tankBaseEntity.pos)
val monitorEntity = world.getBlockEntity(monitorPos) as? MultiblockEntity
val analyzerEntity = world.getBlockEntity(analyzerPos) as? MultiblockEntity
val tankBaseEntity = world.getBlockEntity(tankBasePos) as? MultiblockEntity
val tankTopEntity = world.getBlockEntity(tankBasePos.up()) as? MultiblockEntity
val state = world.getBlockState(tankBaseEntity?.pos)
val direction = state.get(HorizontalFacingBlock.FACING).getOpposite()
val wildPokemon: Pokemon? = this.createdPokemon
monitorEntity.multiblockStructure = null
analyzerEntity.multiblockStructure = null
tankBaseEntity.multiblockStructure = null
tankTopEntity.multiblockStructure = null
monitorEntity.masterBlockPos = null
analyzerEntity.masterBlockPos = null
tankBaseEntity.masterBlockPos = null
tankTopEntity.masterBlockPos = null
monitorEntity?.multiblockStructure = null
analyzerEntity?.multiblockStructure = null
tankBaseEntity?.multiblockStructure = null
tankTopEntity?.multiblockStructure = null
monitorEntity?.masterBlockPos = null
analyzerEntity?.masterBlockPos = null
tankBaseEntity?.masterBlockPos = null
tankTopEntity?.masterBlockPos = null
// Drop fossils from machine as long as the machine is not started or near completion
if (this.timeRemaining == -1 || this.timeRemaining >= 20) {
@@ -619,8 +619,8 @@ class FossilMultiblockStructure (
}
const val TICKS_PER_MINUTE = 1200
const val MATERIAL_TO_START = 256
const val TIME_TO_TAKE = TICKS_PER_MINUTE * 15
const val MATERIAL_TO_START = 128
const val TIME_TO_TAKE = TICKS_PER_MINUTE * 12
const val TIME_PER_STAGE = TIME_TO_TAKE / 8
const val PROTECTION_TIME = TICKS_PER_MINUTE * 5
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.entity
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
@@ -22,7 +21,6 @@ import com.cobblemon.mod.common.api.scheduling.lerpOnClient
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatefulAnimation
@@ -34,11 +32,8 @@ import com.cobblemon.mod.common.util.MovingSoundInstance
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.math.MatrixStack
import java.lang.Float.min
import kotlin.math.abs
import net.minecraft.entity.Entity
import net.minecraft.entity.data.TrackedData
import net.minecraft.particle.ParticleTypes
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvent
import net.minecraft.util.Hand
@@ -105,6 +100,8 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
this.addPrimaryAnimation(primaryAnimation)
}
} else if (data == PokemonEntity.BEAM_MODE) {
// If you make adjustments to this code, make sure to Find Usages for both PokemonEntity.beamMode and PokemonEntity.BEAM_MODE
// TODO: change beamMode to an enum, or a set of booleans (send-out vs recall, delay vs delay)
val beamMode = currentEntity.beamMode
when (beamMode) {
0 -> { /* Do nothing */ }
@@ -141,39 +138,37 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
currentEntity.after(seconds = POKEBALL_AIR_TIME){
beamStartTime = System.currentTimeMillis()
ballDone = true
if (client.soundManager.get(CobblemonSounds.POKE_BALL_OPEN.id) != null && !playedSendOutSound) {
currentEntity.owner?.let {
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
/// create end rod particles in a 0.1 radius around the soundPos with a count of 50 and a random velocity of 0.1
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.toLowerCase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
if (client.soundManager.get(CobblemonSounds.POKE_BALL_SEND_OUT.id) != null && !playedSendOutSound) {
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
playedSendOutSound = true
}
// Display ball-specific particles
sendOutPosition?.let{
val newPos = it.add(sendOutOffset)
val ballType = currentEntity.pokemon.caughtBall.name.path.lowercase().replace("_","")
val mode = if(currentEntity.isBattling) "battle" else "casual"
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
sendflash?.let { effect ->
val wrapper = MatrixWrapper()
val matrix = MatrixStack()
matrix.translate(newPos.x, newPos.y, newPos.z)
wrapper.updateMatrix(matrix.peek().positionMatrix)
val world = MinecraftClient.getInstance().world ?: return@let
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
afterOnClient(seconds = 0.01667f) {
ballsparks?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
ballsendsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
}
}
ballsendsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
currentEntity.after(seconds = 0.4f) {
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
ballsparkle?.let { effect ->
ParticleStorm(effect, wrapper, world).spawn()
}
}
}
@@ -190,7 +185,35 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
}
}
}
else -> {
2 -> {
// Scaling up with no delay and no particles
playedSendOutSound = false
entityScaleModifier = 0F
currentEntity.isInvisible = false
val soundPos = currentEntity.pos
val client = MinecraftClient.getInstance()
if (client.soundManager.get(CobblemonSounds.POKE_BALL_SEND_OUT.id) != null && !playedSendOutSound) {
client.world?.playSound(
client.player,
soundPos.x,
soundPos.y,
soundPos.z,
CobblemonSounds.POKE_BALL_SEND_OUT,
SoundCategory.PLAYERS,
0.6f,
1f
)
playedSendOutSound = true
}
lerpOnClient(BEAM_SHRINK_TIME) { entityScaleModifier = it }
currentEntity.after(seconds = BEAM_SHRINK_TIME * 2) {
ballOffset = 0f
ballRotOffset = 0f
sendOutPosition = null
}
}
3 -> {
// Scaling down into pokeball
entityScaleModifier = 1F
beamStartTime = System.currentTimeMillis()
@@ -203,6 +226,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
}
}
}
else -> { /* Do nothing */ }
}
} else if (data == PokemonEntity.LABEL_LEVEL) {
currentEntity.dataTracker.get(PokemonEntity.LABEL_LEVEL)
@@ -27,7 +27,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.type.DualTypeWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.type.SingleTypeWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.type.TypeWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asTranslated
@@ -40,6 +40,7 @@ import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.toast.Toast
import net.minecraft.text.Text
import net.minecraft.util.Identifier
/**
* Starterselection Screen Thingy
@@ -47,7 +48,7 @@ import net.minecraft.text.Text
* @author Qu
* @since 2022-06-18
*/
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
companion object {
// Size of UI at scale 1
@@ -64,8 +65,9 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
private val doubleTypeBackground = cobblemonResource("textures/gui/starterselection/starterselection_type_slot2.png")
}
private val categories = categories.toSortedMap { a, b -> a.compareTo(b) }
private var currentSelection = 0
private lateinit var currentCategory: RenderableStarterCategory
private lateinit var currentCategoryId: Identifier
private lateinit var modelWidget: ModelWidget
private lateinit var currentPokemon: RenderablePokemon
private lateinit var typeWidget: TypeWidget
@@ -95,7 +97,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
paneWidth = 71, paneHeight = BASE_HEIGHT - 11,
topOffset = 6, bottomOffset = 5,
entryHeight = 20, entryWidth = 57,
categories = categories,
categories = this.categories,
x = x - 2, y = y + 8,
starterSelectionScreen = this
)
@@ -120,8 +122,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
addDrawableChild(rightButton)
addDrawableChild(leftButton)
currentCategory = categories.first()
currentPokemon = currentCategory.pokemon[currentSelection]
currentCategoryId = categories.firstKey()
currentPokemon = this.currentCategory().pokemon[currentSelection]
with(currentPokemon) {
modelWidget = ModelWidget(
@@ -142,11 +144,11 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
) {
CobblemonNetwork.sendPacketToServer(
SelectStarterPacket(
categoryName = currentCategory.name,
categoryId = currentCategoryId,
selected = currentSelection
)
)
MinecraftClient.getInstance().setScreen(null)
this.close()
}
addDrawableChild(selectionButton)
@@ -161,7 +163,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
starterRoundaboutLeft = StarterRoundabout(
pX = x + 89, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[leftOfCurrentSelection()],
pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()],
clickAction = { _, _ -> this.left() },
rotationVector = this.modelWidget.rotVec
)
@@ -169,7 +171,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
starterRoundaboutRight = StarterRoundabout(
pX = x + 149, pY = height / 2 + 84,
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
pokemon = currentCategory.pokemon[rightOfCurrentSelection()],
pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()],
clickAction = { _, _ -> this.right() },
rotationVector = this.modelWidget.rotVec
)
@@ -276,8 +278,8 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
super.render(context, mouseX, mouseY, delta)
}
fun changeCategory(category: RenderableStarterCategory) {
currentCategory = category
fun changeCategory(id: Identifier) {
currentCategoryId = id
currentSelection = 0
updateSelection()
}
@@ -288,7 +290,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
updateSelection()
}
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= currentCategory.pokemon.size - 1) currentSelection + 1 else 0
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= this.currentCategory().pokemon.size - 1) currentSelection + 1 else 0
private fun left() {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
@@ -296,16 +298,16 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
updateSelection()
}
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) currentCategory.pokemon.size - 1 else currentSelection - 1
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) this.currentCategory().pokemon.size - 1 else currentSelection - 1
private fun updateSelection() {
currentPokemon = currentCategory.pokemon[currentSelection].also {
currentPokemon = this.currentCategory().pokemon[currentSelection].also {
modelWidget.pokemon = it
typeWidget = typeWidget(it, (width - BASE_WIDTH) / 2, (height - BASE_HEIGHT) / 2)
}
starterRoundaboutLeft.pokemon = currentCategory.pokemon[leftOfCurrentSelection()]
starterRoundaboutLeft.pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()]
starterRoundaboutCenter.pokemon = currentPokemon
starterRoundaboutRight.pokemon = currentCategory.pokemon[rightOfCurrentSelection()]
starterRoundaboutRight.pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()]
}
private fun typeWidget(pokemon: RenderablePokemon, x: Int, y: Int) : TypeWidget {
@@ -324,5 +326,7 @@ class StarterSelectionScreen(private val categories: List<RenderableStarterCateg
)
}
private fun currentCategory(): RenderableStarterCategory = this.categories[this.currentCategoryId]!!
override fun shouldPause() = true
}
@@ -12,14 +12,14 @@ import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.startselection.StarterSelectionScreen
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.Identifier
class CategoryList(
private val paneWidth: Int,
@@ -28,7 +28,7 @@ class CategoryList(
bottomOffset: Int,
private val entryWidth: Int,
entryHeight: Int,
private val categories: List<RenderableStarterCategory>,
private val categories: Map<Identifier, RenderableStarterCategory>,
val x: Int, val y: Int,
private val minecraft: MinecraftClient = MinecraftClient.getInstance(),
private val starterSelectionScreen: StarterSelectionScreen
@@ -44,7 +44,6 @@ class CategoryList(
companion object {
private const val CATEGORY_BUTTON_WIDTH = 51.5f
private const val CATEGORY_BUTTON_HEIGHT = 16f
private const val ENTRY_X_OFFSET = 10f
private val categoryResource = cobblemonResource("textures/gui/starterselection/starterselection_slot.png")
}
@@ -53,19 +52,12 @@ class CategoryList(
this.setRenderHorizontalShadows(false)
this.setRenderBackground(false)
this.setRenderSelection(false)
}
private var entriesCreated = false
private fun createEntries() = categories.map {
Category(it)
this.categories.forEach { (id, category) ->
this.addEntry(Category(id, category))
}
}
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
if (!entriesCreated) {
createEntries().forEach { addEntry(it) }
entriesCreated = true
}
context.enableScissor(
x,
y,
@@ -80,15 +72,13 @@ class CategoryList(
this.updateSize(this.paneWidth, this.paneHeight, this.y, this.y + this.paneHeight)
this.setLeftPos(this.x)
}
private fun scale(n: Int): Int = (this.client.window.scaleFactor * n).toInt()
override fun getRowWidth() = this.entryWidth
override fun getScrollbarPositionX(): Int {
return this.left + this.width - 5
}
inner class Category(private val category: RenderableStarterCategory) : Entry<Category>() {
inner class Category(private val id: Identifier, private val category: RenderableStarterCategory) : Entry<Category>() {
override fun render(
context: DrawContext,
@@ -121,7 +111,7 @@ class CategoryList(
)
drawScaledText(
context = context,
text = category.displayNameText,
text = category.displayName.copy(),
x = x + 28,
y = y + 4.5F,
scale = 1F,
@@ -132,7 +122,7 @@ class CategoryList(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
starterSelectionScreen.changeCategory(category = category)
starterSelectionScreen.changeCategory(this.id)
minecraft.soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
return true
}
@@ -72,7 +72,7 @@ class RestorationTankRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEn
val fluidModel = if (struct.isRunning()) FLUID_MODELS[8]
else if (struct.createdPokemon != null) FLUID_MODELS[7]
else FLUID_MODELS[fillLevel-1]
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)
}
@@ -67,6 +67,11 @@ abstract class PokeBallPoseableState : PoseableEntityState<EmptyPokeBallEntity>(
setStatefulAnimations(currentModel!!.bedrockStateful(group, "critical"))
}
}
EmptyPokeBallEntity.CaptureState.BROKEN_FREE -> {
doLater {
setStatefulAnimations(currentModel!!.bedrockStateful(group, "break"))
}
}
else -> {}
}
}
@@ -32,7 +32,6 @@ import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.DoubleRange
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import kotlin.math.*
import net.minecraft.client.MinecraftClient
import net.minecraft.client.font.TextRenderer
import net.minecraft.client.render.*
@@ -53,6 +52,7 @@ import org.joml.Math
import org.joml.Quaternionf
import org.joml.Vector3f
import org.joml.Vector4f
import kotlin.math.*
class PokemonRenderer(
context: EntityRendererFactory.Context
@@ -60,7 +60,7 @@ class PokemonRenderer(
companion object {
val recallBeamColour = Vector4f(1F, 0.1F, 0.1F, 1F)
fun ease(x: Double): Double {
return 1 - (1 - x).pow(3);
return 1 - (1 - x).pow(3)
}
}
@@ -86,7 +86,7 @@ class PokemonRenderer(
if (entity.beamMode != 0) {
renderTransition(
modelNow,
entity.beamMode == 1,
entity.beamMode,
entity,
partialTicks,
poseMatrix,
@@ -130,7 +130,7 @@ class PokemonRenderer(
fun renderTransition(
modelNow: PoseableEntityModel<PokemonEntity>,
sendingOut: Boolean,
beamMode: Int,
entity: PokemonEntity,
partialTicks: Float,
poseMatrix: MatrixStack,
@@ -139,7 +139,7 @@ class PokemonRenderer(
clientDelegate: PokemonClientDelegate
) {
val s = clientDelegate.secondsSinceBeamEffectStarted
if (modelNow is PokemonPoseableModel && !sendingOut) {
if (modelNow is PokemonPoseableModel && beamMode == 3) {
if (s > BEAM_EXTEND_TIME) {
val value = (s - BEAM_EXTEND_TIME) / BEAM_SHRINK_TIME
val colourValue = 1F - min(0.6F, value)
@@ -163,9 +163,9 @@ class PokemonRenderer(
}
}
if (clientDelegate.sendOutPosition == null && sendingOut) {
if (clientDelegate.sendOutPosition == null && beamMode == 1) {
clientDelegate.sendOutPosition = beamSourcePosition
} else if (sendingOut) {
} else if (beamMode == 1) {
clientDelegate.sendOutPosition = clientDelegate.sendOutPosition!!.add(0.0, 0.04, 0.0)
beamSourcePosition = clientDelegate.sendOutPosition!!
}
@@ -184,9 +184,9 @@ class PokemonRenderer(
poseMatrix.multiply(RotationAxis.POSITIVE_Y.rotation(-angle.toFloat() + (180 * Math.PI / 180).toFloat()))
// TODO: if you want to remove the open ball, add `!clientDelegate.ballDone` to the if statement
if (sendingOut && !clientDelegate.ballDone){
if (beamMode == 1 && !clientDelegate.ballDone){
if(entity.pokemon.caughtBall.name.toString().contains("beast")){
// get rotation angle on x acis for facingDir
// get rotation angle on x-axis for facingDir
val xAngleFacingDir = MathHelper.atan2(facingDir.y, sqrt(facingDir.x * facingDir.x + facingDir.z * facingDir.z))
poseMatrix.multiply(RotationAxis.POSITIVE_X.rotation(-xAngleFacingDir.toFloat()))
}
@@ -203,7 +203,7 @@ class PokemonRenderer(
)
}
poseMatrix.pop()
if (!sendingOut) {
if (beamMode == 3) {
renderBeam(poseMatrix, partialTicks, entity, phaseTarget, buffer, offsetDirection)
}
}
@@ -9,11 +9,7 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command.SINGLE_SUCCESS
import com.mojang.brigadier.CommandDispatcher
@@ -38,18 +34,7 @@ object OpenStarterScreenCommand {
private fun execute(context: CommandContext<ServerCommandSource>) : Int {
val player = EntityArgumentType.getPlayer(context, "player")
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
context.source.sendFeedback({ lang("ui.starter.hasalreadychosen", player.name).red() }, true)
return 0
}
if (playerData.starterLocked) {
playerData.starterLocked = false
playerData.sendToPlayer(player)
}
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
player.sendPacket(OpenStarterUIPacket(Cobblemon.starterHandler.getStarterList(player)))
Cobblemon.starterHandler.requestStarterChoice(player)
return SINGLE_SUCCESS
}
@@ -113,6 +113,12 @@ class CobblemonConfig {
@NodeCategory(Category.Starter)
var exportStarterConfig = false
@NodeCategory(Category.Starter)
var allowStarterOnJoin = true
@NodeCategory(Category.Starter)
var promptStarterOnceOnly = true
@NodeCategory(Category.Battles)
var autoUpdateShowdown = true
@@ -1,128 +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.config.starter
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.config.Category
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.NodeCategory
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.google.gson.GsonBuilder
class StarterConfig {
companion object {
val GSON = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.create()
}
@NodeCategory(Category.Starter)
var allowStarterOnJoin = true
@NodeCategory(Category.Starter)
@LastChangedVersion("1.5.0")
var promptStarterOnceOnly = true
@NodeCategory(Category.Starter)
var starters = mutableListOf(
StarterCategory(
name = "Kanto",
displayName = "cobblemon.starterselection.category.kanto",
pokemon = mutableListOf(
PokemonProperties.parse("Bulbasaur level=10"),
PokemonProperties.parse("Charmander level=10"),
PokemonProperties.parse("Squirtle level=10"),
)
),
StarterCategory(
name = "Johto",
displayName = "cobblemon.starterselection.category.johto",
pokemon = mutableListOf(
PokemonProperties.parse("Chikorita level=10"),
PokemonProperties.parse("Cyndaquil level=10"),
PokemonProperties.parse("Totodile level=10"),
)
),
StarterCategory(
name = "Hoenn",
displayName = "cobblemon.starterselection.category.hoenn",
pokemon = mutableListOf(
PokemonProperties.parse("Treecko level=10"),
PokemonProperties.parse("Torchic level=10"),
PokemonProperties.parse("Mudkip level=10"),
)
),
StarterCategory(
name = "Sinnoh",
displayName = "cobblemon.starterselection.category.sinnoh",
pokemon = mutableListOf(
PokemonProperties.parse("Turtwig level=10"),
PokemonProperties.parse("Chimchar level=10"),
PokemonProperties.parse("Piplup level=10"),
)
),
StarterCategory(
name = "Unova",
displayName = "cobblemon.starterselection.category.unova",
pokemon = mutableListOf(
PokemonProperties.parse("Snivy level=10"),
PokemonProperties.parse("Tepig level=10"),
PokemonProperties.parse("Oshawott level=10"),
)
),
StarterCategory(
name = "Kalos",
displayName = "cobblemon.starterselection.category.kalos",
pokemon = mutableListOf(
PokemonProperties.parse("Chespin level=10"),
PokemonProperties.parse("Fennekin level=10"),
PokemonProperties.parse("Froakie level=10"),
)
),
StarterCategory(
name = "Alola",
displayName = "cobblemon.starterselection.category.alola",
pokemon = mutableListOf(
PokemonProperties.parse("Rowlet level=10"),
PokemonProperties.parse("Litten level=10"),
PokemonProperties.parse("Popplio level=10"),
)
),
StarterCategory(
name = "Galar",
displayName = "cobblemon.starterselection.category.galar",
pokemon = mutableListOf(
PokemonProperties.parse("Grookey level=10"),
PokemonProperties.parse("Scorbunny level=10"),
PokemonProperties.parse("Sobble level=10"),
)
),
StarterCategory(
name = "Paldea",
displayName = "cobblemon.starterselection.category.paldea",
pokemon = mutableListOf(
PokemonProperties.parse("Sprigatito level=10"),
PokemonProperties.parse("Fuecoco level=10"),
PokemonProperties.parse("Quaxly level=10"),
)
)
//,
// StarterCategory(
// name = "Johto",
// displayName = lang("starterselection.category.johto"),
// pokemon = mutableListOf(
// PokemonProperties().also { it.level = 5 ; it.species = "Chikorita" },
// PokemonProperties().also { it.level = 5 ; it.species = "Cyndaquil" },
// PokemonProperties().also { it.level = 5 ; it.species = "Totodile" }
// )
// )
)
}
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.api.starter.StarterRegistry
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.platform.events.PlatformEvents
@@ -63,6 +64,7 @@ object CobblemonDataProvider : DataProvider {
this.register(Abilities)
this.register(PokemonSpecies)
this.register(SpeciesAdditions)
this.register(StarterRegistry)
this.register(PokeBalls)
this.register(PropertiesCompletionProvider)
this.register(SpawnDetailPresets)
@@ -21,9 +21,8 @@ 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.status.Statuses
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.text.red
@@ -37,17 +36,12 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.Poseable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonServerDelegate
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleCaptureStartPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.sendParticlesServer
import com.cobblemon.mod.common.util.setPositionSafely
import com.cobblemon.mod.common.util.update
import java.util.concurrent.CompletableFuture
import com.cobblemon.mod.common.util.*
import net.minecraft.entity.EntityDimensions
import net.minecraft.entity.EntityPose
import net.minecraft.entity.EntityType
@@ -70,6 +64,7 @@ import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.MathHelper.PI
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import java.util.concurrent.CompletableFuture
class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Schedulable {
enum class CaptureState {
@@ -78,7 +73,8 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
FALL,
SHAKE,
CAPTURED,
CAPTURED_CRITICAL
CAPTURED_CRITICAL,
BROKEN_FREE
}
companion object {
@@ -135,6 +131,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
CaptureState.FALL -> setNoGravity(false)
CaptureState.SHAKE -> setNoGravity(true)
CaptureState.CAPTURED, CaptureState.CAPTURED_CRITICAL -> {}
CaptureState.BROKEN_FREE -> {}
}
}
dataTrackerEmitter.emit(data)
@@ -348,25 +345,27 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_SHAKE, volume = 0.8F)
// Emits a shake by changing the value to the opposite of what it currently is. Sends an update to the client basically.
// We could replace this with a packet but it feels awfully excessive when we already have 5 bajillion packets.
// We could replace this with a packet, but it feels awfully excessive when we already have 5 bajillion packets.
dataTracker.update(SHAKE) { !it }
}
private fun breakFree() {
val pokemon = capturingPokemon ?: return
pokemon.setPosition(pos)
pokemon.beamMode = 1
pokemon.beamMode = 2
pokemon.isInvisible = false
if (pokemon.battleId == null) {
pokemon.pokemon.status?.takeIf { it.status == Statuses.SLEEP }?.let { pokemon.pokemon.status = null }
}
after(seconds = 0.25F) {
captureState = CaptureState.BROKEN_FREE
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_OPEN, volume = 0.8F)
after(seconds = 1F) {
pokemon.busyLocks.remove(this)
captureFuture.complete(false)
world.sendParticlesServer(ParticleTypes.CLOUD, pos, 20, Vec3d(0.0, 0.2, 0.0), 0.05)
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_OPEN, volume = 0.8F)
discard()
}
}
@@ -381,6 +380,17 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
captureState = CaptureState.HIT
val mul = if (random.nextBoolean()) 1 else -1
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_HIT, volume = 0.4F)
// Hit Pokémon plays recoil animation
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
pkt.sendToPlayersAround(
x = pokemonEntity.x,
y = pokemonEntity.y,
z = pokemonEntity.z,
worldKey = pokemonEntity.world.registryKey,
distance = 50.0
)
// Bounce backwards away from the hit Pokémon
velocity = displace.multiply(-1.0, 0.0, -1.0).normalize().rotateY(mul * PI/3).multiply(0.1, 0.0, 0.1).add(0.0, 1.0 / 3, 0.0)
pokemonEntity.phasingTargetId = this.id
@@ -389,7 +399,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
velocity = Vec3d.ZERO
setNoGravity(true)
world.playSoundServer(pos, CobblemonSounds.POKE_BALL_CAPTURE_STARTED, volume = 0.6F)
pokemonEntity.beamMode = 2
pokemonEntity.beamMode = 3
}
after(seconds = 2.2F) {
@@ -28,7 +28,6 @@ import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.ElementalTypes.FIRE
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -54,12 +53,9 @@ import com.cobblemon.mod.common.pokemon.activestate.InactivePokemonState
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.ai.FormPokemonBehaviour
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.misc.GimmighoulStashHandler
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.util.EnumSet
import java.util.Optional
import java.util.UUID
import java.util.concurrent.CompletableFuture
import net.minecraft.entity.*
import net.minecraft.entity.ai.control.MoveControl
import net.minecraft.entity.ai.goal.EatGrassGoal
@@ -106,6 +102,8 @@ import net.minecraft.util.math.Vec3d
import net.minecraft.world.EntityView
import net.minecraft.world.World
import net.minecraft.world.event.GameEvent
import java.util.*
import java.util.concurrent.CompletableFuture
@Suppress("unused")
open class PokemonEntity(
@@ -332,7 +330,7 @@ open class PokemonEntity(
* Prevents fire type Pokémon from taking fire damage.
*/
override fun isFireImmune(): Boolean {
return FIRE in pokemon.types || !behaviour.moving.swim.hurtByLava
return pokemon.isFireImmune()
}
/**
@@ -377,10 +375,10 @@ open class PokemonEntity(
if (dataTracker.get(PHASING_TARGET_ID) == -1 && owner != null) {
owner.getWorld().playSoundServer(pos, CobblemonSounds.POKE_BALL_RECALL, volume = 0.6F)
dataTracker.set(PHASING_TARGET_ID, owner.id)
dataTracker.set(BEAM_MODE, 2)
dataTracker.set(BEAM_MODE, 3)
val state = pokemon.state
after(seconds = SEND_OUT_DURATION) {
// only recall if the pokemon hasn't been recalled yet for this state
// only recall if the Pokémon hasn't been recalled yet for this state
if (state == pokemon.state) {
pokemon.recall()
}
@@ -663,6 +661,16 @@ open class PokemonEntity(
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
}
}
@@ -205,7 +205,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
if (owner != null) {
entity.world.playSoundServer(owner.pos, CobblemonSounds.POKE_BALL_RECALL, volume = 0.6F)
entity.dataTracker.set(PokemonEntity.PHASING_TARGET_ID, owner.id)
entity.dataTracker.set(PokemonEntity.BEAM_MODE, 2)
entity.dataTracker.set(PokemonEntity.BEAM_MODE, 3)
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.util.getWaterAndLavaIn
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
import net.minecraft.entity.ai.control.MoveControl
@@ -75,9 +76,9 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
entity.setSidewaysSpeed(sidewaysMovement)
state = State.WAIT
} else if (state == State.MOVE_TO) {
val xDist = targetX - entity.x
val zDist = targetZ - entity.z
val yDist = targetY - entity.y
var xDist = targetX - entity.x
var zDist = targetZ - entity.z
var yDist = targetY - entity.y
val horizontalDistanceFromTarget = xDist * xDist + zDist * zDist
val closeHorizontally = horizontalDistanceFromTarget < VERY_CLOSE
@@ -99,12 +100,13 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
verticalHandled = true
entity.upwardSpeed = 0F
entity.movementSpeed = 0F
// Refinement is to prevent the entity from spinning around trying to get to a super precise location.
val refine: (Double) -> Double = { if (abs(it) < 0.05) 0.0 else it }
val fullDistance = Vec3d(
refine(xDist),
if (inFluid || pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) refine(yDist + 0.05) else 0.0,
refine(zDist)
xDist,
refine(yDist + 0.05), // + 0.05 for dealing with swimming out of water, they otherwise get stuck on the lip
zDist
)
val direction = fullDistance.normalize()
@@ -112,8 +114,13 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
val scale = min(adjustedSpeed.toDouble(), fullDistance.length())
entity.velocity = direction.multiply(scale)
xDist = fullDistance.x
zDist = fullDistance.z
yDist = fullDistance.y
} else {
val forwardSpeed = min(adjustedSpeed, sqrt(horizontalDistanceFromTarget).toFloat())
// division is to slow the speed down a bit so they don't overshoot when they get there.
val forwardSpeed = min(adjustedSpeed, max(horizontalDistanceFromTarget.toFloat() / 2, 0.15F))
entity.movementSpeed = forwardSpeed
}
@@ -12,12 +12,14 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Hand
import net.minecraft.util.TypedActionResult
import net.minecraft.world.World
import kotlin.math.cos
class PokeBallItem(
val pokeBall : PokeBall
@@ -36,8 +38,12 @@ class PokeBallItem(
private fun throwPokeBall(world: World, player: ServerPlayerEntity) {
val pokeBallEntity = EmptyPokeBallEntity(pokeBall, player.world, player).apply {
// setPos(player.x, player.y + player.standingEyeHeight - 0.2, player.z)
setVelocity(player, player.pitch - 5, player.yaw, 0.0f, pokeBall.throwPower, 1.0f)
val overhandFactor: Float = if (player.pitch < 0) {
5f * cos(player.pitch.toRadians())
} else {
5f
}
setVelocity(player, player.pitch - overhandFactor, player.yaw, 0.0f, pokeBall.throwPower, 1.0f)
setPosition(pos.add(velocity.normalize().multiply(1.0)))
owner = player
}
@@ -9,48 +9,33 @@
package com.cobblemon.mod.common.net.messages.client.starter
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
import com.cobblemon.mod.common.config.starter.StarterCategory
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.StarterCategory
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class OpenStarterUIPacket internal constructor(val categories: List<RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
class OpenStarterUIPacket(val categories: Map<Identifier, RenderableStarterCategory>) : NetworkPacket<OpenStarterUIPacket> {
override val id = ID
constructor(categories: Collection<StarterCategory>) : this(categories.map { it.asRenderableStarterCategory() })
override fun encode(buffer: PacketByteBuf) {
buffer.writeInt(categories.size)
categories.forEach {
buffer.writeString(it.name)
buffer.writeString(it.displayName)
buffer.writeInt(it.pokemon.size)
it.pokemon.forEach { it.saveToBuffer(buffer) }
buffer.writeMap(this.categories, PacketByteBuf::writeIdentifier) { categoryBuffer, category ->
categoryBuffer.writeText(category.displayName)
categoryBuffer.writeCollection(category.pokemon) { pokemonBuffer, renderable ->
renderable.saveToBuffer(pokemonBuffer)
}
}
}
companion object {
val ID = cobblemonResource("open_starter")
fun decode(buffer: PacketByteBuf): OpenStarterUIPacket {
val numCategories = buffer.readInt()
val categories = arrayListOf<RenderableStarterCategory>()
for (i in 0 until numCategories) {
val name = buffer.readString()
val displayName = buffer.readString()
val numProperties = buffer.readInt()
val renderablePokemon = mutableListOf<RenderablePokemon>()
repeat(times = numProperties) {
renderablePokemon.add(RenderablePokemon.loadFromBuffer(buffer))
}
categories.add(
RenderableStarterCategory(
name = name,
displayName = displayName,
pokemon = renderablePokemon
)
)
val categories = buffer.readMap(PacketByteBuf::readIdentifier) { categoryBuffer ->
val displayName = buffer.readText()
val renderablePokemon = categoryBuffer.readList(RenderablePokemon::loadFromBuffer)
return@readMap RenderableStarterCategory(displayName, renderablePokemon)
}
return OpenStarterUIPacket(categories)
}
@@ -29,7 +29,7 @@ class SetClientPlayerDataPacket(val promptStarter: Boolean, val starterLocked: B
playerData: PlayerData,
resetStarterPrompt: Boolean? = null
) : this(
!playerData.starterPrompted || !Cobblemon.starterConfig.promptStarterOnceOnly,
!playerData.starterPrompted || !Cobblemon.config.promptStarterOnceOnly,
playerData.starterLocked,
playerData.starterSelected,
playerData.starterUUID,
@@ -11,16 +11,17 @@ package com.cobblemon.mod.common.net.messages.server
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
class SelectStarterPacket(val categoryName: String, val selected: Int) : NetworkPacket<SelectStarterPacket> {
class SelectStarterPacket(val categoryId: Identifier, val selected: Int) : NetworkPacket<SelectStarterPacket> {
override val id = ID
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(this.categoryName)
buffer.writeIdentifier(this.categoryId)
buffer.writeInt(this.selected)
}
companion object {
val ID = cobblemonResource("select_starter")
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readString(), buffer.readInt())
fun decode(buffer: PacketByteBuf): SelectStarterPacket = SelectStarterPacket(buffer.readIdentifier(), buffer.readInt())
}
}
@@ -16,6 +16,6 @@ import net.minecraft.server.network.ServerPlayerEntity
object SelectStarterPacketHandler : ServerNetworkPacketHandler<SelectStarterPacket> {
override fun handle(packet: SelectStarterPacket, server: MinecraftServer, player: ServerPlayerEntity) {
Cobblemon.starterHandler.chooseStarter(player, packet.categoryName, packet.selected)
Cobblemon.starterHandler.chooseStarter(player, packet.categoryId, packet.selected)
}
}
@@ -13,11 +13,10 @@ import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.traceBlockCollision
import com.cobblemon.mod.common.util.raycastSafeSendout
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.math.Vec3d
import net.minecraft.world.RaycastContext
object SendOutPokemonHandler : ServerNetworkPacketHandler<SendOutPokemonPacket> {
@@ -31,11 +30,10 @@ object SendOutPokemonHandler : ServerNetworkPacketHandler<SendOutPokemonPacket>
return
}
val state = pokemon.state
if (state is ShoulderedState || state !is ActivePokemonState) {
val trace = player.traceBlockCollision(maxDistance = 15F)
if (trace != null && !player.world.getBlockState(trace.blockPos.up()).isSolid) {
val position = Vec3d(trace.location.x, trace.blockPos.up().toVec3d().y, trace.location.z)
val position = player.raycastSafeSendout(pokemon, 12.0, 5.0, RaycastContext.FluidHandling.ANY)
if (position != null) {
pokemon.sendOutWithAnimation(player, player.serverWorld, position)
}
} else {
@@ -49,6 +49,7 @@ import com.cobblemon.mod.common.api.storage.StoreCoordinates
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.tera.TeraType
import com.cobblemon.mod.common.api.types.tera.TeraTypes
import com.cobblemon.mod.common.config.CobblemonConfig
@@ -72,6 +73,7 @@ import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.mojang.serialization.Dynamic
import com.mojang.serialization.JsonOps
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
@@ -80,6 +82,7 @@ import net.minecraft.nbt.NbtElement.COMPOUND_TYPE
import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.NbtString
import net.minecraft.registry.tag.FluidTags
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.text.MutableText
@@ -88,9 +91,11 @@ import net.minecraft.text.TextContent
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.InvalidIdentifierException
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.MathHelper.ceil
import net.minecraft.util.math.MathHelper.clamp
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import java.util.*
import java.util.concurrent.CompletableFuture
import kotlin.math.absoluteValue
@@ -534,6 +539,7 @@ open class Pokemon : ShowdownIdentifiable {
val rotatedOffset = Vec3d(shoulderHorizontalOffset, approxShoulderMonHight, 0.0).rotateY(-rotation * (Math.PI.toFloat() / 180f))
val currentPosition = player.pos.add(rotatedOffset)
recall()
sendOut(level, currentPosition) {
// Play some sound indicating hopping off
level.playSoundServer(currentPosition, CobblemonSounds.PC_DROP, volume = 0.6F)
@@ -614,6 +620,52 @@ open class Pokemon : ShowdownIdentifiable {
}
}
fun isFireImmune(): Boolean {
return ElementalTypes.FIRE in types || !form.behaviour.moving.swim.hurtByLava
}
fun isPositionSafe(world: World, pos: Vec3d): Boolean {
return isPositionSafe(world, pos.toBlockPos())
}
fun isPositionSafe(world: World, pos1: BlockPos): Boolean {
// To make sure a location is safe, both the block the Pokemon is standing ON,
// and the block it's standing IN need to be safe. pos2 represents the other position in that set.
val pos2: BlockPos = if (world.getBlockState(pos1).isSolid) {
pos1.up()
} else {
pos1.down()
}
val positions = arrayOf(pos1, pos2)
var isSafe = true
for (pos in positions) {
if (isSafe) {
val block = world.getBlockState(pos).block
if (block is SweetBerryBushBlock ||
block is CactusBlock ||
block is WitherRoseBlock
) {
isSafe = false
}
if (!this.isFireImmune()) {
if (block is FireBlock ||
block is MagmaBlock ||
block is CampfireBlock ||
world.getBlockState(pos).fluidState.isIn(FluidTags.LAVA)
) {
isSafe = false
}
}
}
}
return isSafe
}
/**
* A utility method that checks if this Pokémon species or form data contains the [CobblemonPokemonLabels.LEGENDARY] label.
* This is used in Pokémon officially considered legendary.
@@ -11,10 +11,13 @@ package com.cobblemon.mod.common.pokemon.misc
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.events.pokemon.HeldItemEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.HeldItemUpdatedEvent
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeature
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Item
import net.minecraft.item.Items
import net.minecraft.sound.SoundCategory
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Hand
/**
* Handles logic relating to Gimmighoul's Coin Stash and Scrap Stash bars.
@@ -30,13 +33,30 @@ object GimmighoulStashHandler {
val SCRAP_VALUE = 1
val INGOT_VALUE = SCRAP_VALUE * 4
val BLOCK_VALUE = INGOT_VALUE * 9
fun interactMob(player: PlayerEntity, hand: Hand, pokemon:Pokemon) : Boolean {
val itemStack = player.getStackInHand(hand)
var success = false
if(player is ServerPlayerEntity && pokemon.getOwnerPlayer() == player) {
success = handleItem(pokemon, itemStack.item)
if(success) {
itemStack.decrement(1)
}
}
return success
}
fun giveHeldItem(event: HeldItemEvent.Post) {
val goldHoard = event.pokemon.getFeature<IntSpeciesFeature>("gimmighoul_coins")
val netheriteHoard = event.pokemon.getFeature<IntSpeciesFeature>("gimmighoul_netherite")
val pokemon = event.pokemon
val item = event.received.item
if(handleItem(pokemon, item)) {
pokemon.removeHeldItem()
}
}
fun handleItem(pokemon: Pokemon, item: Item) : Boolean {
val goldHoard = pokemon.getFeature<IntSpeciesFeature>("gimmighoul_coins")
val netheriteHoard = pokemon.getFeature<IntSpeciesFeature>("gimmighoul_netherite")
if (goldHoard != null && goldHoard.value < 999) {
val increase = when (event.received.item) {
val increase = when (item) {
CobblemonItems.RELIC_COIN -> COIN_VALUE
CobblemonItems.RELIC_COIN_POUCH -> POUCH_VALUE
CobblemonItems.RELIC_COIN_SACK -> SACK_VALUE
@@ -46,14 +66,14 @@ object GimmighoulStashHandler {
if (increase != 0) {
goldHoard.value += increase
if (goldHoard.value > 999) goldHoard.value = 999
if (event.pokemon.entity != null) event.pokemon.entity!!.playSound(CobblemonSounds.GIMMIGHOUL_GIVE_ITEM_SMALL, 1f, 1f)
event.pokemon.markFeatureDirty(goldHoard)
event.pokemon.removeHeldItem()
if (pokemon.entity != null) pokemon.entity!!.playSound(CobblemonSounds.GIMMIGHOUL_GIVE_ITEM_SMALL, 1f, 1f)
pokemon.markFeatureDirty(goldHoard)
return true
}
}
if (netheriteHoard != null && netheriteHoard.value < 256) {
val increase = when (event.received.item) {
val increase = when (item) {
Items.NETHERITE_SCRAP -> SCRAP_VALUE
Items.NETHERITE_INGOT -> INGOT_VALUE
Items.NETHERITE_BLOCK -> BLOCK_VALUE
@@ -63,11 +83,12 @@ object GimmighoulStashHandler {
if (increase != 0) {
netheriteHoard.value += increase
if (netheriteHoard.value > 256) netheriteHoard.value = 256
if (event.pokemon.entity != null) event.pokemon.entity!!.playSound(CobblemonSounds.GIMMIGHOUL_GIVE_ITEM_SMALL, 1f, 1f)
event.pokemon.markFeatureDirty(netheriteHoard)
event.pokemon.removeHeldItem()
if (pokemon.entity != null) pokemon.entity!!.playSound(CobblemonSounds.GIMMIGHOUL_GIVE_ITEM_SMALL, 1f, 1f)
pokemon.markFeatureDirty(netheriteHoard)
return true
}
}
return false
}
}
@@ -1,73 +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.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.server.network.ServerPlayerEntity
open class CobblemonStarterHandler : StarterHandler {
override fun getStarterList(player: ServerPlayerEntity) = Cobblemon.starterConfig.starters
override fun handleJoin(player: ServerPlayerEntity) {}
override fun requestStarterChoice(player: ServerPlayerEntity) {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
playerData.sendToPlayer(player)
player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
} else if (playerData.starterLocked) {
player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
} else {
OpenStarterUIPacket(getStarterList(player)).sendToPlayer(player)
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
}
}
override fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int) {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
return player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
} else if (playerData.starterLocked) {
return player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
}
val category = getStarterList(player).find { it.name == categoryName } ?: return
if (index > category.pokemon.size) {
return
}
val properties = category.pokemon[index]
val pokemon = properties.create()
CobblemonEvents.STARTER_CHOSEN.postThen(StarterChosenEvent(player, properties, pokemon)) {
Cobblemon.storage.getParty(player).add(
it.pokemon.also {
playerData.starterSelected = true
playerData.starterUUID = it.uuid
if (player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)) { pokemon.shiny = true }
}
)
CobblemonCriteria.PICK_STARTER.trigger(player, pokemon)
Cobblemon.playerData.saveSingle(playerData)
playerData.sendToPlayer(player)
}
}
}
@@ -0,0 +1,96 @@
/*
* 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.starter
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
import com.cobblemon.mod.common.api.starter.StarterCategory
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.starter.StarterRegistry
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class CobblemonStarterHandler : StarterHandler {
override fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory> = StarterRegistry.all()
// No OP the starters are sent out when the open UI packet is sent out instead
override fun handleJoin(player: ServerPlayerEntity) {}
override fun requestStarterChoice(player: ServerPlayerEntity) {
if (!this.isAbleToSelect(player)) {
return
}
val playerData = Cobblemon.playerData.get(player)
val renderable = this.getStarters(player).entries.associate { (id, category) ->
val renderable = category.pokemon.map {
// Let the actual Pokémon be created and apply the shiny accordingly
// This won't be accurate with random but I mean what else do you want me to do
val pokemon = it.create()
// While they can still be shiny a random shiny from the #create doesn't mean the actual starter will be shiny
// This tries to be realistic
pokemon.shiny = player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)
pokemon.asRenderablePokemon()
}
id to RenderableStarterCategory(category.displayName, renderable)
}
OpenStarterUIPacket(renderable).sendToPlayer(player)
playerData.starterPrompted = true
Cobblemon.playerData.saveSingle(playerData)
}
override fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int) {
if (!this.isAbleToSelect(player)) {
return
}
val category = this.getStarters(player)[categoryId] ?: return
val properties = category.pokemon.getOrNull(index) ?: return
val pokemon = properties.create()
val playerData = Cobblemon.playerData.get(player)
CobblemonEvents.STARTER_CHOSEN.postThen(StarterChosenEvent(player, properties, pokemon)) { event ->
Cobblemon.storage.getParty(player).add(
event.pokemon.also {
playerData.starterSelected = true
playerData.starterUUID = it.uuid
if (player.world.gameRules.getBoolean(CobblemonGameRules.SHINY_STARTERS)) { pokemon.shiny = true }
}
)
CobblemonCriteria.PICK_STARTER.trigger(player, pokemon)
Cobblemon.playerData.saveSingle(playerData)
playerData.sendToPlayer(player)
}
}
/**
* Checks if the [player] can select a starter.
* If not gives them an appropriate feedback message.
*
* @param player The [ServerPlayerEntity] being tested.
* @return If the [player] can pick a starter.
*/
private fun isAbleToSelect(player: ServerPlayerEntity): Boolean {
val playerData = Cobblemon.playerData.get(player)
if (playerData.starterSelected) {
player.sendMessage(lang("ui.starter.alreadyselected").red(), true)
return false
} else if (playerData.starterLocked) {
player.sendMessage(lang("ui.starter.cannotchoose").red(), true)
return false
}
return true
}
}
@@ -13,11 +13,32 @@ import net.minecraft.entity.data.DataTracker
import net.minecraft.entity.data.TrackedData
import net.minecraft.util.function.BooleanBiFunction
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Direction
import net.minecraft.util.math.Vec3d
import net.minecraft.util.shape.VoxelShapes
fun Entity.setPositionSafely(pos: Vec3d): Boolean {
// TODO: Rework this function. Best way to do it would be to define a vertical and horizontal min/max shift based on the Pokemon's hitbox.
// Then loop through horizontal/vertical shifts, and detect collisions in three categories: suffocation, damaging blocks, and general collision
// The closest position with the least severe collision types will be selected to move the Pokemon to
// The throw could be cancelled if there are no viable locations without severe problems
// Optional: use getBlockCollisions iterator and VoxelShapes.combineAndSimplify to create a single cube to represent collision area
// Use that cube to "push" the Pokemon out of the wall at an angle
// Note: may not work well with L shape wall collisions
// val collisions = world.getBlockCollisions(this, box).iterator()
// if (collisions.hasNext()) {
// var collisionShape = collisions.next()
// while (collisions.hasNext()) {
// collisionShape = VoxelShapes.union(collisionShape, collisions.next())
// println(collisionShape)
// }
// } else {
// setPosition(pos)
// return true
// }
var result = pos
val eyes = pos.withAxis(Direction.Axis.Y, pos.y + this.standingEyeHeight)
@@ -29,27 +50,6 @@ fun Entity.setPositionSafely(pos: Vec3d): Boolean {
return true
}
val yChanges = listOf(1.0, -1.0, 2.0, -2.0)
var previousChange = 0.0
for (yChange in yChanges) {
box = box.offset(0.0, yChange - previousChange, 0.0)
val it = world.getBlockCollisions(this, box).iterator()
previousChange = yChange
if (it.hasNext()) {
continue
} else {
val roundedY = (pos.y + yChange).toInt()
box = box.offset(0.0, roundedY - pos.y, 0.0)
// If the rounded position actually collides again, then don't round at all.
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
setPosition(pos.add(0.0, yChange, 0.0))
return true
}
setPosition(Vec3d(pos.x, roundedY.toDouble(), pos.z))
return true
}
}
for (target in BlockPos.stream(box)) {
val blockState = this.world.getBlockState(target)
val collides = !blockState.isAir &&
@@ -73,13 +73,13 @@ fun Entity.setPositionSafely(pos: Vec3d): Boolean {
result = result.add(Vec3d(0.0, 0.0, 1 + (conflict.z - box.minZ + (1.0 / 8.0))))
}
Direction.SOUTH -> {
result = result.add(Vec3d(0.0, 0.0, -1 * (1 - (conflict.z - box.minZ) + (1.0 / 8.0))))
result = result.add(Vec3d(0.0, 0.0, (conflict.z - box.maxZ) - (1.0 / 8.0)))
}
Direction.WEST -> {
result = result.add(Vec3d(1 + (conflict.x - box.minX + (1.0 / 8.0)), 0.0, 0.0))
}
Direction.EAST -> {
result = result.add(Vec3d(-1 * (1 - (conflict.x - box.minX) + (1.0 / 8.0)), 0.0, 0.0))
result = result.add(Vec3d((conflict.x - box.maxX) - (1.0 / 8.0), 0.0, 0.0))
}
else -> {}
}
@@ -89,12 +89,90 @@ fun Entity.setPositionSafely(pos: Vec3d): Boolean {
}
}
box = boundingBox.offset(result)
if (!world.getBlockCollisions(this, box).iterator().hasNext()) {
this.setPosition(result)
return true
} else {
val yChanges = listOf(1.0, -1.0, 2.0, -2.0)
var previousChange = 0.0
// If the Pokemon still collides with blocks after horizontal shifting, try vertical shifting from the shifted position
for (yChange in yChanges) {
box = box.offset(0.0, yChange - previousChange, 0.0)
previousChange = yChange
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
continue
} else {
val roundedY = (result.y + yChange).toInt()
box = box.offset(0.0, roundedY - result.y, 0.0)
// If the rounded position actually collides again, then don't round at all.
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
result = result.add(0.0, yChange, 0.0)
this.setPosition(result)
return true
} else {
result = Vec3d(result.x, roundedY.toDouble(), result.z)
this.setPosition(result)
return true
}
}
}
// If vertical shifting from the new position still collides, try vertical shifting from the original pos
previousChange = 0.0
box = boundingBox.offset(pos)
for (yChange in yChanges) {
box = box.offset(0.0, yChange - previousChange, 0.0)
previousChange = yChange
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
continue
} else {
val roundedY = (result.y + yChange).toInt()
box = box.offset(0.0, roundedY - result.y, 0.0)
// If the rounded position actually collides again, then don't round at all.
if (world.getBlockCollisions(this, box).iterator().hasNext()) {
result = result.add(0.0, yChange, 0.0)
this.setPosition(result)
return true
} else {
result = Vec3d(result.x, roundedY.toDouble(), result.z)
this.setPosition(result)
return true
}
}
}
}
if (conflicts.size >= 3) {
this.setPosition(pos)
}
this.setPosition(result)
return true
// This final check guarantees that the sendout will return to the original position if the Pokemon will suffocate in the new one
// This will only happen if the horizontal shift moved the Pokemon into a suffocating position, and there was no valid vertical shift
val resultEyes = result.withAxis(Direction.Axis.Y, result.y + this.standingEyeHeight)
val resultEyeBox = Box.of(resultEyes, width.toDouble(), 1.0E-6, width.toDouble())
var collides = false
for (target in BlockPos.stream(resultEyeBox)) {
val blockState = this.world.getBlockState(target)
collides = !blockState.isAir &&
blockState.shouldSuffocate(this.world, target) &&
VoxelShapes.matchesAnywhere(
blockState.getCollisionShape(this.world, target)
.offset(target.x.toDouble(), target.y.toDouble(), target.z.toDouble()),
VoxelShapes.cuboid(box),
BooleanBiFunction.AND
)
if (collides) break
}
if (collides) {
this.setPosition(pos)
return true
} else {
this.setPosition(result)
return true
}
}
fun Entity.distanceTo(pos: BlockPos): Double {
@@ -19,7 +19,7 @@ import com.cobblemon.mod.common.api.reactive.Observable.Companion.filter
import com.cobblemon.mod.common.api.reactive.Observable.Companion.takeFirst
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.platform.events.PlatformEvents
import java.util.UUID
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.block.BlockState
import net.minecraft.entity.Entity
import net.minecraft.entity.player.PlayerEntity
@@ -30,10 +30,11 @@ import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvents
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Direction
import net.minecraft.util.math.Vec3d
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.*
import net.minecraft.world.RaycastContext
import java.util.*
import kotlin.math.min
// Stuff like getting their party
fun ServerPlayerEntity.party() = Cobblemon.storage.getParty(this)
@@ -251,6 +252,91 @@ fun findDirectionForIntercept(p0: Vec3d, p1: Vec3d, blockPos: BlockPos): Directi
return minDirection
}
fun ServerPlayerEntity.raycast(maxDistance: Float, fluidHandling: RaycastContext.FluidHandling?): BlockHitResult {
val f = pitch
val g = yaw
val vec3d = eyePos
val h = MathHelper.cos(-g * 0.017453292f - 3.1415927f)
val i = MathHelper.sin(-g * 0.017453292f - 3.1415927f)
val j = -MathHelper.cos(-f * 0.017453292f)
val k = MathHelper.sin(-f * 0.017453292f)
val l = i * j
val n = h * j
val vec3d2 = vec3d.add(l.toDouble() * maxDistance, k.toDouble() * maxDistance, n.toDouble() * maxDistance)
return world.raycast(RaycastContext(vec3d, vec3d2, RaycastContext.ShapeType.OUTLINE, fluidHandling, this))
}
fun ServerPlayerEntity.raycastSafeSendout(pokemon: Pokemon, maxDistance: Double, dropHeight: Double, fluidHandling: RaycastContext.FluidHandling?): Vec3d? {
// Crazy math stuff, don't worry about it
val f = pitch
val g = yaw
val vec3d = eyePos
val h = MathHelper.cos(-g * 0.017453292f - 3.1415927f)
val i = MathHelper.sin(-g * 0.017453292f - 3.1415927f)
val j = -MathHelper.cos(-f * 0.017453292f)
val k = MathHelper.sin(-f * 0.017453292f)
val l = i * j
val n = h * j
val vec3d2 = vec3d.add(l.toDouble() * maxDistance, k.toDouble() * maxDistance, n.toDouble() * maxDistance)
val result = world.raycast(RaycastContext(vec3d, vec3d2, RaycastContext.ShapeType.OUTLINE, fluidHandling, this))
if (world.getBlockState(result.blockPos).isAir) {
// If the trace returns air, the player isn't aiming at any blocks within range
var traceDown: TraceResult?
val minDrop = min(2.5, maxDistance)
val stepDistance = 0.05
var step = minDrop
var stepDrop = minDrop
var stepPos: Vec3d
var traceHeight: Double
var smallestHeight = dropHeight
var fallLoc: TraceResult? = null
// Try to find a valid block below the player's aim instead
while (step <= maxDistance) {
stepPos = vec3d.add(l.toDouble() * step, k.toDouble() * step, n.toDouble() * step)
if (minDrop != maxDistance) {
stepDrop = ((step - minDrop) / (maxDistance - minDrop)) * dropHeight
}
traceDown = stepPos.traceDownwards(this.world, maxDistance = stepDrop.toFloat())
if (traceDown != null && pokemon.isPositionSafe(world, traceDown.blockPos)) {
traceHeight = (stepPos.y - traceDown.location.y)
if (traceHeight < smallestHeight) {
smallestHeight = traceHeight
fallLoc = traceDown
}
}
step += stepDistance
}
// If a valid block was found below the player's aim, return the location directly above it
return fallLoc?.blockPos?.up()?.toCenterPos()
} else if (result.side != Direction.UP) {
// If the player targets the side or bottom of a block, try to find a valid spot in front of / below that block
val offset: Double = if (result.side == Direction.DOWN) {
0.125 + pokemon.form.hitbox.height*pokemon.form.baseScale*0.5
} else {
0.125 + pokemon.form.hitbox.width*pokemon.form.baseScale*0.5
}
val posOffset = result.pos.offset(result.side, offset)
val traceDown = posOffset.traceDownwards(this.world, maxDistance = dropHeight.toFloat())
return if (traceDown == null || !pokemon.isPositionSafe(world, traceDown.blockPos)) {
null
} else {
return Vec3d(traceDown.location.x, traceDown.blockPos.up().toVec3d().y, traceDown.location.z)
}
} else if (!this.world.getBlockState(result.blockPos.up()).isSolid && pokemon.isPositionSafe(world, result.blockPos)) {
// If the player is targeting the top of a block, and the block above it isn't solid, it will spawn on that block
return Vec3d(result.pos.x, result.blockPos.up().toVec3d().y, result.pos.z)
}
return null
}
fun PlayerInventory.usableItems() = offHand + main
/**
@@ -124,3 +124,41 @@ val World.itemRegistry: Registry<Item>
get() = registryManager.get(RegistryKeys.ITEM)
val World.biomeRegistry: Registry<Biome>
get() = registryManager.get(RegistryKeys.BIOME)
fun Vec3d.traceDownwards(
world: World,
maxDistance: Float = 10F,
stepDistance: Float = 0.5F,
): TraceResult? {
var step = stepDistance
val startPos = Vec3d(x, y, z)
val direction = Vec3d(0.0, -1.0, 0.0)
var lastBlockPos = startPos.toBlockPos()
while (step <= maxDistance) {
val location = startPos.add(direction.multiply(step.toDouble()))
step += stepDistance
val blockPos = location.toBlockPos()
if (blockPos == lastBlockPos) {
continue
} else {
lastBlockPos = blockPos
}
val block = world.getBlockState(blockPos)
if (!block.isAir) {
val dir = findDirectionForIntercept(startPos, location, blockPos)
return TraceResult(
location = location,
blockPos = blockPos,
direction = dir
)
}
}
return null
}
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.util.adapters
import com.google.gson.*
import net.minecraft.text.Text
import java.lang.reflect.Type
object PlainJsonTextAdapter : JsonDeserializer<Text>, JsonSerializer<Text> {
override fun deserialize(jElement: JsonElement, type: Type, context: JsonDeserializationContext): Text {
return Text.Serializer.fromJson(jElement) ?: throw IllegalArgumentException("Cannot resolve null text")
}
override fun serialize(text: Text, type: Type, context: JsonSerializationContext): JsonElement = JsonPrimitive(Text.Serializer.toJson(text))
}
@@ -1,5 +1,5 @@
{
"maxScale": 0.8,
"maxScale": 0.55,
"yTranslation": 0,
"yGrowthPoint": 0.5625,
"animations": [
@@ -1,5 +1,5 @@
{
"maxScale": 0.75,
"maxScale": 0.85,
"yTranslation": 0.1,
"yGrowthPoint": 0.3125,
@@ -884,7 +884,7 @@
"advancements.cobblemon.obtain_leek.description": "Obtain a Medicinal Leek, a wild crop that grows in freshwater",
"advancements.cobblemon.medicinal_brew": "Local Apothecary",
"advancements.cobblemon.medicinal_brew.description": "Brew a Medicinal Leek to create the basis for your healing needs",
"advancements.cobblemon.brew_potion": "You cannot handle my Potions",
"advancements.cobblemon.brew_potion": "You Cannot Handle My Potions",
"advancements.cobblemon.brew_potion.description": "Brew an Oran Berry into Medicinal Brew to create a Potion",
"advancements.cobblemon.brew_full_restore": "Champion's Choice",
"advancements.cobblemon.brew_full_restore.description": "Follow the brewing tree and create a Full Restore with a Lum Berry and Max Potion",
@@ -934,12 +934,12 @@
"advancements.cobblemon.obtain_fossil.description": "Discover a fossil buried in ancient ruins",
"advancements.cobblemon.evolve_gholdengo_netherite": "Cover thee in Debris",
"advancements.cobblemon.evolve_gholdengo_netherite.description": "Evolve a Gimmighoul into a fully netherite-coated Gholdengo",
"advancements.cobblemon.resurrect_pokemon": "Life finds a way",
"advancements.cobblemon.resurrect_pokemon": "Life Finds a Way",
"advancements.cobblemon.resurrect_pokemon.description": "Obtain a Fossil Pokémon from a Resurrection Machine",
"advancements.cobblemon.obtain_tumblestone": "The Rolling Stone",
"advancements.cobblemon.obtain_tumblestone.description": "Safely extract a Tumblestone from suspicious sand or gravel",
"advancements.cobblemon.grow_tumblestone": "Heat from stone, stone from heat",
"advancements.cobblemon.grow_tumblestone.description": "Place a Tumblestone near Lava or Magma, allowing the Tumblestone to grow slowly over time",
"advancements.cobblemon.grow_tumblestone": "Tumbling Down",
"advancements.cobblemon.grow_tumblestone.description": "Place a Tumblestone near Lava or Magma to grow it over time",
"advancements.cobblemon.craft_ancient_balls": "A true Antiquarian",
"advancements.cobblemon.craft_ancient_balls.description": "Utilize Tumblestones to craft all fifteen Ancient Balls",
@@ -1534,6 +1534,7 @@
"gamerule.doPokemonSpawning": "Spawn Pokémon",
"gamerule.doShinyStarters": "Starters will be shiny",
"gamerule.doPokemonLoot": "Drop Pokémon Loot",
"cobblemon.command.checkspawns.invalid-bucket": "Invalid Spawn Bucket",
"cobblemon.command.checkspawns.spawns": "Spawns",
"cobblemon.command.checkspawns.nothing": "Nothing can spawn here right now.",
@@ -38,7 +38,13 @@
"predicate": {
"custom_model_data": 6
},
"model": "cobblemon:item/cobblemon"
"model": "cobblemon:item/shiny_marker"
},
{
"predicate": {
"custom_model_data": 7
},
"model": "cobblemon:item/link_cable"
}
]
}
@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "cobblemon:item/advancements/shiny_icon"
}
}
@@ -104,49 +104,54 @@
"berry.eat": {
"sounds": [ "cobblemon:berry_eat" ]
},
"fossilmachine.activate": {
"fossil_machine.activate": {
"subtitle": "cobblemon.subtitle.fossilmachine.activate",
"sounds": [ "cobblemon:fossilmachine/activate" ]
"sounds": [ { "name": "cobblemon:block/fossil_machine/fossil_machine_activate", "volume": 0.8 } ]
},
"fossilmachine.active_loop": {
"fossil_machine.active_loop": {
"subtitle": "cobblemon.subtitle.fossilmachine.active_loop",
"sounds": [ "cobblemon:fossilmachine/active_loop" ]
"sounds": [ { "name": "cobblemon:block/fossil_machine/fossil_machine_active_loop", "volume": 0.6 } ]
},
"fossilmachine.assemble": {
"fossil_machine.assemble": {
"subtitle": "cobblemon.subtitle.fossilmachine.assemble",
"sounds": [ "cobblemon:fossilmachine/assemble" ]
"sounds": [ { "name": "cobblemon:block/fossil_machine/fossil_machine_assemble", "volume": 0.3 } ]
},
"fossilmachine.dna_full": {
"fossil_machine.dna_full": {
"subtitle": "cobblemon.subtitle.fossilmachine.dna_full",
"sounds": [ "cobblemon:fossilmachine/dna_full" ]
"sounds": [ "cobblemon:block/fossil_machine/fossil_machine_dna_full" ]
},
"fossilmachine.finished": {
"fossil_machine.finished": {
"subtitle": "cobblemon.subtitle.fossilmachine.finished",
"sounds": [ "cobblemon:fossilmachine/finished" ]
"sounds": [ "cobblemon:block/fossil_machine/fossil_machine_finished" ]
},
"fossilmachine.insert_dna": {
"fossil_machine.insert_dna": {
"subtitle": "cobblemon.subtitle.fossilmachine.insert_dna",
"sounds": [ "cobblemon:fossilmachine/insert_dna" ]
"sounds": [ "cobblemon:block/fossil_machine/fossil_machine_insert_dna" ]
},
"fossilmachine.insert_dna_small": {
"fossil_machine.insert_dna_small": {
"subtitle": "cobblemon.subtitle.fossilmachine.insert_dna_small",
"sounds": [ "cobblemon:fossilmachine/insert_dna_small" ]
"sounds": [
{ "name": "cobblemon:block/fossil_machine/fossil_machine_insert_dna_small_1", "volume": 0.7 },
{ "name": "cobblemon:block/fossil_machine/fossil_machine_insert_dna_small_2", "volume": 0.7 },
{ "name": "cobblemon:block/fossil_machine/fossil_machine_insert_dna_small_3", "volume": 0.7 },
{ "name": "cobblemon:block/fossil_machine/fossil_machine_insert_dna_small_4", "volume": 0.7 }
]
},
"fossilmachine.insert_fossil": {
"fossil_machine.insert_fossil": {
"subtitle": "cobblemon.subtitle.fossilmachine.insert_fossil",
"sounds": [ "cobblemon:fossilmachine/insert_fossil" ]
"sounds": [ "cobblemon:block/fossil_machine/fossil_machine_insert_fossil" ]
},
"fossilmachine.retrieve_fossil": {
"fossil_machine.retrieve_fossil": {
"subtitle": "cobblemon.subtitle.fossilmachine.retrieve_fossil",
"sounds": [ "cobblemon:fossilmachine/retrieve_fossil" ]
"sounds": [ "cobblemon:block/fossil_machine/fossil_machine_retrieve_fossil" ]
},
"fossilmachine.retrieve_pokemon": {
"fossil_machine.retrieve_pokemon": {
"subtitle": "cobblemon.subtitle.fossilmachine.retrieve_pokemon",
"sounds": [ "cobblemon:fossilmachine/retrieve_pokemon" ]
"sounds": [ "cobblemon:block/fossil_machine/fossil_machine_retrieve_pokemon" ]
},
"fossilmachine.unprotected": {
"fossil_machine.unprotected": {
"subtitle": "cobblemon.subtitle.fossilmachine.unprotected",
"sounds": [ "cobblemon:fossilmachine/unprotected" ]
"sounds": [ "cobblemon:block/fossil_machine/fossil_machine_unprotected" ]
},
"gimmighoul.give_item_small": {
"sounds": [ "cobblemon:gimmighoul_give_item_small" ]
@@ -164,10 +169,10 @@
},
"coin_pouch.hit": {
"sounds": [
"cobblemon:block/coin_pouch/coin_pouch_block_hit_1",
"cobblemon:block/coin_pouch/coin_pouch_block_hit_2",
"cobblemon:block/coin_pouch/coin_pouch_block_hit_3",
"cobblemon:block/coin_pouch/coin_pouch_block_hit_4"
{ "name": "cobblemon:block/coin_pouch/coin_pouch_block_hit_1", "volume": 0.6 },
{ "name": "cobblemon:block/coin_pouch/coin_pouch_block_hit_2", "volume": 0.6 },
{ "name": "cobblemon:block/coin_pouch/coin_pouch_block_hit_3", "volume": 0.6 },
{ "name": "cobblemon:block/coin_pouch/coin_pouch_block_hit_4", "volume": 0.6 }
]
},
"coin_pouch.place": {
@@ -204,10 +209,10 @@
},
"tumblestone.hit": {
"sounds": [
"cobblemon:block/tumblestone/tumblestone_hit_1",
"cobblemon:block/tumblestone/tumblestone_hit_2",
"cobblemon:block/tumblestone/tumblestone_hit_3",
"cobblemon:block/tumblestone/tumblestone_hit_4"
{ "name": "cobblemon:block/tumblestone/tumblestone_hit_1", "volume": 0.8 },
{ "name": "cobblemon:block/tumblestone/tumblestone_hit_2", "volume": 0.8 },
{ "name": "cobblemon:block/tumblestone/tumblestone_hit_3", "volume": 0.8 },
{ "name": "cobblemon:block/tumblestone/tumblestone_hit_4", "volume": 0.8 }
]
},
"tumblestone.place": {
@@ -304,6 +309,20 @@
"cobblemon:block/plants/medicinal_leek/medicinal_leek_plant_3"
]
},
"vivichoke.break": {
"sounds": [
"cobblemon:block/plants/vivichoke/vivichoke_break_1",
"cobblemon:block/plants/vivichoke/vivichoke_break_2",
"cobblemon:block/plants/vivichoke/vivichoke_break_3"
]
},
"vivichoke.place": {
"sounds": [
"cobblemon:block/plants/vivichoke/vivichoke_place_1",
"cobblemon:block/plants/vivichoke/vivichoke_place_2",
"cobblemon:block/plants/vivichoke/vivichoke_place_3"
]
},
"mint.break": {
"sounds": [
"cobblemon:block/plants/mint/mint_break_1",
@@ -318,6 +337,20 @@
"cobblemon:block/plants/mint/mint_place_3"
]
},
"revival_herb.break": {
"sounds": [
"cobblemon:block/plants/revival_herb/revival_herb_break_1",
"cobblemon:block/plants/revival_herb/revival_herb_break_2",
"cobblemon:block/plants/revival_herb/revival_herb_break_3"
]
},
"revival_herb.place": {
"sounds": [
"cobblemon:block/plants/revival_herb/revival_herb_place_1",
"cobblemon:block/plants/revival_herb/revival_herb_place_2",
"cobblemon:block/plants/revival_herb/revival_herb_place_3"
]
},
"gilded_chest.break": {
"sounds": [
"cobblemon:block/gilded_chest/gilded_chest_break_1",
@@ -374,58 +407,220 @@
"sounds": [ "cobblemon:supereffective" ]
},
"impact.normal": {
"sounds": [ "cobblemon:attacks/impacts/impact_normal" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_8", "volume": 0.8 }
]
},
"impact.ice": {
"sounds": [ "cobblemon:attacks/impacts/impact_ice" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ice_8", "volume": 0.8 }
]
},
"impact.water": {
"sounds": [ "cobblemon:attacks/impacts/impact_water" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_water_8", "volume": 0.8 }
]
},
"impact.fire": {
"sounds": [ "cobblemon:attacks/impacts/impact_fire" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fire_8", "volume": 0.8 }
]
},
"impact.electric": {
"sounds": [ "cobblemon:attacks/impacts/impact_electric" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_electric_8", "volume": 0.8 }
]
},
"impact.grass": {
"sounds": [ "cobblemon:attacks/impacts/impact_grass" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_grass_8", "volume": 0.8 }
]
},
"impact.fighting": {
"sounds": [ "cobblemon:attacks/impacts/impact_fighting" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fighting_8", "volume": 0.8 }
]
},
"impact.poison": {
"sounds": [ "cobblemon:attacks/impacts/impact_poison" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_poison_8", "volume": 0.8 }
]
},
"impact.ground": {
"sounds": [ "cobblemon:attacks/impacts/impact_ground" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ground_8", "volume": 0.8 }
]
},
"impact.flying": {
"sounds": [ "cobblemon:attacks/impacts/impact_flying" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_flying_8", "volume": 0.8 }
]
},
"impact.psychic": {
"sounds": [ "cobblemon:attacks/impacts/impact_psychic" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_psychic_8", "volume": 0.8 }
]
},
"impact.bug": {
"sounds": [ "cobblemon:attacks/impacts/impact_bug" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_bug_8", "volume": 0.8 }
]
},
"impact.rock": {
"sounds": [ "cobblemon:attacks/impacts/impact_rock" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_rock_8", "volume": 0.8 }
]
},
"impact.ghost": {
"sounds": [ "cobblemon:attacks/impacts/impact_ghost" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_ghost_8", "volume": 0.8 }
]
},
"impact.dragon": {
"sounds": [ "cobblemon:attacks/impacts/impact_dragon" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dragon_8", "volume": 0.8 }
]
},
"impact.dark": {
"sounds": [ "cobblemon:attacks/impacts/impact_dark" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_dark_8", "volume": 0.8 }
]
},
"impact.steel": {
"sounds": [ "cobblemon:attacks/impacts/impact_steel" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_steel_8", "volume": 0.8 }
]
},
"impact.fairy": {
"sounds": [ "cobblemon:attacks/impacts/impact_fairy" ]
"sounds": [
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_1", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_2", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_3", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_4", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_5", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_6", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_7", "volume": 0.8 },
{ "name": "cobblemon:attacks/impact_generic/impact_generic_fairy_8", "volume": 0.8 }
]
},
"pokemon.bulbasaur.ambient": {
"sounds": [ "cobblemon:pokemon/bulbasaur/bulbasaur_ambient" ]
Loaded 100 of 1068 files, more files were not shown because too many files have changed in this diff. Show more