Compare commits

..
Author SHA1 Message Date
Plastered_Crab 3fa0544c28 - adjusted logic to prepare for detecting user switches and to allow for better use of pivot moves 2024-07-01 11:51:52 -07:00
Plastered_Crab e2445db9c4 - adjusted logic 2024-06-21 18:07:35 -07:00
Plastered_Crab 9610332de5 - adjusted team list 2024-06-19 22:38:15 -07:00
Plastered_Crab 0c08a3c271 - adjusted team list 2024-06-19 22:23:56 -07:00
Plastered_Crab 7aafc377db - added test sun team, rain team, and comp team to fight against for testing 2024-06-19 22:17:17 -07:00
Plastered_Crab 91fad06986 - start of incorporating skill levels into the battle ai 2024-06-19 16:33:07 -07:00
Lily 6865654f37 started on move tags 2024-01-26 17:57:03 -05:00
jeffw773 17b3345abe - Merged in MoreHoldItems branch and adjusted teams 2024-01-10 16:49:32 -08:00
jeffw773 9c037dbad2 Merge remote-tracking branch 'origin/main' into better_battle_ai_test 2024-01-10 16:12:58 -08:00
jeffw773 8afbe3a3a7 Merge branch 'MoreHoldItems' into better_battle_ai_test
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/pokemon/PokemonProperties.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/properties/PropertiesCompletionProvider.kt
2024-01-10 14:42:27 -08:00
jeffw773 916147ee0d - Battle AI should keep track of some volatiles more now 2024-01-08 14:30:12 -08:00
jeffw773 ad83d0e4a1 - Battle AI can now detect Type Changes
- Added hard "pivoter" NPC
2024-01-06 23:32:56 -08:00
jeffw773 5a993f3d88 - Fixed Battle AI to be able to use status moves on opponents 2024-01-05 13:59:36 -08:00
JazzMcNade 373c383097 Fixed matching the activepokemon on both parties in the tracker party. Fixed added newly revealed pokemon to the tracker party. 2024-01-04 00:47:13 -06:00
jeffw773 00c095ba8d - Almost done with rework of ActiveTracker 2024-01-03 21:24:32 -08:00
dhi_holo e8236b55a4 Recipe Fixes plus Information in Description
There are 6 items left unimplemented from the recipe document.
Soothe Bell, due to being an out of battle effect, I don't want to attempt, and Amulet Coin needs design work before implementation.
The four terrain seeds, due to future plans, I'm not sure if need to be set up differently. Beyond those six held items, this branch has all currently requested held items to my knowledge, all should now work as intended, although I have not confirmed all battle dialogue works, in part due to not wanting to find all the documentations of what the lines should be. I might come back to that another day, though.

Commits marked with "No Obtain Method" refer to items which have obtain methods of loot or whatever, which can be handled later by people familiar with those systems. If not, I'm down to learn, just not a thing for today.
2024-01-04 13:52:21 +10:00
dhi_holo 2ea07899ea -Added Sticky Barb
No Obtain Method
2024-01-04 13:01:30 +10:00
dhi_holo d0ce03cd5e -Added Shed Shell
No Obtain Method
2024-01-04 13:00:02 +10:00
dhi_holo 8ae0c409a3 -Added Light Ball
No Obtain Method
2024-01-04 12:58:15 +10:00
dhi_holo 2782cb5f34 -Added Lagging Tail
No Obtain Method
2024-01-04 12:56:09 +10:00
dhi_holo 2f10707211 -Added Grip Claw
No Obtain Method
2024-01-04 12:54:48 +10:00
dhi_holo f40a9d762a -Added Float Stone
No Obtain Method
2024-01-04 12:53:17 +10:00
dhi_holo 17587f97e5 -Added Absorb Bulb
No Obtain Method
2024-01-04 12:51:46 +10:00
dhi_holo 5230820657 -Added Punching Glove 2024-01-04 12:49:01 +10:00
dhi_holo 61c55e136f -Added Throat Spray
need to figure out how to add the potion brewing later, not a huge priority at this stage
2024-01-04 12:45:59 +10:00
dhi_holo 22cced74f5 -Added Luminous Moss 2024-01-04 12:43:40 +10:00
dhi_holo 3cd2dec8e0 -Added Loaded Dice 2024-01-04 12:40:34 +10:00
dhi_holo cea930893a -Added Iron Ball 2024-01-04 10:26:54 +10:00
dhi_holo 8662364ca8 -Added Wide Lens and Zoom Lens 2024-01-04 10:24:00 +10:00
dhi_holo 1fd079b1b4 -Added Weather Rocks
Damp Rock, Heat Rock, Smooth Rock, Icy Rock, terrible items
2024-01-04 10:19:53 +10:00
dhi_holo 244876a26c -Added Weakness Policy 2024-01-04 10:11:52 +10:00
dhi_holo e7790f530f -Added Utility Umbrella 2024-01-04 10:08:56 +10:00
dhi_holo 04a9ab0e67 -Adds Terrain Extender
If someone wants to work out how to make it so we can have a recipe that costs a water bucket, rather than water from a bucket, that'd be good, for now the recipe just costs an empty bucket
2024-01-04 10:06:39 +10:00
dhi_holo 3f338d27c3 -Added Scope Lens 2024-01-04 09:55:46 +10:00
dhi_holo 5da3dfb451 -Added Room Service 2024-01-04 09:54:00 +10:00
dhi_holo fd329e00cd -Added Ring Target 2024-01-04 09:52:00 +10:00
dhi_holo 6da47a8b2c -Added Red Card
Not gonna test this one for now, don't feel like setting up a second instance test. But, given Eject Button works, I'd say decent shot this one also works
2024-01-04 09:49:02 +10:00
dhi_holo 860c73fac9 -Added Protective Pads 2024-01-04 09:45:50 +10:00
dhi_holo e26fdd4bdd -Added Metronome
Deciding I'll ignore all held items with a * next to the name for now, not sure what they mean but hey, makes it easier to work through, and I can come back to them afterwards
2024-01-04 09:42:31 +10:00
dhi_holo 8e642f71b8 Merge branch 'main' into MoreHoldItems 2024-01-04 09:32:19 +10:00
dhi_holo f17884b4e1 Eject Button Lang, Read Description
All of the items added just now by me were tested besides Eviolite (don't feel like crunching those numbers today), Eject Pack is broken in my testing (see https://canary.discord.com/channels/725110639554592879/993671151870017656/1192009424282320957 for details), and ofc no item sprites yet, but that's dev life
2024-01-03 17:53:57 +10:00
dhi_holo c40b456948 -Added Eviolite and Expert Belt
test and finalise this little bit time (also corrected some jsons)
2024-01-03 17:19:31 +10:00
dhi_holo 7e74e85315 -Added Eject Button and Pack
I'm not testing these in game rn, I'll do that at the end of the session, which will probably be in a couple more items. But, these all hit the internet at the same time, so haha
2024-01-03 17:09:13 +10:00
dhi_holo 21fd4d7cbf -Added Covert Cloak 2024-01-03 17:00:58 +10:00
dhi_holo 908bff9892 -Added Cell Battery and Clear Amulet 2024-01-03 16:57:32 +10:00
dhi_holo fa14f70bbb -Added Shell Bell
Bell Go Heal
2024-01-03 15:59:01 +10:00
jeffw773 610ea0f30c - Added Sandstorm Team to battle that has stall, hazards, removal 2024-01-02 15:13:33 -08:00
jeffw773 a9c92d72d0 - NPC team for Red was buffed and given hazard removal
- AI can now do hazard removal properly (was broken)
2024-01-02 12:22:53 -08:00
jeffw773 25cc5f526b Merge remote-tracking branch 'origin/better_battle_ai_test' into better_battle_ai_test 2024-01-01 23:37:17 -08:00
jeffw773 3cb4f938f1 - AI can now use Hazards, Clear Hazards, and Status Moves
- Added trickroom NPC Team
2024-01-01 23:37:00 -08:00
dhi_holo 346559f292 Tags on top of Tags
Added a very large amount of tags.
The different tiers of Poké balls, also the Apricorn balls so it's easier for people to see what balls count for that advancement, different healing and battle item types, some evolution item sets which made sense to make tags (may add more later, these ones were just the obvious ones to make thing easier for future), the three colours for tumblestones, machine blocks, as well as adding the fence gate item tag, and adding the new tumblestone stuff to the pickaxe tag (although they can still be mined with an axe, probably something that needs fixing)

Mod loader tags were not tested on Forge, due to a crash involving CobblemonEntities, but Fabric works all nicely
2024-01-02 14:44:01 +10:00
dhi_holo 331b282fcf Decorated sherds tag, and fixed Focus Sash not being named
`focus_Sash` isn't valid, sadly
2024-01-02 12:41:28 +10:00
JazzMcNade a876666bc7 -Fixed an issue in which a pokemon with less than 4 moves would not allow its currently populated moveslots to be overwritten. 2024-01-01 20:31:24 -06:00
dhi_holo 7f97eeafed Update is_held_item.json
Reorganized alphabetically, for the most part, roughly matches the registry now.
2024-01-02 11:45:38 +10:00
dhi_holo 51f5a1f5a8 Merge branch 'main' into MoreHoldItems 2024-01-02 11:35:53 +10:00
dhi_holo 7c0535b8bd Merge branch 'main' into MoreHoldItems 2024-01-01 21:08:33 +10:00
dhi_holo 4f15ac0cbc Update CobblemonItems.kt
No More "why are the held items not in alphabetical order" for the time being (besides the three Powders and four Herbs, I left them together at the bottom because nobody cares about them)
2024-01-01 18:37:53 +10:00
JazzMcNade 35a0d8a80d -Added Blunder Policy 2024-01-01 01:31:08 -06:00
JazzMcNade c0270d3ef0 -Added Binding Band 2023-12-31 23:58:24 -06:00
JazzMcNade 82a1eb2a55 -Finished Air Balloon implementation. Fixed white space on recipes for for focus sash and Ability Shield 2023-12-31 21:44:26 -06:00
JazzMcNade a528ea952a -Added Ability Shield and Focus Sash. Begin work on Air Balloon. 2023-12-31 21:20:10 -06:00
JazzMcNade 592b40cd12 -Added temporary party store for cloned parties. Allows for proper faint behavior for clone team. 2023-12-31 18:49:45 -06:00
JazzMcNade 9e05cfa385 -Set simulation flag for clone battles 2023-12-31 14:22:47 -06:00
jeffw773 36f13f5b77 - Added Red, Blue, Volo, Evice to AI Battle pool
- AI can now use Trick Room
- Tweaked switchin considerations more to account for speed more
2023-12-30 02:05:45 -08:00
jeffw773 141a88ba13 - Cynthia Battle NPC tweaked + AI now doesn't constantly switch out
- Recovery moves consideration tweaked from .3 to .45
- Disabled moves now will not be considered
2023-12-29 19:50:16 -08:00
jeffw773 fc269e853c - Cynthia Battle AI is now stronger and hungers for battle
- Moves that must be used don't crash the battle for the Battle AI now
2023-12-29 00:21:28 -08:00
jeffw773 df44486eee - Added less chance of the strong AI being cheesed by a bad Ability 2023-12-28 22:29:13 -08:00
jeffw773 d7881a327a Merge remote-tracking branch 'origin/better_battle_ai_test' into better_battle_ai_test 2023-12-28 22:19:55 -08:00
jeffw773 6ff719d2ec - Made Battle AI able to know if it was given a bad abiltiy and will switch out 2023-12-28 22:19:26 -08:00
JazzMcNade 088fd65159 -Fixed cloning pokemon not capturing the current HP correctly 2023-12-29 00:06:38 -06:00
jeffw773 3a2d4028a5 - Cynthia battle NPC can be set to be any level (VERY HARD) 2023-12-28 12:37:12 -08:00
jeffw773 a6a9dce811 Merge remote-tracking branch 'origin/1.4.1' into better_battle_ai_test 2023-12-28 11:54:21 -08:00
jeffw773 55f0a392e9 - Added Cynthia (with team) from BDSP to NPC battle list
- Added ability to specify AI type before NPC battle
- Allow for different orders of commands for /randombattle
2023-12-28 11:53:49 -08:00
jeffw773 fb3b2a9452 Merge branch 'main' into better_battle_ai_test
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleGeneralActionSelection.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
2023-12-27 08:01:21 -08:00
jeffw773 dff934b308 - Added better considerations for meaningful switch-ins mid battle 2023-12-26 23:31:28 -08:00
jeffw773 0de555610a - Stronger Battle AI test will send in the best matchup after faint
- Also tweaked Tailwind considerations and healing considerations
2023-12-26 13:42:33 -08:00
jeffw773 2e213e0472 - Started fixing Battle AI's switch in/out logic 2023-12-24 00:04:54 -08:00
jeffw773 eeb4000b71 - fixed weather conditions causing null issue for battle AI 2023-12-23 14:40:31 -08:00
JazzMcNade 2fbf3100c5 -Added more status immunity and effect info to the AI 2023-12-23 16:33:52 -06:00
jeffw773 802bf10c6e - fixed switching out for the random NPC battle actor 2023-12-21 15:40:18 -08:00
jeffw773 42f442233a - Added Command to start random NPC battle with random party to use for testing AI 2023-12-21 14:30:35 -08:00
jeffw773 addd063406 - Adjusted how Strong Battle AI calculates what damaging moves to use (based on mainline game battle damage calculations) 2023-12-21 00:06:38 -08:00
jeffw773 bae01e7143 - Strong Battle AI is working! It hungers for the blood of the innocent. Still going to be worked on as time goes on though 2023-12-20 21:34:31 -08:00
jeffw773 fd0e2fa43f - Only 2 main errors left before testing can begin 2023-12-19 22:47:25 -08:00
jeffw773 76260fb998 - removed unused test code 2023-12-18 10:52:01 -08:00
jeffw773 3aa45d83b6 - Made additional changes to the test StrongBattleAI file. Fixed Obstruct spelling and begun converting over the functions to take in battle and request. Still more left to do. 2023-12-18 09:51:09 -08:00
jeffw773 2eb6062b9c - Beginning test of a smarter battle AI for Wild and NPC battles 2023-12-15 23:48:09 -08:00
2268 changed files with 11812 additions and 47542 deletions

No files matched your search

-1
View File
@@ -33,4 +33,3 @@ common/src/main/resources/assets/cobblemon/showdown
# Generated files
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
/.ignored/
+9 -48
View File
@@ -3,23 +3,13 @@
### Additions
- Added over (number here) ruin structures, where you can find Pokémon-themed Armor Trims and Pottery Sherds, Tumblestones, and more.
- Added (number here) fossil structures, where you can brush Suspicious Sand/Gravel for Fossils.
- Added the Data Monitor, Fossil Compartment, and Restoration Tank. Placed in the correct formation, you can use these to create a Restoration Machine where you can bring fossils back to life.
- Added the Data Monitor, Fossil Compartment, and Restoration Tube. Placed in the correct formation, you can use these to create a Restoration Machine where you can bring fossils back to life.
- Added 3 variants of Tumblestones, which can be planted near Lava or Magma to grow harvestable Tumblestone Clusters.
- Added Tumblestone Blocks, a storage block crafted from 9 Tumblestones.
- Added Ancient Poké Balls, which are aesthetic variants crafted from Tumblestones.
- Added the Ancient Feather, Wing, and Jet Balls, which fly further than regular Poké Balls.
- Added the Ancient Heavy, Leaden, and Gigaton Balls, which are heavier and don't fly as far as regular Poké Balls. These will receive more functionality in a future update.
- Added the Ancient Heavy, Leaden, and Gigaton Balls, which are heavier and don't fly as far as regular Poké Balls. These will recieve more functionality in a future update.
- Added Original Trainer functionality. To edit the OT through commands, you must specify originaltrainertype=<Player/NPC>, and originaltrainer=<Username or UUID/NPC Name>
- Added support for planting Apricorn trees, berries, and mints in [Botany Pots](https://modrinth.com/mod/botany-pots)
- Added the hidden "True Vivillionaire" advancement.
- The "Poké Ball" Vivillon variant is now obtainable by evolving a Spewpa after obtaining the "Aspiring Vivillionaire" advancement.
- Added dynamic lighting support for any Species or Forms when using [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) or [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), at this time we don't ship with any defaults for this feature but addon creators can make use of this feature.
- Added 6 Pokémon-themed Pottery Sherds, obtained from ruin structures.
- Added a Pokémon-themed armor trim, obtained from ruin structures.
- You can now find Nether Fire Stone Ore in, surprisingly, the Nether.
- Added effects for many Poké Balls when sending out Pokémon or capturing.
- Added a new send-out ball toss animation that showcases the ball used for the Pokémon.
- Added simple move animations for Pokémon in battle with a proof of concept for more complicated animations.
### Changes
- Pokémon now transition more smoothly between different poses.
@@ -27,39 +17,22 @@
- Added all ores to the modloader ore tags, and added tags for each ore type to the mod (both blocks and items).
- Changed the Destiny Knot to be crafted with a Ghast Tear, rather than a diamond.
- You can now punch grown apricorns to harvest them.
- Renamed the "Vivillionaire" advancement to "Aspiring Vivillionaire"
- Removed the Inferno, Forsaken, Poké Ball, and Void patterns from the Aspiring Vivillionaire advancement.
- Updated movesets to add Gen 9 DLC moves.
- Apricorn Sprouts can now be planted directly on Apricorn leaves to create a new Apricorn of the same color.
- Added tooltips to interaction GUIs to make it clearer what they do.
### Fixes
- 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.
- Fixed battle lang for Poison Touch Ability
- Fixed Cubone not being able to evolve into Alolan Marowak.
- Fixed Squawkabilly forms not being recognized.
- Fixed the bounce animation for Poké Balls being delayed by like, a full second.
- Note blocks now play the correct sound on the Apricorn Plank based blocks.
- Fixed an issue with transformed parts in posers causing whacky positional issues.
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
- Adds a translation string name to the Empty Pokeball entity.
- Fixed very many issues with Bedrock particle effects.
### Developer
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed).
- Rebuilt the scheduling API to more clearly force side choices and temporal frames of reference for tasks.
- Added dialogue API and data registry.
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (albeit in a quick-fix way).
- Rebuilt the scheduling API to more clearly force side choices and allow more local temporal frames of reference for tasks.
- Added dialogue API.
- Opened up the ItemDropEntry class to allow for it to be extended.
- Added a new "advancement" evolution variant that takes an advancement identifier and succeeds if the player has the advancement.
- Made spawning API capable of non-entity spawning
- 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.
### Datapack & Resourcepack Creators
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
- The maximum amount of fossils that can fit in the Fossil Compartment can be adjusted in the config.
- Custom fossils can be defined using a list of items and the resulting Pokémon. An example of `aerodactyl.json`:
```JSON
{
@@ -77,20 +50,9 @@
- `returnItem`: Identifier of an item to return to the player after consumption.
- Added 3 new item tags: `ancient_poke_balls`, `fossils`, and `tumblestones`.
- Added a spawn rules system to modify general spawning behaviour, see the [wiki](https://wiki.cobblemon.com/index.php/Spawn_Rules) for more information.
- Fixed an issue with transformed parts in posers causing whacky positional issues.
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
- Added dialogue datapack folder and /opendialogue command.
- Added the item tag ``cobblemon:held/leaves_leftovers`` this can be used to flag apple-like items that can create leftovers when eaten.
- You can now add support for dynamic lighting implementations, Cobblemon ships with a default implementation for [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) and the [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), add the following to any species or form:
```JSON
{
"lightingData": {
"lightLevel": 14,
"_lightLevelCommentRemoveMe": "Above supports 0 to 15",
"liquidGlowMode": "LAND",
"_liquidGlowModeCommentRemoveMe": "Above supports LAND, UNDERWATER or BOTH"
}
}
```
- Following up on this change ``light_source`` was removed as a possible shoulder effect due to becoming unnecessary and never having had a default implementation.
## [1.4.1 (December 23rd, 2023)](#1-4-1)
@@ -108,7 +70,6 @@
- Added cries to Beldum, Metang and Metagross.
- Added a /bedrockparticle command to run Snowstorm-format particle effects.
- Added data for Dipplin, Fezandipiti, Munkidori, Ogerpon, Okidogi, Poltchageist and Sinistcha.
- Added additional nickname trigger "Grumm" for Inkay's evolution.
### Changes
- Using Potions, Status Heals, Ethers, and Antidotes will now return a glass bottle
@@ -1026,4 +987,4 @@ Thank you so much to all of our community translators that bring the mod to the
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
- Initial release.
+1 -2
View File
@@ -1,7 +1,6 @@
plugins {
base
id("cobblemon.root-conventions")
id ("net.nemerosa.versioning") version "2.8.2"
}
group = "com.cobblemon.mod"
@@ -9,5 +8,5 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
if (isSnapshot) {
version = "$version-SNAPSHOT-${versioning.info.branch}-${versioning.info.build}"
version = "$version-SNAPSHOT"
}
-1
View File
@@ -33,7 +33,6 @@ dependencies {
modApi(libs.molang)
compileOnlyApi(libs.jeiApi)
modCompileOnly(libs.adornFabric)
modCompileOnly(libs.lambDynamicLights) { isTransitive = false }
// For Showdown
modCompileOnly(libs.graal)
@@ -1,96 +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.compat;
import com.cobblemon.mod.common.CobblemonEntities;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
// Java class due to relying on entrypoint on Fabric side
public class LambDynamicLightsCompat {
// This is all called on client side
// These things are checked every tick so try to keep it as free as possible
// That being said it's unlikely school Toshiba person is running dynamic lighting and shaders to enjoy this
// Make sure not to call anything Fabric specific :)
public static void hookCompat() {
// Pokémon entities
DynamicLightHandlers.registerDynamicLightHandler(
CobblemonEntities.POKEMON,
DynamicLightHandler.makeHandler(
pokemon -> resolvedPokemonLightLevel(pokemon, false),
pokemon -> true
)
);
DynamicLightHandlers.registerDynamicLightHandler(
CobblemonEntities.POKEMON,
DynamicLightHandler.makeHandler(
pokemon -> resolvedPokemonLightLevel(pokemon, true),
pokemon -> false
)
);
// Shouldered Pokémon
DynamicLightHandlers.registerDynamicLightHandler(
EntityType.PLAYER,
DynamicLightHandler.makeHandler(
player -> resolvedShoulderLightLevel(player, false),
player -> true
)
);
DynamicLightHandlers.registerDynamicLightHandler(
EntityType.PLAYER,
DynamicLightHandler.makeHandler(
player -> resolvedShoulderLightLevel(player, true),
player -> false
)
);
}
private static int resolvedPokemonLightLevel(PokemonEntity pokemon, boolean underwater) {
return extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
}
private static int resolvedShoulderLightLevel(PlayerEntity player, boolean underwater) {
final Pair<PokemonOnShoulderRenderer.ShoulderData, PokemonOnShoulderRenderer.ShoulderData> shoulderDataPair = PokemonOnShoulderRenderer.shoulderDataOf(player);
final Optional<Integer> leftLightLevel = extractShoulderLightLevel(shoulderDataPair.getLeft(), underwater);
final Optional<Integer> rightLightLevel = extractShoulderLightLevel(shoulderDataPair.getRight(), underwater);
return Math.max(leftLightLevel.orElse(0), rightLightLevel.orElse(0));
}
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
return Optional.empty();
}
return Optional.of(form.getLightingData().getLightLevel());
}
private static Optional<Integer> extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
if (shoulderData == null) {
return Optional.empty();
}
return extractFormLightLevel(shoulderData.getForm(), underwater);
}
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -1,30 +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.mixin;
import com.cobblemon.mod.common.sherds.CobblemonSherds;
import net.minecraft.block.DecoratedPotPatterns;
import net.minecraft.item.Item;
import net.minecraft.registry.RegistryKey;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(DecoratedPotPatterns.class)
public abstract class DecoratedPotPatternsMixin {
@Inject(method = "fromSherd", at=@At("HEAD"), cancellable = true)
private static void cobblemon$getCobblemonSherdTexture(Item sherd,
CallbackInfoReturnable<RegistryKey<String>> cir) {
if (CobblemonSherds.INSTANCE.getSherdToPattern().containsKey(sherd)) {
cir.setReturnValue(CobblemonSherds.INSTANCE.getSherdToPattern().get(sherd));
return;
}
}
}
@@ -40,5 +40,4 @@ public class GameRendererMixin {
}
lastTime = newTime;
}
}
@@ -1,37 +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.mixin;
import com.cobblemon.mod.common.api.snowstorm.ParticleMaterials;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.particle.ParticleTextureSheet;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(ParticleManager.class)
final class ParticleManagerMixin {
@Mutable
@Final
@Shadow
private static List<ParticleTextureSheet> PARTICLE_TEXTURE_SHEETS;
@Inject(at = @At("RETURN"), method = "<clinit>")
private static void lodestone$addTypes(CallbackInfo ci) {
PARTICLE_TEXTURE_SHEETS = ImmutableList.<ParticleTextureSheet>builder().addAll(PARTICLE_TEXTURE_SHEETS)
.add(ParticleMaterials.INSTANCE.getADD(), ParticleMaterials.INSTANCE.getALPHA(), ParticleMaterials.INSTANCE.getBLEND(), ParticleMaterials.INSTANCE.getOPAQUE()).build();
}
}
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents;
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
import com.cobblemon.mod.common.api.storage.NoPokemonStoreException;
import com.cobblemon.mod.common.api.storage.party.PartyStore;
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.pokemon.Pokemon;
import com.cobblemon.mod.common.util.CompoundTagExtensionsKt;
import com.cobblemon.mod.common.util.DataKeys;
@@ -23,8 +22,11 @@ import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.world.World;
import org.objectweb.asm.Opcodes;
@@ -38,6 +40,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Objects;
import java.util.UUID;
import static com.cobblemon.mod.common.util.LocalizationUtilsKt.lang;
@Mixin(PlayerEntity.class)
public abstract class PlayerMixin extends LivingEntity {
@@ -140,7 +144,7 @@ public abstract class PlayerMixin extends LivingEntity {
)
public void onEatFood(World world, ItemStack stack, CallbackInfoReturnable<ItemStack> cir) {
if (!getWorld().isClient) {
if (stack.isIn(CobblemonItemTags.LEAVES_LEFTOVERS) && getWorld().random.nextDouble() < Cobblemon.config.getAppleLeftoversChance()) {
if (stack.getItem() == Items.APPLE && getWorld().random.nextDouble() < Cobblemon.config.getAppleLeftoversChance()) {
ItemStack leftovers = new ItemStack(CobblemonItems.LEFTOVERS);
ServerPlayerEntity player = Objects.requireNonNull(getServer()).getPlayerManager().getPlayer(uuid);
assert player != null;
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.net.serializers.IdentifierDataSerializer
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
@@ -46,13 +45,8 @@ import com.cobblemon.mod.common.api.scheduling.ServerRealTimeTaskTracker
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawningProspector
import com.cobblemon.mod.common.api.spawning.WorldSlice
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.AreaSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.AreaSpawningInput
import com.cobblemon.mod.common.api.spawning.prospecting.SpawningProspector
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.starter.StarterHandler
import com.cobblemon.mod.common.api.storage.PokemonStoreManager
import com.cobblemon.mod.common.api.storage.adapter.conversions.ReforgedConversion
@@ -103,10 +97,15 @@ import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.sherds.CobblemonSherds
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.*
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.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -130,7 +129,6 @@ import net.minecraft.item.Items
import net.minecraft.item.NameTagItem
import net.minecraft.registry.RegistryKey
import net.minecraft.util.WorldSavePath
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
@@ -237,7 +235,6 @@ object Cobblemon {
TrackedDataHandlerRegistry.register(Vec3DataSerializer)
TrackedDataHandlerRegistry.register(StringSetDataSerializer)
TrackedDataHandlerRegistry.register(PoseTypeDataSerializer)
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
}
@@ -388,8 +385,6 @@ object Cobblemon {
true
).ifSuccessful { it.mute = true }
}
CobblemonSherds.registerSherds()
}
fun getLevel(dimension: RegistryKey<World>): World? {
@@ -12,9 +12,9 @@ import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
import com.cobblemon.mod.common.block.entity.PCBlockEntity
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
import com.cobblemon.mod.common.block.entity.FossilAnalyzerBlockEntity
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
import com.cobblemon.mod.common.block.entity.fossil.FossilCompartmentBlockEntity
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
import com.cobblemon.mod.common.block.entity.fossil.FossilTubeBlockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
import com.cobblemon.mod.common.block.entity.*
import com.cobblemon.mod.common.platform.PlatformRegistry
@@ -48,27 +48,21 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val FOSSIL_MULTIBLOCK: BlockEntityType<FossilMultiblockEntity> = this.create("fossil_multiblock",
BlockEntityType.Builder.create({ pos, state -> FossilMultiblockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.MONITOR
CobblemonBlocks.FOSSIL_MONITOR
).build(null)
)
@JvmField
val RESTORATION_TANK: BlockEntityType<RestorationTankBlockEntity> = this.create("restoration_tank",
BlockEntityType.Builder.create({ pos, state -> RestorationTankBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.RESTORATION_TANK
val FOSSIL_TUBE: BlockEntityType<FossilTubeBlockEntity> = this.create("fossil_tube",
BlockEntityType.Builder.create({ pos, state -> FossilTubeBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.FOSSIL_TUBE
).build(null)
)
@JvmField
val FOSSIL_ANALYZER: BlockEntityType<FossilAnalyzerBlockEntity> = this.create("fossil_analyzer",
BlockEntityType.Builder.create({ pos, state -> FossilAnalyzerBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.FOSSIL_ANALYZER
val FOSSIL_COMPARTMENT: BlockEntityType<FossilCompartmentBlockEntity> = this.create("fossil_compartment",
BlockEntityType.Builder.create({ pos, state -> FossilCompartmentBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
CobblemonBlocks.FOSSIL_COMPARTMENT
).build(null)
)
@JvmField
val DYNITE_ORE: BlockEntityType<DyniteOreBlockEntity> = this.create("dynite_ore",
BlockEntityType.Builder.create(::DyniteOreBlockEntity, CobblemonBlocks.DYNITE_ORE)
.build(null)
)
}
@@ -11,9 +11,9 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.block.*
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.block.FossilAnalyzerBlock
import com.cobblemon.mod.common.block.MonitorBlock
import com.cobblemon.mod.common.block.RestorationTankBlock
import com.cobblemon.mod.common.block.fossilmachine.FossilCompartmentBlock
import com.cobblemon.mod.common.block.fossilmachine.FossilMonitorBlock
import com.cobblemon.mod.common.block.fossilmachine.FossilTubeBlock
import com.cobblemon.mod.common.mixin.invoker.DoorBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.PressurePlateBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
@@ -27,7 +27,6 @@ import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
import net.minecraft.block.*
import net.minecraft.block.enums.Instrument
import net.minecraft.block.piston.PistonBehavior
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.registry.Registries
@@ -57,8 +56,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val FIRE_STONE_ORE = evolutionStoneOre("fire_stone_ore")
@JvmField
val NETHER_FIRE_STONE_ORE = evolutionStoneOre("nether_fire_stone_ore")
@JvmField
val ICE_STONE_ORE = evolutionStoneOre("ice_stone_ore")
@JvmField
val LEAF_STONE_ORE = evolutionStoneOre("leaf_stone_ore")
@@ -96,15 +93,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val DEEPSLATE_WATER_STONE_ORE = this.deepslateEvolutionStoneOre("deepslate_water_stone_ore")
@JvmField
val DYNITE_ORE = this.create("dynite_ore", DyniteOreBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.BRIGHT_RED)
.strength(4.5f, 3f)
.requiresTool()
.sounds(BlockSoundGroup.METAL)
))
// Apricorns
@JvmField
val APRICORN_LOG = log("apricorn_log", arg2 = MapColor.BROWN)
@@ -115,17 +103,17 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val STRIPPED_APRICORN_WOOD = log("stripped_apricorn_wood")
@JvmField
val APRICORN_PLANKS = this.create("apricorn_planks", Block(AbstractBlock.Settings.create().mapColor(MapColor.DIRT_BROWN).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
val APRICORN_PLANKS = this.create("apricorn_planks", Block(AbstractBlock.Settings.create().mapColor(MapColor.DIRT_BROWN).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
@JvmField
val APRICORN_LEAVES = leaves("apricorn_leaves")
@JvmField
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
val APRICORN_FENCE = this.create("apricorn_fence", FenceBlock(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
@JvmField
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD), APRICORN_WOOD_TYPE))
val APRICORN_FENCE_GATE = this.create("apricorn_fence_gate", FenceGateBlock(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD), APRICORN_WOOD_TYPE))
@JvmField // Note At the time of 1.20.0 we don't need our own BlockSetType for Apricorn wood
val APRICORN_BUTTON = this.create("apricorn_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
@JvmField
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(PressurePlateBlock.ActivationRule.EVERYTHING, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD), APRICORN_BLOCK_SET_TYPE))
val APRICORN_PRESSURE_PLATE = this.create("apricorn_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(PressurePlateBlock.ActivationRule.EVERYTHING, AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD), APRICORN_BLOCK_SET_TYPE))
@JvmField
val APRICORN_SIGN = this.create("apricorn_sign", CobblemonSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_SIGN), APRICORN_WOOD_TYPE))
@JvmField
@@ -135,13 +123,13 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val APRICORN_WALL_HANGING_SIGN = this.create("apricorn_wall_hanging_sign", CobblemonWallHangingSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_HANGING_SIGN), APRICORN_WOOD_TYPE))
@JvmField
val APRICORN_SLAB = this.create("apricorn_slab", SlabBlock(AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
val APRICORN_SLAB = this.create("apricorn_slab", SlabBlock(AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
@JvmField
val APRICORN_STAIRS = this.create("apricorn_stairs", StairsBlockInvoker.`cobblemon$create`(APRICORN_PLANKS.defaultState, AbstractBlock.Settings.copy(APRICORN_PLANKS)))
@JvmField
val APRICORN_DOOR = this.create("apricorn_door", DoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque(), APRICORN_BLOCK_SET_TYPE))
val APRICORN_DOOR = this.create("apricorn_door", DoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(APRICORN_PLANKS.defaultMapColor).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque(), APRICORN_BLOCK_SET_TYPE))
@JvmField
val APRICORN_TRAPDOOR = this.create("apricorn_trapdoor", TrapdoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque().allowsSpawning { _, _, _, _ -> false }, APRICORN_BLOCK_SET_TYPE))
val APRICORN_TRAPDOOR = this.create("apricorn_trapdoor", TrapdoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque().allowsSpawning { _, _, _, _ -> false }, APRICORN_BLOCK_SET_TYPE))
private val PLANT_PROPERTIES = AbstractBlock.Settings.create().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY)
@JvmField
@@ -169,52 +157,49 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
@JvmField
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
@JvmField
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
@JvmField
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
@JvmField
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
@JvmField
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.instrument(Instrument.BASEDRUM)
))
@JvmField
val SKY_TUMBLESTONE_BLOCK = this.create("sky_tumblestone_block", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.instrument(Instrument.BASEDRUM)
))
@JvmField
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(AbstractBlock.Settings.create()
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.instrument(Instrument.BASEDRUM)
))
@JvmField
@@ -233,41 +218,38 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
@JvmField
val MONITOR = create(
"monitor",
MonitorBlock(
val FOSSIL_MONITOR = create(
"fossil_monitor",
FossilMonitorBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.requiresTool()
.strength(5.0F, 6.0F)
.luminance { if (it.get(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 15 else 0 }
.strength(2f)
.luminance { if (it.get(FossilMonitorBlock.PROGRESS) > 0) 15 else 0 }
)
)
@JvmField
val FOSSIL_ANALYZER = create(
"fossil_analyzer",
FossilAnalyzerBlock(
val FOSSIL_COMPARTMENT = create(
"fossil_compartment",
FossilCompartmentBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.requiresTool()
.strength(5.0F, 6.0F)
.strength(2f)
.nonOpaque()
)
)
@JvmField
val RESTORATION_TANK = create(
"restoration_tank",
RestorationTankBlock(
val FOSSIL_TUBE = create(
"fossil_tube",
FossilTubeBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.requiresTool()
.strength(5.0F, 6.0F)
.strength(2f)
.nonOpaque()
)
)
@@ -46,6 +46,7 @@ object CobblemonCommands {
ClearPartyCommand.register(dispatcher)
ClearPCCommand.register(dispatcher)
PokemonRestartCommand.register(dispatcher)
RandomBattleCommand.register(dispatcher)
BedrockParticleCommand.register(dispatcher)
OpenDialogueCommand.register(dispatcher)
}
@@ -11,9 +11,6 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.boat.CobblemonBoatEntity
import com.cobblemon.mod.common.entity.boat.CobblemonChestBoatEntity
import com.cobblemon.mod.common.entity.fallingstar.FallingStarEntity
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.platform.PlatformRegistry
@@ -68,33 +65,6 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
.build(CHEST_BOAT_KEY.toString())
)
@JvmField
val GENERIC_BEDROCK_ENTITY_KEY = cobblemonResource("generic_bedrock")
@JvmField
val GENERIC_BEDROCK_ENTITY: EntityType<GenericBedrockEntity> = this.create(
GENERIC_BEDROCK_ENTITY_KEY.path,
EntityType.Builder.create({ _, world -> GenericBedrockEntity(world) }, SpawnGroup.MISC)
.build(GENERIC_BEDROCK_ENTITY_KEY.toString())
)
@JvmField
val FALLING_STAR_ENTITY_KEY = cobblemonResource("falling_star")
@JvmField
val FALLING_STAR_ENTITY: EntityType<FallingStarEntity> = this.create(
FALLING_STAR_ENTITY_KEY.path,
EntityType.Builder.create({ _, world -> FallingStarEntity(world) }, SpawnGroup.MISC)
.build(FALLING_STAR_ENTITY_KEY.toString())
)
@JvmField
val POKE_BOBBER_KEY = cobblemonResource("poke_bobber")
@JvmField
val POKE_BOBBER: EntityType<PokeRodFishingBobberEntity> = this.create(
POKE_BOBBER_KEY.path,
EntityType.Builder.create(::PokeRodFishingBobberEntity, SpawnGroup.MISC).setDimensions(0.5F, 0.5F).maxTrackingRange(10)
.build(POKE_BOBBER_KEY.toString())
)
fun registerAttributes(consumer: (EntityType<out LivingEntity>, DefaultAttributeContainer.Builder) -> Unit) {
consumer(POKEMON, PokemonEntity.createAttributes())
}
@@ -18,7 +18,6 @@ import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
import com.cobblemon.mod.common.item.battle.DireHitItem
import com.cobblemon.mod.common.item.battle.GuardSpecItem
import com.cobblemon.mod.common.item.battle.XStatItem
@@ -29,7 +28,6 @@ import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.PotionItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
@@ -38,14 +36,12 @@ import net.minecraft.block.Block
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.effect.StatusEffectInstance
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.item.*
import net.minecraft.item.BlockItem
import net.minecraft.item.FoodComponent
import net.minecraft.item.HangingSignItem
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.item.SignItem
import net.minecraft.item.SmithingTemplateItem
import net.minecraft.item.StewItem
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
@@ -180,19 +176,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val WHITE_APRICORN = apricornItem("white", ApricornItem(CobblemonBlocks.WHITE_APRICORN))
@JvmField
val RED_APRICORN_SEED = apricornSeedItem("red", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING, CobblemonBlocks.RED_APRICORN))
val RED_APRICORN_SEED = apricornSeedItem("red", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING))
@JvmField
val YELLOW_APRICORN_SEED = apricornSeedItem("yellow", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING, CobblemonBlocks.YELLOW_APRICORN))
val YELLOW_APRICORN_SEED = apricornSeedItem("yellow", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING))
@JvmField
val GREEN_APRICORN_SEED = apricornSeedItem("green", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING, CobblemonBlocks.GREEN_APRICORN))
val GREEN_APRICORN_SEED = apricornSeedItem("green", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING))
@JvmField
val BLUE_APRICORN_SEED = apricornSeedItem("blue", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING, CobblemonBlocks.BLUE_APRICORN))
val BLUE_APRICORN_SEED = apricornSeedItem("blue", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING))
@JvmField
val PINK_APRICORN_SEED = apricornSeedItem("pink", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING, CobblemonBlocks.PINK_APRICORN))
val PINK_APRICORN_SEED = apricornSeedItem("pink", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING))
@JvmField
val BLACK_APRICORN_SEED = apricornSeedItem("black", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING, CobblemonBlocks.BLACK_APRICORN))
val BLACK_APRICORN_SEED = apricornSeedItem("black", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING))
@JvmField
val WHITE_APRICORN_SEED = apricornSeedItem("white", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING, CobblemonBlocks.WHITE_APRICORN))
val WHITE_APRICORN_SEED = apricornSeedItem("white", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING))
@JvmField
val APRICORN_LOG = blockItem("apricorn_log", CobblemonBlocks.APRICORN_LOG)
@@ -233,11 +229,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", HangingSignItem(CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
@JvmField
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
val FOSSIL_TUBE = blockItem("fossil_tube", CobblemonBlocks.FOSSIL_TUBE)
@JvmField
val FOSSIL_ANALYZER = blockItem("fossil_analyzer", CobblemonBlocks.FOSSIL_ANALYZER)
val FOSSIL_COMPARTMENT = blockItem("fossil_compartment", CobblemonBlocks.FOSSIL_COMPARTMENT)
@JvmField
val MONITOR = blockItem("monitor", CobblemonBlocks.MONITOR)
val FOSSIL_MONITOR = blockItem("fossil_monitor", CobblemonBlocks.FOSSIL_MONITOR)
@JvmField
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE)
@JvmField
@@ -577,12 +573,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val DEEPSLATE_WATER_STONE_ORE = blockItem("deepslate_water_stone_ore", CobblemonBlocks.DEEPSLATE_WATER_STONE_ORE)
@JvmField
val DRIPSTONE_MOON_STONE_ORE = blockItem("dripstone_moon_stone_ore", CobblemonBlocks.DRIPSTONE_MOON_STONE_ORE)
@JvmField
val NETHER_FIRE_STONE_ORE = blockItem("nether_fire_stone_ore", CobblemonBlocks.NETHER_FIRE_STONE_ORE)
@JvmField
val DYNITE_ORE = blockItem("dynite_ore", CobblemonBlocks.DYNITE_ORE)
@JvmField
val DAWN_STONE = noSettingsItem("dawn_stone")
@JvmField
@@ -606,16 +596,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
// Held Items
@JvmField
val ABILITY_SHIELD = heldItem("ability_shield")
@JvmField
val ABSORB_BULB = heldItem("absorb_bulb")
@JvmField
val AIR_BALLOON = heldItem("air_balloon")
@JvmField
val ASSAULT_VEST = heldItem("assault_vest")
@JvmField
val BIG_ROOT = blockItem("big_root", CobblemonBlocks.BIG_ROOT)
@JvmField
val BINDING_BAND = heldItem("binding_band")
@JvmField
val BLACK_BELT = heldItem("black_belt")
@JvmField
val BLACK_GLASSES = heldItem("black_glasses")
@JvmField
val BLACK_SLUDGE = heldItem("black_sludge")
@JvmField
val BLUNDER_POLICY = heldItem("blunder_policy")
@JvmField
val CELL_BATTERY = heldItem("cell_battery")
@JvmField
val CHARCOAL = heldItem("charcoal_stick", remappedName = "charcoal")
@JvmField
val CHOICE_BAND = heldItem("choice_band")
@@ -626,26 +628,64 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val CLEANSE_TAG = heldItem("cleanse_tag")
@JvmField
val CLEAR_AMULET = heldItem("clear_amulet")
@JvmField
val COVERT_CLOAK = heldItem("covert_cloak")
@JvmField
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val DRAGON_FANG = heldItem("dragon_fang")
@JvmField
val EJECT_BUTTON = heldItem("eject_button")
@JvmField
val EJECT_PACK = heldItem("eject_pack")
@JvmField
val EVERSTONE = heldItem("everstone")
@JvmField
val EVIOLITE = heldItem("eviolite")
@JvmField
val EXP_SHARE = heldItem("exp_share")
@JvmField
val EXPERT_BELT = heldItem("expert_belt")
@JvmField
val FAIRY_FEATHER = heldItem("fairy_feather")
@JvmField
val FLAME_ORB = heldItem("flame_orb")
@JvmField
val FLOAT_STONE = heldItem("float_stone")
@JvmField
val FOCUS_BAND = heldItem("focus_band")
@JvmField
val FOCUS_SASH = heldItem("focus_sash")
@JvmField
val GRIP_CLAW = heldItem("grip_claw")
@JvmField
val HARD_STONE = heldItem("hard_stone")
@JvmField
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
@JvmField
val IRON_BALL = heldItem("iron_ball")
@JvmField
val LAGGING_TAIL = heldItem("lagging_tail")
@JvmField
val LEFTOVERS = heldItem("leftovers")
@JvmField
val LIFE_ORB = heldItem("life_orb")
@JvmField
val LIGHT_BALL = heldItem("light_ball")
@JvmField
val LIGHT_CLAY = heldItem("light_clay")
@JvmField
val LOADED_DICE = heldItem("loaded_dice")
@JvmField
val LUCKY_EGG = heldItem("lucky_egg")
@JvmField
val LUMINOUS_MOSS = heldItem("luminous_moss")
@JvmField
val MAGNET = heldItem("magnet")
@JvmField
val METRONOME = heldItem("metronome")
@JvmField
val MIRACLE_SEED = heldItem("miracle_seed")
@JvmField
val MUSCLE_BAND = heldItem("muscle_band")
@@ -656,14 +696,42 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val POISON_BARB = heldItem("poison_barb")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
@JvmField
val PROTECTIVE_PADS = heldItem("protective_pads")
@JvmField
val PUNCHING_GLOVE = heldItem("punching_glove")
@JvmField
val QUICK_CLAW = heldItem("quick_claw")
@JvmField
val RED_CARD = heldItem("red_card")
@JvmField
val RING_TARGET = heldItem("ring_target")
@JvmField
val ROCKY_HELMET = heldItem("rocky_helmet")
@JvmField
val ROOM_SERVICE = heldItem("room_service")
@JvmField
val SAFETY_GOGGLES = heldItem("safety_goggles")
@JvmField
val SCOPE_LENS = heldItem("scope_lens")
@JvmField
val SHARP_BEAK = heldItem("sharp_beak")
@JvmField
val SHED_SHELL = heldItem("shed_shell")
@JvmField
val SHELL_BELL = heldItem("shell_bell")
@JvmField
val SILK_SCARF = heldItem("silk_scarf")
@JvmField
val SILVER_POWDER = heldItem("silver_powder")
@@ -672,10 +740,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val SPELL_TAG = heldItem("spell_tag")
@JvmField
val SMOKE_BALL = heldItem("smoke_ball")
@JvmField
val STICKY_BARB = heldItem("sticky_barb")
@JvmField
val TERRAIN_EXTENDER = heldItem("terrain_extender")
@JvmField
val THROAT_SPRAY = heldItem("throat_spray")
@JvmField
val TOXIC_ORB = heldItem("toxic_orb")
@JvmField
val TWISTED_SPOON = heldItem("twisted_spoon")
@JvmField
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
@JvmField
val WEAKNESS_POLICY = heldItem("weakness_policy")
@JvmField
val WIDE_LENS = heldItem("wide_lens")
@JvmField
val WISE_GLASSES = heldItem("wise_glasses")
@JvmField
val ZOOM_LENS = heldItem("zoom_lens")
@JvmField
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 1F)
@JvmField
val MIRROR_HERB = compostableHeldItem("mirror_herb", null, 1F)
@@ -690,29 +776,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val QUICK_POWDER = heldItem("quick_powder")
@JvmField
val DESTINY_KNOT = heldItem("destiny_knot")
val DAMP_ROCK = heldItem("damp_rock")
@JvmField
val EVERSTONE = heldItem("everstone")
val HEAT_ROCK = heldItem("heat_rock")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
val SMOOTH_ROCK = heldItem("smooth_rock")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
@JvmField
val FLAME_ORB = heldItem("flame_orb")
@JvmField
val LIFE_ORB = heldItem("life_orb")
@JvmField
val TOXIC_ORB = heldItem("toxic_orb")
@JvmField
val SMOKE_BALL = heldItem("smoke_ball")
val ICY_ROCK = heldItem("icy_rock")
// Mulch
@JvmField
@@ -776,8 +846,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val HELIX_SHERD = noSettingsItem("helix_sherd")
@JvmField
val NOSTALGIC_SHERD = noSettingsItem("nostalgic_sherd")
@JvmField
val SUSPICIOUS_SHERD = noSettingsItem("suspicious_sherd")
@JvmField
val TUMBLESTONE = this.create("tumblestone", TumblestoneItem(Item.Settings(), CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE))
@@ -820,15 +888,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
@JvmField
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
"automaton_armor_trim_smithing_template",
SmithingTemplateItem.of(CobblemonArmorTrims.AUTOMATON)
)
@JvmField
val POKEROD = pokerodItem("pokerod")
// Misc
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
@@ -850,12 +909,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
private fun candyItem(name: String, calculator: CandyItem.Calculator): CandyItem = this.create(name, CandyItem(calculator))
private fun pokerodItem(name: String): PokerodItem {
var settings = Item.Settings()
val item = create(name, PokerodItem(settings))
return item
}
private fun heldItem(name: String, remappedName: String? = null): CobblemonItem = create(
name,
CobblemonItem(Item.Settings()).also {
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.animation.PlayPoseableAnimationHandler
import com.cobblemon.mod.common.client.net.battle.BattleApplyPassResponseHandler
import com.cobblemon.mod.common.client.net.battle.BattleCaptureEndHandler
import com.cobblemon.mod.common.client.net.battle.BattleCaptureShakeHandler
@@ -38,10 +37,8 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
@@ -51,6 +48,7 @@ import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.PokemonCryHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
import com.cobblemon.mod.common.client.net.spawn.SpawnExtraDataEntityHandler
import com.cobblemon.mod.common.client.net.starter.StarterUIPacketHandler
@@ -75,7 +73,6 @@ import com.cobblemon.mod.common.client.net.trade.TradeOfferNotificationHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.*
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
@@ -88,8 +85,6 @@ import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPack
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
@@ -102,8 +97,8 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.Add
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.RemoveEvolutionPacket
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.net.messages.client.sound.PokemonCryPacket
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
@@ -319,15 +314,12 @@ object CobblemonNetwork : NetworkManager {
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
this.createClientBound(SpawnSnowstormEntityParticlePacket.ID, SpawnSnowstormEntityParticlePacket::decode, SpawnSnowstormEntityParticleHandler)
this.createClientBound(RunPosableMoLangPacket.ID, RunPosableMoLangPacket::decode, RunPosableMoLangHandler)
// Hax
this.createClientBound(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler)
this.createClientBound(SpawnPokemonPacket.ID, SpawnPokemonPacket::decode, SpawnExtraDataEntityHandler())
this.createClientBound(SpawnPokeballPacket.ID, SpawnPokeballPacket::decode, SpawnExtraDataEntityHandler())
this.createClientBound(ToastPacket.ID, ToastPacket::decode, ToastPacketHandler)
this.createClientBound(SpawnGenericBedrockPacket.ID, SpawnGenericBedrockPacket::decode, SpawnExtraDataEntityHandler())
// Trade packets
this.createClientBound(TradeAcceptanceChangedPacket.ID, TradeAcceptanceChangedPacket::decode, TradeAcceptanceChangedHandler)
@@ -345,7 +337,7 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler)
// Behaviours
this.createClientBound(PlayPoseableAnimationPacket.ID, PlayPoseableAnimationPacket::decode, PlayPoseableAnimationHandler)
this.createClientBound(PokemonCryPacket.ID, PokemonCryPacket::decode, PokemonCryHandler)
// Move select packets
this.createClientBound(OpenMoveCallbackPacket.ID, OpenMoveCallbackPacket::decode, OpenMoveCallbackHandler)
@@ -55,10 +55,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val POKE_BALL_SEND_OUT = this.create("poke_ball.send_out")
@JvmField
val POKE_BALL_RECALL = this.create("poke_ball.recall")
@JvmField
val POKE_BALL_THROW = this.create("poke_ball.throw")
@JvmField
val POKE_BALL_TRAIL = this.create("poke_ball.trail")
@JvmField
val ITEM_USE = this.create("item.use")
@@ -66,8 +62,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val CAN_EVOLVE = this.create("pokemon.can_evolve")
@JvmField
val EVOLVING = this.create("pokemon.evolving")
@JvmField
val EVOLVE = this.create("evolution.evolve")
@JvmField
val PVN_BATTLE = this.create("battle.pvn.default")
@@ -129,43 +123,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val TUMBLESTONE_STEP = this.create("tumblestone.step")
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@JvmField
val IMPACT_BUG = this.create("impact.bug")
@JvmField
val IMPACT_DARK = this.create("impact.dark")
@JvmField
val IMPACT_DRAGON = this.create("impact.dragon")
@JvmField
val IMPACT_ELECTRIC = this.create("impact.electric")
@JvmField
val IMPACT_FAIRY = this.create("impact.fairy")
@JvmField
val IMPACT_FIGHTING = this.create("impact.fighting")
@JvmField
val IMPACT_FIRE = this.create("impact.fire")
@JvmField
val IMPACT_FLYING = this.create("impact.flying")
@JvmField
val IMPACT_GHOST = this.create("impact.ghost")
@JvmField
val IMPACT_GRASS = this.create("impact.grass")
@JvmField
val IMPACT_GROUND = this.create("impact.ground")
@JvmField
val IMPACT_ICE = this.create("impact.ice")
@JvmField
val IMPACT_POISON = this.create("impact.poison")
@JvmField
val IMPACT_PSYCHIC = this.create("impact.psychic")
@JvmField
val IMPACT_ROCK = this.create("impact.rock")
@JvmField
val IMPACT_STEEL = this.create("impact.steel")
@JvmField
val IMPACT_WATER = this.create("impact.water")
@JvmField
val TUMBLESTONE_SOUNDS = BlockSoundGroup(1f, 1f,
TUMBLESTONE_BREAK,
@@ -8,13 +8,9 @@
package com.cobblemon.mod.common.api.battles.model
import com.bedrockk.molang.runtime.MoLangEnvironment
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.Environment
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
@@ -22,8 +18,6 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.text.red
@@ -102,6 +96,7 @@ open class PokemonBattle(
val chatLog = mutableListOf<Text>()
var started = false
var ended = false
var simulation = false
// TEMP battle showcase stuff
var announcingRules = false
var turn: Int = 1
@@ -211,7 +206,8 @@ open class PokemonBattle(
fun end() {
ended = true
this.actors.forEach { actor ->
// award XP if it is not a simulation battle
if (simulation == false) this.actors.forEach { actor ->
val faintedPokemons = actor.pokemonList.filter { it.health <= 0 }
actor.getSide().getOppositeSide().actors.forEach { opponent ->
val opponentNonFaintedPokemons = opponent.pokemonList.filter { it.health > 0 }
@@ -470,13 +466,4 @@ open class PokemonBattle(
LOGGER.error("Missing interpretation on '{}' action: \nPublic » {}\nPrivate » {}", publicMessage.id, publicMessage.rawMessage, privateMessage.rawMessage)
return Text.literal("Missing interpretation on '${publicMessage.id}' action please report to the developers").red()
}
fun addQueryFunctions(queryStruct: QueryStruct): QueryStruct {
queryStruct.addFunction("pvp") { DoubleValue(isPvP) }
queryStruct.addFunction("pvn") { DoubleValue(isPvN) }
queryStruct.addFunction("pvw") { DoubleValue(isPvW) }
queryStruct.addFunction("has_rule") { params -> DoubleValue(params.getString(0) in format.ruleSet) }
return queryStruct
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.battles.PassActionResponse
import com.cobblemon.mod.common.battles.ai.StrongBattleAI
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
@@ -20,7 +21,7 @@ import java.util.UUID
abstract class AIBattleActor(
gameId: UUID,
pokemonList: List<BattlePokemon>,
val battleAI: BattleAI
val battleAI: BattleAI = StrongBattleAI(0)
) : BattleActor(gameId, pokemonList.toMutableList()) {
override fun sendUpdate(packet: NetworkPacket<*>) {
super.sendUpdate(packet)
@@ -33,7 +33,6 @@ import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEv
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
@@ -101,9 +100,6 @@ object CobblemonEvents {
@JvmField
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
@JvmField
/** CLIENT ONLY! */
val POKEMON_INTERACTION_GUI_CREATION = EventObservable<PokemonInteractionGUICreationEvent>()
@JvmField
val POKEMON_ENTITY_SAVE = EventObservable<PokemonEntitySaveEvent>()
@JvmField
@@ -157,8 +153,4 @@ object CobblemonEvents {
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
@JvmField
val BIG_ROOT_PROPAGATED = CancelableObservable<BigRootPropagatedEvent>()
@JvmField
val HELD_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
@JvmField
val HELD_ITEM_POST = EventObservable<HeldItemEvent.Post>()
}
@@ -1,56 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.item.ItemStack
/**
* The base for all the events related to held items.
*
* @see [Pre]
* @see [Post]
*/
interface HeldItemEvent {
/**
* The [Pokemon] triggering this event.
*/
val pokemon: Pokemon
/**
* Fired at the start of [Pokemon.swapHeldItem].
*
* This event should be used to mutate the results of this transaction.
*
* Canceling this event will prevent the operation and causes the return to be item attempted to be given originally.
*
* @property pokemon The [Pokemon] triggering this event.
* @property receiving The [ItemStack] being sent received from the transaction. By default, this is the item that triggered the interaction.
* @property returning The [ItemStack] being sent back from the transaction. By default, this is the currently held item.
* @property decrement If the operation should decrement the [receiving] [ItemStack.count], this is handled in the implementation.
*
* @see [Post]
*/
data class Pre(override val pokemon: Pokemon, var receiving: ItemStack, var returning: ItemStack, var decrement: Boolean) : HeldItemEvent, Cancelable()
/**
* Fired at the end of [Pokemon.swapHeldItem].
*
* @property pokemon The [Pokemon] triggering this event.
* @property received The [ItemStack] considered as received and set to the [pokemon]. This is a copy and mutation will not be taken into account.
* @property returned The [ItemStack] returned by the method call. This is a copy and mutation will not be taken into account.
* @property decremented If the [received] [ItemStack] got decremented.
*
* @see [Pre]
*/
data class Post(override val pokemon: Pokemon, val received: ItemStack, val returned: ItemStack, val decremented: Boolean) : HeldItemEvent
}
@@ -1,42 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon.interaction
import com.cobblemon.mod.common.client.gui.interact.wheel.InteractWheelOption
import com.cobblemon.mod.common.client.gui.interact.wheel.Orientation
import com.google.common.collect.Multimap
import java.util.UUID
data class PokemonInteractionGUICreationEvent(
val pokemonID: UUID,
val mountShoulder: Boolean,
val options: Multimap<Orientation, InteractWheelOption>
) {
fun addFillingOption(option: InteractWheelOption) {
options.put(getNextFreeOrientation(), option)
}
fun addOption(orientation: Orientation, option: InteractWheelOption) {
options.put(orientation, option)
}
private fun getNextFreeOrientation(): Orientation {
var largest = Orientation.TOP_LEFT
for(orientation in Orientation.values()) {
if(!options.containsKey(orientation)) {
return orientation
} else {
if(options.get(orientation).size < options.get(largest).size) {
largest = orientation
}
}
}
return largest
}
}
@@ -19,8 +19,7 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.block.Block
@@ -52,30 +51,7 @@ object MoLangFunctions {
"is_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() != null) },
"to_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0) },
"to_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toIntOrNull() ?: 0) },
"to_string" to java.util.function.Function { params -> StringValue(params.get<MoValue>(0).asString()) },
"do_effect_walks" to java.util.function.Function { _ ->
DoubleValue(Cobblemon.config.walkingInBattleAnimations)
},
"random" to java.util.function.Function { params ->
val options = mutableListOf<MoValue>()
var index = 0
while (params.contains(index)) {
options.add(params.get(index))
index++
}
return@Function options.random() // Can throw an exception if they specified no args. They'd be idiots though.
},
"curve" to java.util.function.Function { params ->
val curveName = params.getString(0)
val curve = WaveFunctions.functions[curveName] ?: throw IllegalArgumentException("Unknown curve: $curveName")
return@Function ObjectValue(curve)
},
"array" to java.util.function.Function { params ->
val values = params.params
val array = ArrayStruct(hashMapOf())
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
return@Function array
}
"to_string" to java.util.function.Function { params -> StringValue(params.get<MoValue>(0).asString()) }
)
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -134,11 +110,6 @@ object MoLangFunctions {
return this
}
fun <T : QueryStruct> T.addFunction(name: String, function: java.util.function.Function<MoParams, Any>): T {
this.functions[name] = function
return this
}
fun MoLangEnvironment.getQueryStruct(name: String = "query") = structs.getOrPut(name) { QueryStruct(hashMapOf()) } as QueryStruct
fun MoLangRuntime.setup(): MoLangRuntime {
@@ -8,18 +8,14 @@
package com.cobblemon.mod.common.api.moves
import com.bedrockk.molang.runtime.struct.MoStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.MoveTarget
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.util.lang
import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName
import net.minecraft.text.MutableText
@@ -52,21 +48,8 @@ open class MoveTemplate(
val priority: Int,
val critRatio: Double,
val effectChances: Array<Double>,
val actionEffect: ActionEffectTimeline?
val flags: Set<String>
) {
val struct: MoStruct by lazy {
QueryStruct(hashMapOf())
.addFunction("name") { StringValue(name) }
.addFunction("type") { StringValue(elementalType.name) }
.addFunction("damage_category") { StringValue(damageCategory.name) }
.addFunction("power") { DoubleValue(power) }
.addFunction("target") { StringValue(target.name) }
.addFunction("accuracy") { DoubleValue(accuracy) }
.addFunction("pp") { DoubleValue(pp) }
.addFunction("priority") { DoubleValue(priority) }
.addFunction("crit_ratio") { DoubleValue(critRatio) }
}
val displayName: MutableText
get() = lang("move.$name")
@@ -86,7 +69,7 @@ open class MoveTemplate(
priority = 0,
critRatio = 0.0,
effectChances = emptyArray(),
actionEffect = null
flags = emptySet()
)
companion object {
@@ -113,4 +96,9 @@ open class MoveTemplate(
template = this
)
}
/**
* Returns this move's showdown data as a [JsonObject]
*/
fun showdownJson() = ShowdownService.service.getMoves()[num].asJsonObject
}
@@ -10,15 +10,12 @@ package com.cobblemon.mod.common.api.moves
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.MoveTarget
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.JsonArray
import com.google.gson.JsonObject
@@ -75,18 +72,22 @@ object Moves : DataRegistry {
effectChances += secondaryMember.get("chance").asDouble
}
}
val actionEffect = ActionEffects.actionEffects[id.asIdentifierDefaultingNamespace()]
?: run {
ActionEffects.actionEffects["generic_move".asIdentifierDefaultingNamespace()]
// if (damageCategory == DamageCategories.STATUS) {
// ActionEffects.actionEffects[cobblemonResource("status")]
// } else {
// val type = elementalType.name.lowercase()
// val category = damageCategory.name.lowercase()
// ActionEffects.actionEffects["${category}_$type".asIdentifierDefaultingNamespace()]
// }
}
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray(), actionEffect)
val flags = mutableSetOf<String>()
jsMove["flags"].asJsonObject.asMap().forEach { flags.add(it.key) }
val move = MoveTemplate(
id,
num,
elementalType,
damageCategory,
power,
target,
accuracy,
pp,
priority,
critRatio,
effectChances.toTypedArray(),
flags
)
this.register(move)
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
@@ -1,94 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import java.util.concurrent.CompletableFuture
import net.minecraft.entity.Entity
/**
* An action effect will run and execute a series of 'keyframes', with each running once
* the previous has completed. The entire effect is considered complete for the given context
* once all the 'holds' provided in the context have been cleared or once every keyframe
* has finished.
*
* @author Hiroku
* @since October 20th, 2023
*/
class ActionEffectTimeline(
val timeline: List<ActionEffectKeyframe> = mutableListOf(),
val condition: Expression = "true".asExpression()
) {
companion object {
val NONE = ActionEffectTimeline()
}
fun run(context: ActionEffectContext): CompletableFuture<Unit> {
return if (timeline.isEmpty() || !context.runtime.resolveBoolean(condition)) {
CompletableFuture.completedFuture(Unit)
} else {
val finalFuture = CompletableFuture<Unit>()
// .toList copy because I'm paranoid about iterators being trying to share between identical effects playing
chainKeyframes(context, timeline.toList().iterator(), finalFuture)
finalFuture
}
}
fun chainKeyframes(context: ActionEffectContext, iterator: Iterator<ActionEffectKeyframe>, finalFuture: CompletableFuture<Unit>) {
if (!iterator.hasNext()) {
finalFuture.complete(Unit)
} else {
val keyframe = iterator.next()
context.currentKeyframes.add(keyframe)
keyframe.play(context)
.thenRun { context.currentKeyframes.remove(keyframe) }
.thenApply { chainKeyframes(context, iterator, finalFuture) }
}
}
}
class ActionEffectContext(
val actionEffect: ActionEffectTimeline,
val holds: MutableSet<String> = mutableSetOf(),
val providers: MutableList<Any> = mutableListOf(),
val runtime: MoLangRuntime,
var canBeInterrupted: Boolean = false,
var interrupted: Boolean = false,
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf()
) {
inline fun <reified T> findOneProvider() = providers.filterIsInstance<T>().firstOrNull()
}
class UsersProvider(users: List<Entity>): EntityProvider {
override val entities = users
constructor(vararg users: Entity): this(users.toList())
}
class TargetsProvider(targets: List<Entity>): EntityProvider {
override val entities = targets
constructor(vararg targets: Entity): this(targets.toList())
}
interface EntityProvider {
val entities: List<Entity>
}
//class MoveAnimationKeyframe(
// val sound: Identifier? = null,
// val animation: List<AnimationWithMoments> = listOf(),
// val effects: List<ActionEffect> = listOf()
//)
@@ -1,95 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.keyframes.*
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.awt.Color
import net.minecraft.predicate.NumberRange
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
/**
* Data registry containing all of the [ActionEffectTimeline]s that can be triggered from various actions.
*
* @author Hiroku
* @since October 21st, 2023
*/
object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
override val id: Identifier = cobblemonResource("action_effects")
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable = SimpleObservable<ActionEffects>()
init {
ActionEffectKeyframe.register<AnimationActionEffectKeyframe>("animation")
ActionEffectKeyframe.register<EntityMoLangActionEffectKeyframe>("entity_molang")
ActionEffectKeyframe.register<MoLangActionEffectKeyframe>("molang")
ActionEffectKeyframe.register<ParallelActionEffectKeyframe>("parallel")
ActionEffectKeyframe.register<CanInterruptActionEffectKeyframe>("can_interrupt")
ActionEffectKeyframe.register<CannotInterruptActionEffectKeyframe>("cannot_interrupt")
ActionEffectKeyframe.register<RemoveHoldsActionEffectKeyframe>("remove_holds")
ActionEffectKeyframe.register<AddHoldsActionEffectKeyframe>("add_holds")
ActionEffectKeyframe.register<MoveToTargetActionEffectKeyframe>("move_to_target")
ActionEffectKeyframe.register<ReturnToPositionActionEffectKeyframe>("return_to_position")
ActionEffectKeyframe.register<PauseActionEffectKeyframe>("pause")
ActionEffectKeyframe.register<SavePositionActionEffectKeyframe>("save_position")
ActionEffectKeyframe.register<ForkActionEffectKeyframe>("fork")
ActionEffectKeyframe.register<SequenceActionEffectKeyframe>("sequence")
ActionEffectKeyframe.register<RunActionEffectKeyframe>("run_action_effect")
ActionEffectKeyframe.register<EntityParticlesActionEffectKeyframe>("entity_particles")
ActionEffectKeyframe.register<EntitySoundActionEffectKeyframe>("entity_sound")
}
override val gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(ActionEffectKeyframe::class.java, ActionEffectKeyframeAdapter)
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(IntRange::class.java, VerboseIntRangeAdapter)
.registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
.registerTypeAdapter(Stat::class.java, CobblemonStatTypeAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(
TypeToken.getParameterized(
TypeToken.get(List::class.java).type,
TypeToken.get(ActionEffectKeyframe::class.java).type
).type,
SingleToPluralAdapter(ActionEffectKeyframe::class.java) { it }
)
.create()
override val typeToken: TypeToken<ActionEffectTimeline> = TypeToken.get(ActionEffectTimeline::class.java)
override val resourcePath = "action_effects"
val actionEffects = mutableMapOf<Identifier, ActionEffectTimeline>()
override fun reload(data: Map<Identifier, ActionEffectTimeline>) {
actionEffects.clear()
actionEffects.putAll(data)
observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {}
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
/**
* A single component of an action effect. When played it must return a completable future that completes
* when the action this keyframe represents is finished. [interrupt] is provided for when something successfully
* interrupts the effect so that if any cancellation logic is necessary, it can be provided.
*
* @author Hiroku
* @since October 26th, 2023
*/
interface ActionEffectKeyframe {
companion object {
val types = mutableMapOf<String, Class<out ActionEffectKeyframe>>()
inline fun <reified T : ActionEffectKeyframe> register(type: String) {
types[type] = T::class.java
}
}
fun play(context: ActionEffectContext): CompletableFuture<Unit>
fun interrupt(context: ActionEffectContext) {}
fun skip(): CompletableFuture<Unit> = CompletableFuture.completedFuture(Unit)
}
@@ -1,27 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
/**
* An [ActionEffectKeyframe] that adds some holds from the context for future code to recognize as blocking some
* functionality from proceeding.
*
* @author Hiroku
* @since October 27th, 2023
*/
class AddHoldsActionEffectKeyframe : ActionEffectKeyframe {
val holds = mutableSetOf<String>()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
context.holds.addAll(holds)
return CompletableFuture.completedFuture(Unit)
}
}
@@ -1,54 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
import com.cobblemon.mod.common.util.asExpressionLike
import java.util.concurrent.CompletableFuture
import net.minecraft.server.world.ServerWorld
class AnimationActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
var delay = "0".asExpressionLike()
var visibilityRange = 200
var animation: Set<String> = setOf("physical")
var variables: List<Expression> = listOf()
override val entityCondition = "q.entity.is_user".asExpressionLike()
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val entities = context.providers
.filterIsInstance<EntityProvider>()
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val expressions = variables.map { it.originalString }.toSet()
// Treat them as expressions if possible but otherwise yeah just send them as strings
val animation = animation.map {
try {
it.asExpressionLike().resolveString(context.runtime).takeIf { it != "0.0" } ?: it
} catch (e: Exception) {
it
}
}.toSet()
for (entity in entities) {
val world = entity.world as ServerWorld
val players = world.getPlayers { it.distanceTo(entity) <= visibilityRange }
val pkt = PlayPoseableAnimationPacket(entity.id, animation = animation, expressions = expressions)
players.forEach { it.sendPacket(pkt) }
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -1,19 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
class CanInterruptActionEffectKeyframe : ConditionalActionEffectKeyframe() {
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
context.canBeInterrupted = true
return CompletableFuture.completedFuture(Unit)
}
}
@@ -1,19 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
class CannotInterruptActionEffectKeyframe : ConditionalActionEffectKeyframe() {
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
context.canBeInterrupted = true
return CompletableFuture.completedFuture(Unit)
}
}
@@ -1,27 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.util.asExpressionLike
import java.util.concurrent.CompletableFuture
abstract class ConditionalActionEffectKeyframe : ActionEffectKeyframe {
var condition: ExpressionLike = "true".asExpressionLike()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
return if (condition.resolveBoolean(context.runtime)) {
playWhenTrue(context)
} else {
skip()
}
}
abstract fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit>
}
@@ -1,36 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import net.minecraft.entity.Entity
/**
* An action effect keyframe that plays for all entities for which the condition is true.
*
* @author Hiroku
* @since January 21st, 2024
*/
interface EntityConditionalActionEffectKeyframe {
val entityCondition: ExpressionLike
fun test(context: ActionEffectContext, entity: Entity, isUser: Boolean): Boolean {
// TODO this should use the entity's own query struct
context.runtime.environment.getQueryStruct().addFunction("entity") {
QueryStruct(hashMapOf())
.addFunction("uuid") { StringValue(entity.uuidAsString) }
.addFunction("is_user") { DoubleValue(isUser) }
}
return entityCondition.resolveBoolean(context.runtime)
}
}
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.util.asExpressionLike
import java.util.concurrent.CompletableFuture
import net.minecraft.server.world.ServerWorld
/**
* An action effect keyframe that will run client-side MoLang for all entities that fit the entity condition.
*
* This MoLang executes within that entity's runtime, meaning you can trigger animations or locational particles
* or sounds.
*
* @author Hiroku
* @since October 30th, 2023
*/
class EntityMoLangActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
var delay = "0".asExpressionLike()
val expressions = mutableSetOf<String>()
val visibilityRange = 200
override val entityCondition: ExpressionLike = "q.entity.is_user".asExpressionLike()
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val entities = context.providers
.filterIsInstance<EntityProvider>()
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
for (entity in entities) {
val world = entity.world as ServerWorld
val players = world.getPlayers { it.distanceTo(entity) <= visibilityRange }
val pkt = RunPosableMoLangPacket(entityId = entity.id, expressions = expressions)
players.forEach { it.sendPacket(pkt) }
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -1,56 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.entity.Poseable
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import java.util.concurrent.CompletableFuture
import net.minecraft.server.world.ServerWorld
/**
* Spawns particles on the entity based on the given particle effect and locator.
*
* @author Hiroku
* @since January 21st, 2024
*/
class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
override val entityCondition = "q.entity.is_user".asExpressionLike()
var effect: String? = null
var locator: String = "root"
val delay: ExpressionLike = "0".asExpressionLike()
val visibilityRange = 200
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val entities = context.providers
.filterIsInstance<EntityProvider>()
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val effectIdentifier = try {
effect?.asExpressionLike()?.resolveString(context.runtime)
} catch (e: Exception) {
effect
}?.asIdentifierDefaultingNamespace() ?: return skip()
entities.filter { it is Poseable }.forEach { entity ->
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locator)
val players = (entity.world as ServerWorld).getPlayers { it.distanceTo(entity) <= visibilityRange }
packet.sendToPlayers(players)
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import java.util.concurrent.CompletableFuture
import net.minecraft.registry.RegistryKeys
/**
* An action effect keyframe that plays a positioned sound for all matching entities.
*
* @author Hiroku
* @since January 21st, 2024
*/
class EntitySoundActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
override val entityCondition = "q.entity.is_user".asExpressionLike()
var sound: String? = null
val delay: ExpressionLike = "0".asExpressionLike()
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val entities = context.providers
.filterIsInstance<EntityProvider>()
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
val soundIdentifier = try {
sound?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: sound
} catch (e: Exception) {
sound
}?.asIdentifierDefaultingNamespace() ?: return skip()
entities.forEach { entity ->
val soundEvent = entity.world.registryManager.get(RegistryKeys.SOUND_EVENT).get(soundIdentifier) ?: return skip()
entity.playSound(soundEvent, 1f, 1f)
}
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.util.asExpressionLike
import java.util.concurrent.CompletableFuture
class ForkActionEffectKeyframe(
val condition: ExpressionLike = "true".asExpressionLike(),
val ifTrue: List<ActionEffectKeyframe> = listOf(),
val ifFalse: List<ActionEffectKeyframe> = listOf()
) : ActionEffectKeyframe {
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
/*
* Using .toList() before running chainKeyframes because iterators can be weird about sharing,
* .toList() creates a copy of the list and then grabs its iterator so much safer (three parachutes)
*/
if (!condition.resolveBoolean(context.runtime)) {
context.actionEffect.chainKeyframes(context, ifFalse.toList().iterator(), future)
} else {
context.actionEffect.chainKeyframes(context, ifTrue.toList().iterator(), future)
}
return future
}
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.util.asExpressionLike
import java.util.concurrent.CompletableFuture
/**
* An action effect keyframe that simply executes some MoLang on the effect context's runtime.
*
* @author Hiroku
* @since January 21st, 2024
*/
class MoLangActionEffectKeyframe : ActionEffectKeyframe {
val expressions: ExpressionLike = "0".asExpressionLike()
val delay: ExpressionLike = "0".asExpressionLike()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
expressions.resolve(context.runtime)
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
}
}
@@ -1,75 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonNavigation
import com.cobblemon.mod.common.util.asExpressionLike
import java.util.concurrent.CompletableFuture
import kotlin.math.pow
import kotlin.math.sqrt
import net.minecraft.util.Identifier
class MoveToTargetActionEffectKeyframe : ActionEffectKeyframe {
val speed = 1F
val timeout = "4".asExpressionLike()
var proximity = -1F
val timeoutActionEffect: Identifier? = null
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
val user = context.findOneProvider<UsersProvider>()?.entities?.firstOrNull() as? PokemonEntity ?: return CompletableFuture.completedFuture(Unit)
val target = context.findOneProvider<TargetsProvider>()?.entities?.firstOrNull() ?: return CompletableFuture.completedFuture(Unit)
val future = CompletableFuture<Unit>()
var timedOut = false
val proximity = this.proximity.takeIf { it != -1F } ?: (sqrt(2 * user.boundingBox.xLength.pow(2)) + 1.5F + sqrt(2 * target.boundingBox.xLength.pow(2))).toFloat()
if (target.distanceTo(user) !in proximity..20F) {
future.complete(Unit)
return future
}
val timeoutEffect = timeoutActionEffect?.let { ActionEffects.actionEffects[it] } ?: ActionEffectTimeline.NONE
val nav = user.navigation
val navContext = PokemonNavigation.NavigationContext(
destinationProximity = proximity,
onArrival = {
if (!future.isDone && !timedOut) {
future.complete(Unit)
}
},
onCannotReach = {
if (!future.isDone && !timedOut) {
timedOut = true
timeoutEffect.run(context).thenApply { future.complete(Unit) }
}
}
)
afterOnServer(seconds = timeout.resolveFloat(context.runtime)) {
if (!future.isDone && !timedOut) {
timedOut = true
timeoutEffect.run(context).thenApply { future.complete(Unit) }
nav.stop()
}
}
nav.startMovingTo(target.x, target.y, target.z, speed.toDouble(), navContext)
return future
}
}
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
class ParallelActionEffectKeyframe : ConditionalActionEffectKeyframe() {
var keyframes: MutableList<ActionEffectKeyframe> = mutableListOf()
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
return CompletableFuture.allOf(
*keyframes.map {
context.currentKeyframes.add(it)
it.play(context).thenRun { context.currentKeyframes.remove(it) }
}.toTypedArray()
).thenApply {} // Map Void -> Unit at the end
}
}
@@ -1,18 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import com.cobblemon.mod.common.util.asExpressionLike
class PauseActionEffectKeyframe : ConditionalActionEffectKeyframe() {
val pause = "1".asExpressionLike()
override fun playWhenTrue(context: ActionEffectContext) = delayedFuture(seconds = pause.resolveFloat(context.runtime), serverThread = true)
}
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.scheduling.delayedFuture
import java.util.concurrent.CompletableFuture
/**
* An [ActionEffectKeyframe] that clears some holds from the context, potentially marking the effect as
* complete as far as any blocked actions are concerned. If no holds are specified, then all holds are
* removed.
*
* @author Hiroku
* @since October 27th, 2023
*/
class RemoveHoldsActionEffectKeyframe : ActionEffectKeyframe {
val delay = 0F
val holds = mutableSetOf<String>()
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
return delayedFuture(seconds = delay, serverThread = true).thenApply {
if (holds.isEmpty()) {
context.holds.clear()
} else {
context.holds.removeAll(holds)
}
}
}
}
@@ -1,70 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.scheduling.after
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonNavigation
import com.cobblemon.mod.common.util.asExpressionLike
import java.util.concurrent.CompletableFuture
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
class ReturnToPositionActionEffectKeyframe : ActionEffectKeyframe {
val speed = 1F
val timeout = "4".asExpressionLike()
val timeoutActionEffect: Identifier? = null
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
val user = context.findOneProvider<UsersProvider>()?.entities?.firstOrNull() as? PokemonEntity ?: return skip()
val future = CompletableFuture<Unit>()
val pos = (context.runtime.environment.getValue(setOf("${user.uuidAsString}-pos").iterator())?.value() as? ObjectValue<*>)?.obj as? Vec3d ?: return skip()
var timedOut = false
if (pos.distanceTo(user.pos) > 20) {
future.complete(Unit)
return future
}
val timeoutEffect = timeoutActionEffect?.let { ActionEffects.actionEffects[it] } ?: ActionEffectTimeline.NONE
val nav = user.navigation
val navContext = PokemonNavigation.NavigationContext(
onArrival = {
if (!future.isDone && !timedOut) {
future.complete(Unit)
}
},
onCannotReach = {
if (!future.isDone && !timedOut) {
timedOut = true
timeoutEffect.run(context).thenApply { future.complete(Unit) }
}
}
)
after(seconds = timeout.resolveFloat(context.runtime), serverThread = true) {
if (!future.isDone && !timedOut) {
timedOut = true
timeoutEffect.run(context).thenApply { future.complete(Unit) }
nav.stop()
}
}
nav.startMovingTo(pos.x, pos.y, pos.z, speed.toDouble(), navContext)
return future
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import java.util.concurrent.CompletableFuture
import net.minecraft.util.Identifier
/**
* An action effect keyframe that simply runs another action effect based on an identifier. This action
* effect is marked complete only when the action effect it runs is complete unless [waitForActionEffect] is
* false.
*
* @author Hiroku
* @since January 21st, 2024
*/
class RunActionEffectKeyframe : ConditionalActionEffectKeyframe() {
val actionEffect: Identifier? = null
val waitForActionEffect = true
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
return if (actionEffect != null) {
ActionEffects.actionEffects[actionEffect]?.run(context)?.takeIf { waitForActionEffect } ?: skip()
} else {
skip()
}
}
}
@@ -1,22 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import java.util.concurrent.CompletableFuture
class SavePositionActionEffectKeyframe: ConditionalActionEffectKeyframe() {
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val user = context.findOneProvider<UsersProvider>()?.entities?.firstOrNull() ?: return skip()
context.runtime.environment.setSimpleVariable("${user.uuidAsString}-pos", ObjectValue(user.pos))
return skip()
}
}
@@ -1,26 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
/**
* A sequence of action effects that are dependent on a condition.
*
* @author Hiroku
* @since October 29th, 2023
*/
class SequenceActionEffectKeyframe(val keyframes: List<ActionEffectKeyframe> = listOf()) : ConditionalActionEffectKeyframe() {
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
context.actionEffect.chainKeyframes(context, keyframes.toList().iterator(), future)
return future
}
}
@@ -6,6 +6,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.battles.dispatch
package com.cobblemon.mod.common.api.moves.tags
interface CauserInstruction
import com.cobblemon.mod.common.api.moves.MoveTemplate
class MoveTag(
val moves: List<MoveTemplate>
)
@@ -0,0 +1,55 @@
/*
* 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.moves.tags
import com.cobblemon.mod.common.api.moves.Moves
object MoveTags {
private val moves = Moves.all()
val PRIORITY_ZERO = MoveTag(moves.filter { it.priority == 0 })
/* Showdown flags */
val PROTECT = MoveTag(moves.filter { it.flags.contains("protect") })
val CONTACT = MoveTag(moves.filter { it.flags.contains("contact") })
val DISTANCE = MoveTag(moves.filter { it.flags.contains("distance") })
val ALLY_ANIM = MoveTag(moves.filter { it.flags.contains("allyanim") })
val FAIL_ENCORE = MoveTag(moves.filter { it.flags.contains("failencore") })
val FAIL_COPYCAT = MoveTag(moves.filter { it.flags.contains("failcopycat") })
val REFLECTABLE = MoveTag(moves.filter { it.flags.contains("reflectable") })
val BITE = MoveTag(moves.filter { it.flags.contains("bite") })
val NON_SKY = MoveTag(moves.filter { it.flags.contains("nonsky") })
val GRAVITY = MoveTag(moves.filter { it.flags.contains("gravity") })
val POWDER = MoveTag(moves.filter { it.flags.contains("powder") })
val PLEDGE_COMBO = MoveTag(moves.filter { it.flags.contains("pledgecombo") })
val MIRROR = MoveTag(moves.filter { it.flags.contains("mirror") })
val SNATCH = MoveTag(moves.filter { it.flags.contains("snatch") })
val SLICING = MoveTag(moves.filter { it.flags.contains("slicing") })
val WIND = MoveTag(moves.filter { it.flags.contains("wind") })
val NO_SLEEP_TALK = MoveTag(moves.filter { it.flags.contains("nosleeptalk") })
val FAIL_INSTRUCT = MoveTag(moves.filter { it.flags.contains("failinstruct") })
val PULSE = MoveTag(moves.filter { it.flags.contains("pulse") })
val RECHARGE = MoveTag(moves.filter { it.flags.contains("recharge") })
val SOUND = MoveTag(moves.filter { it.flags.contains("sound") })
val PUNCH = MoveTag(moves.filter { it.flags.contains("punch") })
val FUTURE_MOVE = MoveTag(moves.filter { it.flags.contains("futuremove") })
val MUST_PRESSURE = MoveTag(moves.filter { it.flags.contains("mustpressure") })
val HEAL = MoveTag(moves.filter { it.flags.contains("heal") })
val BULLET = MoveTag(moves.filter { it.flags.contains("bullet") })
val BYPASS_SUB = MoveTag(moves.filter { it.flags.contains("bypasssub") })
val DANCE = MoveTag(moves.filter { it.flags.contains("dance") })
val NO_ASSIST = MoveTag(moves.filter { it.flags.contains("noassist") })
val FAIL_MIMIC = MoveTag(moves.filter { it.flags.contains("failmimic") })
val FAIL_ME_FIRST = MoveTag(moves.filter { it.flags.contains("failmefirst") })
val CANT_USE_TWICE = MoveTag(moves.filter { it.flags.contains("cantusetwice") })
val CHARGE = MoveTag(moves.filter { it.flags.contains("charge") })
val DEFROST = MoveTag(moves.filter { it.flags.contains("defrost") })
val NO_PARENTAL_BOND = MoveTag(moves.filter { it.flags.contains("noparentalbond") })
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.multiblock
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
import net.minecraft.block.*
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.LivingEntity
@@ -42,7 +42,5 @@ interface MultiblockStructure {
fun getComparatorOutput(state: BlockState, world: World?, pos: BlockPos?): Int {
return 0
}
fun markRemoved(world: World):Unit
fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?)
}
@@ -1,28 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.net.serializers
import net.minecraft.entity.data.TrackedDataHandler
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
/**
* Data serializer of [Identifier] for DataTracker things.
*
* @author Hiroku
* @since May 22nd, 2023
*/
object IdentifierDataSerializer : TrackedDataHandler<Identifier> {
override fun copy(value: Identifier) = Identifier(value.namespace, value.path)
override fun read(buf: PacketByteBuf) = Identifier(buf.readString(), buf.readString())
override fun write(buf: PacketByteBuf, value: Identifier) {
buf.writeString(value.namespace)
buf.writeString(value.path)
}
}
@@ -70,6 +70,9 @@ object CobblemonPermissions {
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
// Test for starting a random battle with AI Player
val RANDOM_BATTLE = this.create("${COMMAND_PREFIX}randombattle", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
private fun create(node: String, level: PermissionLevel): Permission {
@@ -204,7 +204,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("ancient_feather_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_wing_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_jet_ball", throwPower = 2.5f, ancient = true)
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
createDefault("ancient_origin_ball", ancient = true)
// Luxury ball effect
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOWEST) { event ->
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
@@ -52,7 +53,6 @@ import java.util.UUID
*/
open class PokemonProperties {
companion object {
@JvmOverloads
fun parse(string: String, delimiter: String = " ", assigner: String = "="): PokemonProperties {
val props = PokemonProperties()
props.originalString = string
@@ -93,6 +93,10 @@ open class PokemonProperties {
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
props.tradeable = parseBooleanProperty(keyPairs, listOf("tradeable", "tradable"))
props.move1 = parseStringOfRegistry(keyPairs, listOf("move1", "m1")) { Moves.getByName(it)?.name }
props.move2 = parseStringOfRegistry(keyPairs, listOf("move2", "m2")) { Moves.getByName(it)?.name }
props.move3 = parseStringOfRegistry(keyPairs, listOf("move3", "m3")) { Moves.getByName(it)?.name }
props.move4 = parseStringOfRegistry(keyPairs, listOf("move4", "m4")) { Moves.getByName(it)?.name }
props.originalTrainerType = OriginalTrainerType.values().toList().parsePropertyOfCollection(keyPairs, listOf("originaltrainertype", "ottype"), labelsOptional = true) { it.name.lowercase() }
props.originalTrainer = parsePlayerProperty(keyPairs, listOf("originaltrainer", "ot"))
@@ -275,9 +279,14 @@ open class PokemonProperties {
var dmaxLevel: Int? = null
var gmaxFactor: Boolean? = null
var tradeable: Boolean? = null
var move1: String? = null
var move2: String? = null
var move3: String? = null
var move4: String? = null
var originalTrainerType: OriginalTrainerType? = null
var originalTrainer: String? = null // Original Trainer by Username or UUID
var ivs: IVs? = null
var evs: EVs? = null
var customProperties = mutableListOf<CustomPokemonProperty>()
@@ -330,6 +339,10 @@ open class PokemonProperties {
dmaxLevel?.let { pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemon.gmaxFactor = it }
tradeable?.let { pokemon.tradeable = it }
move1?.let { pokemon.setMove(it, 0) }
move2?.let { pokemon.setMove(it, 1) }
move3?.let { pokemon.setMove(it, 2) }
move4?.let { pokemon.setMove(it, 3) }
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
originalTrainerType?.let {
if (it == OriginalTrainerType.NONE) {
@@ -392,6 +405,10 @@ open class PokemonProperties {
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
move1?.let { pokemonEntity.pokemon.setMove(it, 0) }
move2?.let { pokemonEntity.pokemon.setMove(it, 1) }
move3?.let { pokemonEntity.pokemon.setMove(it, 2) }
move4?.let { pokemonEntity.pokemon.setMove(it, 3) }
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
originalTrainerType?.let {
if (it == OriginalTrainerType.NONE) {
@@ -452,6 +469,10 @@ open class PokemonProperties {
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
move1?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move2?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move3?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move4?.takeIf { move -> pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let{ return false }
return customProperties.none { !it.matches(pokemon) }
@@ -490,6 +511,10 @@ open class PokemonProperties {
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemonEntity.pokemon.tradeable }?.let { return false }
move1?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move2?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move3?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
move4?.takeIf { move -> pokemonEntity.pokemon.moveSet.getMoves().none { it.name == move } }?.let { return false }
originalTrainer?.takeIf { it != pokemonEntity.pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemonEntity.pokemon.originalTrainerType }?.let{ return false }
return customProperties.none { !it.matches(pokemonEntity) }
@@ -536,6 +561,10 @@ open class PokemonProperties {
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != properties.tradeable }?.let { return false }
move1?.takeIf { it != properties.move1 }?.let { return false }
move2?.takeIf { it != properties.move2 }?.let { return false }
move3?.takeIf { it != properties.move3 }?.let { return false }
move4?.takeIf { it != properties.move4 }?.let { return false }
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
return true
@@ -544,7 +573,8 @@ open class PokemonProperties {
fun create(): Pokemon {
val pokemon = Pokemon()
apply(pokemon)
pokemon.initialize()
if (move1 == null) //if there is not a move1 or later specific moves stated then initialize
pokemon.initialize()
roll(pokemon)
return pokemon
}
@@ -33,6 +33,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
@@ -171,6 +172,9 @@ object PokemonSpecies : JsonDataRegistry<Species> {
*/
fun random(): Species = this.implemented.random()
// overloaded function to give random pokemon species of a certain type
fun random(type: ElementalType): Species = this.implemented.filter { it.primaryType == type || it.secondaryType == type }.random()
override fun reload(data: Map<Identifier, Species>) {
this.speciesByIdentifier.clear()
this.implemented.clear()
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokemon.effect
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.effects.LightSourceEffect
import com.cobblemon.mod.common.pokemon.effects.PotionBaseEffect
import com.cobblemon.mod.common.util.party
import net.minecraft.server.network.ServerPlayerEntity
@@ -28,6 +29,7 @@ object ShoulderEffectRegistry {
private val effects = mutableMapOf<String, Class<out ShoulderEffect>>()
// Effects - START
val LIGHT_SOURCE = register("light_source", LightSourceEffect::class.java)
val POTION_EFFECT = register("potion_effect", PotionBaseEffect::class.java)
// Effects - END
@@ -16,14 +16,11 @@ import com.cobblemon.mod.common.api.moves.BenchedMove
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.item.ItemStack
import net.minecraft.sound.SoundCategory
@@ -95,7 +92,6 @@ interface Evolution : EvolutionLike {
return true
}
/**
* Starts this evolution as soon as possible.
* This will not present a choice to the client regardless of [optional].
@@ -103,57 +99,25 @@ interface Evolution : EvolutionLike {
* @param pokemon The [Pokemon] being evolved.
*/
fun forceEvolve(pokemon: Pokemon) {
// This is a switch to enable/disable the evolution effect while we get particles improved
val useEvolutionEffect = false
if (pokemon.state is ShoulderedState) {
pokemon.tryRecallWithAnimation()
}
val pokemonEntity = pokemon.entity
if (pokemonEntity == null || !useEvolutionEffect) {
evolutionMethod(pokemon)
} else {
pokemonEntity.evolutionEntity = pokemon.getOwnerPlayer()?.let { GenericBedrockEntity(world = it.world) }
val evolutionEntity = pokemon.entity!!.evolutionEntity
evolutionEntity?.apply {
category = cobblemonResource("evolution")
colliderHeight = pokemonEntity.height
colliderWidth = pokemonEntity.width
scale = pokemonEntity.scaleFactor
syncAge = true // Otherwise particle animation will be starting from zero even if you come along partway through
setPosition(pokemonEntity.x, pokemonEntity.y, pokemonEntity.z)
}
pokemon.getOwnerPlayer()?.world?.spawnEntity(evolutionEntity)
afterOnServer(seconds = 9F) {
if (!pokemonEntity.isRemoved) {
evolutionMethod(pokemon)
afterOnServer(seconds = 1.5F) { pokemonEntity.cry() }
afterOnServer(seconds = 3F) {
if (evolutionEntity != null) {
evolutionEntity.kill()
if (!pokemonEntity.isRemoved) {
pokemonEntity.evolutionEntity = null
}
}
}
}
}
}
}
fun evolutionMethod(pokemon: Pokemon) {
// ToDo Once implemented queue evolution for a pokemon state that is not in battle, start animation instead of instantly doing all of this
this.result.apply(pokemon)
this.learnableMoves.forEach { move ->
if (pokemon.moveSet.hasSpace()) {
pokemon.moveSet.add(move.create())
} else {
}
else {
pokemon.benchedMoves.add(BenchedMove(move, 0))
}
pokemon.getOwnerPlayer()?.sendMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
}
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
pokemon.evolutions.filterIsInstance<PassiveEvolution>()
.forEach { evolution ->
evolution.attemptEvolution(pokemon)
}
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLVING, SoundCategory.NEUTRAL, 1F, 1F)
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
}
@@ -8,27 +8,13 @@
package com.cobblemon.mod.common.api.scheduling
import java.util.concurrent.CompletableFuture
@Deprecated("Use afterOnServer or afterOnClient; ambiguous side is not good for your health")
@JvmOverloads
fun after(ticks: Int = 0, seconds: Float = 0F, serverThread: Boolean = false, action: () -> Unit) {
val scheduler = if (serverThread) ServerTaskTracker else ClientTaskTracker
scheduler.after(seconds = seconds + ticks / 20F, action)
}
fun delayedFuture(ticks: Int = 0, seconds: Float = 0F, serverThread: Boolean = false): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
if (ticks == 0 && seconds == 0F) {
future.complete(Unit)
} else {
after(ticks = ticks, seconds = seconds, serverThread = serverThread) { future.complete(Unit) }
}
return future
}
/**
* Delayed task is created to run on the main thread. This is for when the task
* being completed after the delay does things like entity removal or other thread-unsafe actions.
@@ -25,7 +25,6 @@ import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import com.mojang.serialization.codecs.UnboundedMapCodec
import kotlin.math.floor
import net.minecraft.network.PacketByteBuf
@@ -45,7 +44,6 @@ interface MoLangCurve : CodecMapped {
registerSubtype(CurveType.LINEAR, LinearMoLangCurve::class.java, LinearMoLangCurve.CODEC)
registerSubtype(CurveType.CATMULL_ROM, CatmullRomMoLangCurve::class.java, CatmullRomMoLangCurve.CODEC)
registerSubtype(CurveType.BEZIER, BezierMoLangCurve::class.java, BezierMoLangCurve.CODEC)
registerSubtype(CurveType.BEZIER_CHAIN, BezierChainMoLangCurve::class.java, BezierChainMoLangCurve.CODEC)
}
}
@@ -54,7 +52,8 @@ interface MoLangCurve : CodecMapped {
var input: Expression
fun resolve(runtime: MoLangRuntime, inputValue: Double): Double
fun apply(runtime: MoLangRuntime) {
runtime.environment.setSimpleVariable(name, DoubleValue(resolve(runtime, runtime.resolveDouble(input))))
val inputValue = runtime.resolveDouble(input)
runtime.environment.setSimpleVariable(name, DoubleValue(resolve(runtime, inputValue)))
}
}
@@ -166,103 +165,6 @@ class CatmullRomMoLangCurve(
}
}
class BezierChainMoLangCurve(
override var name: String = "variable",
override var input: Expression = NumberExpression(0.5),
var nodes: Map<Double, BezierChainNode> = emptyMap()
) : MoLangCurve {
var nodePairs: List<BezierChainNodePair> = emptyList()
fun deriveNodePairs() {
val nodePairs = mutableListOf<BezierChainNodePair>()
val sortedNodes = nodes.entries.sortedBy { it.key }
for (i in 1 until sortedNodes.size) {
val node1 = sortedNodes[i - 1]
val node2 = sortedNodes[i]
nodePairs.add(BezierChainNodePair(node1.key, node2.key, node1.value, node2.value))
}
this.nodePairs = nodePairs
}
init {
deriveNodePairs()
}
class BezierChainNode(var value: Double, var slope: Double) {
fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeDouble(value)
buffer.writeDouble(slope)
}
fun readFromBuffer(buffer: PacketByteBuf) {
value = buffer.readDouble()
slope = buffer.readDouble()
}
companion object {
val CODEC = RecordCodecBuilder.create<BezierChainNode> { instance ->
instance.group(
PrimitiveCodec.DOUBLE.fieldOf("value").forGetter { it.value },
PrimitiveCodec.DOUBLE.fieldOf("slope").forGetter { it.slope }
).apply(instance, ::BezierChainNode)
}
}
}
class BezierChainNodePair(
val startTime: Double,
val endTime: Double,
node1: BezierChainNode,
node2: BezierChainNode
) {
val curve: CubedBezierCurve
init {
val v1 = node1.value
val v2 = node1.value + node1.slope / 3
val v3 = node2.value - node2.slope / 3
val v4 = node2.value
curve = CubedBezierCurve(v1, v2, v3, v4)
}
}
class BezierChainPointData(val time: Double, val value: Double, val slope: Double)
companion object {
val CODEC: Codec<BezierChainMoLangCurve> = RecordCodecBuilder.create { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
PrimitiveCodec.STRING.fieldOf("name").forGetter { it.name },
EXPRESSION_CODEC.fieldOf("input").forGetter { it.input },
UnboundedMapCodec(PrimitiveCodec.DOUBLE, BezierChainNode.CODEC).fieldOf("nodes").forGetter { it.nodes }
).apply(instance) { _, name, input, nodes -> BezierChainMoLangCurve(name, input, nodes) }
}
}
override val type: CurveType = CurveType.BEZIER_CHAIN
override fun resolve(runtime: MoLangRuntime, inputValue: Double): Double {
val position = inputValue.coerceIn(0.0, 1.0)
val nodePair = nodePairs.last { position >= it.startTime }
val curve = nodePair.curve
val t = (position - nodePair.startTime) / (nodePair.endTime - nodePair.startTime)
return curve.getY(t)
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {
name = buffer.readString()
input = MoLang.createParser(buffer.readString()).parseExpression()
nodes = buffer.readMap({ buffer.readDouble() }, { BezierChainNode(0.0, 0.0).also { it.readFromBuffer(buffer) } })
deriveNodePairs()
}
override fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeString(name)
buffer.writeString(input.getString())
buffer.writeMap(nodes, { pb, key -> pb.writeDouble(key) }, { pb, value -> value.writeToBuffer(pb) })
}
}
class BezierMoLangCurve(
override var name: String = "variable",
override var input: Expression = NumberExpression(0.5),
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.codec.CodecMapped
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.util.math.hamiltonProduct
import com.cobblemon.mod.common.util.set
import com.mojang.serialization.Codec
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.PrimitiveCodec
@@ -27,7 +26,6 @@ import net.minecraft.util.math.RotationAxis
import net.minecraft.util.math.Vec3d
import org.joml.AxisAngle4d
import org.joml.Quaternionf
import org.joml.Vector3f
interface ParticleCameraMode : CodecMapped {
companion object : ArbitrarilyMappedSerializableCompanion<ParticleCameraMode, ParticleCameraModeType>(
@@ -56,8 +54,6 @@ interface ParticleCameraMode : CodecMapped {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -81,8 +77,6 @@ class RotateXYZCameraMode : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -115,8 +109,6 @@ class RotateYCameraMode : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -154,15 +146,13 @@ class LookAtXYZ : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
viewDirection: Vec3d
): Quaternionf {
val i = if (angle == 0F) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
val rotation = Quaternionf()
val rotation = Quaternionf(0F, 0F, 0F, 1F)
rotation.hamiltonProduct(RotationAxis.POSITIVE_Y.rotationDegrees(-cameraYaw))
rotation.hamiltonProduct(RotationAxis.POSITIVE_X.rotationDegrees(cameraPitch))
rotation.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees(i))
@@ -184,8 +174,6 @@ class LookAtY : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -215,8 +203,6 @@ class DirectionZ : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -250,8 +236,6 @@ class EmitterYZPlane : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -290,8 +274,6 @@ class EmitterXZPlane : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -329,8 +311,6 @@ class EmitterXYPlane : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -367,8 +347,6 @@ class DirectionY : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -402,8 +380,6 @@ class DirectionX : ParticleCameraMode {
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
@@ -422,29 +398,6 @@ class DirectionX : ParticleCameraMode {
override fun writeToBuffer(buffer: PacketByteBuf) {}
}
/**
* You can think of this camera mode as the one that does the following:
*
* - Uses the direction of motion as the particle X axis
* - Uses a vector parallel to the camera plane and perpendicular to the direction of motion as the particle Y axis.
*
* The second requirement is difficult to accomplish. The way it was eventually solved is by
* thinking of the plane generated by two specific vectors. One is the camera to the particle,
* the other is the camera to where the particle will be after it continues moving on its
* trajectory for some amount of time.
*
* That plane slices right through the camera, and so the normal vector of that plane is parallel
* lies on the camera plane (hard to explain that one). Since the plane includes the direction vector,
* the normal vector of that plane is perpendicular to the direction vector. Since the direction
* vector is being used for the X axis, and the normal vector is perpendicular, we can use that as
* the Y axis safely.
*
* The chicanery of this look direction is all simply to calculate the correct Y axis, use it,
* and then use the direction vector for the X axis.
*
* @author Hiroku
* @since June 29th, 2023
*/
class LookAtDirection : ParticleCameraMode {
override val type: ParticleCameraModeType = ParticleCameraModeType.LOOK_AT_DIRECTION
companion object {
@@ -455,50 +408,21 @@ class LookAtDirection : ParticleCameraMode {
}
}
// Reusable shit so fewer instantiations during rendering.
val viewDirectionF = Vector3f(0F, 0F, 0F)
val particlePositionF = Vector3f(0F, 0F, 0F)
val cameraPositionF = Vector3f(0F, 0F, 0F)
val axisAngle = AxisAngle4d()
override fun getRotation(
matrixWrapper: MatrixWrapper,
prevAngle: Float,
angle: Float,
deltaTicks: Float,
particlePosition: Vec3d,
cameraPosition: Vec3d,
cameraAngle: Quaternionf,
cameraYaw: Float,
cameraPitch: Float,
viewDirection: Vec3d
): Quaternionf {
viewDirectionF.set(viewDirection)
particlePositionF.set(particlePosition)
cameraPositionF.set(cameraPosition)
// Perform a rotation around the axis that should later become the particle Y axis
Quaternionf().rotateTo(
// Camera -> Particle
particlePositionF.sub(cameraPositionF, Vector3f()),
// Camera -> (Particle + Direction)
particlePositionF.add(viewDirectionF, Vector3f()).sub(cameraPositionF)
).get(axisAngle) // Extract the rotation into axis + angle, so we can pluck the axis out.
// Extract the axis of rotation, it is what we want the particle's local Y axis to be.
val correctY = Vector3f(axisAngle.x.toFloat(), axisAngle.y.toFloat(), axisAngle.z.toFloat())
// First make X look along the direction vector
val rotation = Quaternionf().rotateTo(Vector3f(1F, 0F, 0F), viewDirectionF)
// Now figure out what the local Y axis is
val currentY = Vector3f(0F, 1F, 0F).rotate(rotation)
// Move that local Y to the correct local Y
// Pre-multiply is because the previous transform won't affect the correct Y, but vice versa absolutely would; we don't want that.
rotation.premul(Quaternionf().rotateTo(currentY, correctY))
// Do the regular rotation around Z to spin the particle, same as all other modes.
val particleAngle = if (angle == 0.0f) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
rotation.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees(particleAngle))
val i = if (angle == 0F) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
val rotation = Quaternionf(0F, 0F, 0F, 1F)
rotation.hamiltonProduct(RotationAxis.POSITIVE_Y.rotationDegrees((viewDirection.x * -cameraYaw).toFloat()))
rotation.hamiltonProduct(RotationAxis.POSITIVE_X.rotationDegrees((viewDirection.y * cameraPitch).toFloat()))
rotation.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees((viewDirection.z * i).toFloat()))
return rotation
}
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.mojang.serialization.Codec
@@ -56,9 +55,7 @@ class OnceEmitterLifetime(var activeTime: Expression = 1.0.asExpression()) : Par
override val type = ParticleEmitterLifetimeType.ONCE
override fun getAction(runtime: MoLangRuntime, started: Boolean, emitterAge: Double): ParticleEmitterAction {
val activeTime = runtime.resolve(activeTime)
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
return if (emitterAge > activeTime.asDouble()) {
return if (emitterAge > runtime.resolveDouble(activeTime)) {
ParticleEmitterAction.STOP
} else {
ParticleEmitterAction.GO
@@ -130,13 +127,11 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
override val type = ParticleEmitterLifetimeType.LOOPING
override fun getAction(runtime: MoLangRuntime, started: Boolean, emitterAge: Double): ParticleEmitterAction {
val activeTime = runtime.resolve(activeTime)
val activeTimeValue = activeTime.asDouble()
val activeTime = runtime.resolveDouble(activeTime)
val sleepTime = runtime.resolveDouble(sleepTime)
val interval = activeTimeValue + sleepTime
val interval = activeTime + sleepTime
val displacement = emitterAge % interval
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
return if (displacement < activeTimeValue) {
return if (displacement < activeTime) {
ParticleEmitterAction.GO
} else {
ParticleEmitterAction.RESET
@@ -150,6 +145,7 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
}
override fun writeToBuffer(buffer: PacketByteBuf) {
val activeTimeString =
buffer.writeString(activeTime.getString())
buffer.writeString(sleepTime.getString())
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.snowstorm
import com.bedrockk.molang.Expression
import com.bedrockk.molang.MoLang
import com.bedrockk.molang.ast.NumberExpression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.VariableStruct
@@ -16,11 +17,9 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.codec.CodecMapped
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterRate.Companion.OVERFLOW_VARIABLE
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveInt
import com.mojang.serialization.Codec
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.PrimitiveCodec
@@ -50,12 +49,12 @@ enum class ParticleEmitterRateType {
INSTANT
}
class InstantParticleEmitterRate(var amount: Expression = "1".asExpression()) : ParticleEmitterRate {
class InstantParticleEmitterRate(var amount: Int = 1) : ParticleEmitterRate {
companion object {
val CODEC: Codec<InstantParticleEmitterRate> = RecordCodecBuilder.create { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
EXPRESSION_CODEC.fieldOf("amount").forGetter { it.amount }
PrimitiveCodec.INT.fieldOf("amount").forGetter { it.amount }
).apply(instance) { _, amount -> InstantParticleEmitterRate(amount) }
}
}
@@ -66,17 +65,17 @@ class InstantParticleEmitterRate(var amount: Expression = "1".asExpression()) :
if (started) {
return 0
} else {
return runtime.resolveInt(amount)
return amount
}
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {
amount = buffer.readString().asExpression()
amount = buffer.readInt()
}
override fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeString(amount.getString())
buffer.writeInt(amount)
}
}
@@ -100,38 +99,29 @@ class SteadyParticleEmitterRate(
override val type = ParticleEmitterRateType.STEADY
override fun getEmitCount(runtime: MoLangRuntime, started: Boolean, currentlyActive: Int): Int {
// The emitting rates are all per second, but this runs every tick. Presents some difficulties.
val max = runtime.resolveDouble(maximum).toInt()
val variables = runtime.environment.structs["variable"] as VariableStruct
/*
* The strategy is that per tick it might be calculated to be 1.2 particles. We can't spawn a fifth
* of a particle, so we consider the .2 to be overflow which will be added to the next total. If the
* next tick we end up with 1.9, the overflow from last time will bring it to 2.1, so 2 particles with
* 0.1 overflow for the next tick.
*/
val currentOverflow = variables.map[OVERFLOW_VARIABLE]?.asDouble() ?: 0.0
var currentOverflow = variables.map[OVERFLOW_VARIABLE]?.asDouble() ?: 0.0
if (currentlyActive >= max) {
return 0
}
val perSecond = runtime.resolveDouble(rate)
val trySpawn = perSecond / 20.0 + currentOverflow
val intComponent = trySpawn.toInt() // round down
val doubleComponent = trySpawn - intComponent // decimal portion (the overflow)
val intComponent = trySpawn.toInt()
val doubleComponent = trySpawn - intComponent
variables.map[OVERFLOW_VARIABLE] = DoubleValue(doubleComponent)
currentOverflow = doubleComponent
variables.map[OVERFLOW_VARIABLE] = DoubleValue(currentOverflow)
return min(intComponent, max - currentlyActive)
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {
rate = buffer.readString().asExpression()
maximum = buffer.readString().asExpression()
rate = MoLang.createParser(buffer.readString()).parseExpression()
maximum = MoLang.createParser(buffer.readString()).parseExpression()
}
override fun writeToBuffer(buffer: PacketByteBuf) {
@@ -11,6 +11,5 @@ package com.cobblemon.mod.common.api.snowstorm
enum class ParticleMaterial {
ALPHA,
OPAQUE,
BLEND,
ADD
BLEND
}
@@ -1,103 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.snowstorm
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.particle.ParticleTextureSheet
import net.minecraft.client.render.*
import net.minecraft.client.texture.SpriteAtlasTexture
import net.minecraft.client.texture.TextureManager
object ParticleMaterials {
val ALPHA = object : ParticleTextureSheet {
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
RenderSystem.disableBlend()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_CUTOUT }
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
}
override fun draw(tessellator: Tessellator) {
tessellator.draw()
}
override fun toString(): String {
return "ALPHA"
}
}
val ADD = object : ParticleTextureSheet {
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
RenderSystem.enableBlend()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA)
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
}
override fun draw(tessellator: Tessellator) {
tessellator.draw()
}
override fun toString(): String {
return "ADD"
}
}
val BLEND = object : ParticleTextureSheet {
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
RenderSystem.enableBlend()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
RenderSystem.blendFunc(GlStateManager.SrcFactor.DST_COLOR, GlStateManager.DstFactor.ZERO)
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
}
override fun draw(tessellator: Tessellator) {
tessellator.draw()
}
override fun toString(): String {
return "BLEND"
}
}
val OPAQUE = object : ParticleTextureSheet {
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
RenderSystem.enableBlend()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
RenderSystem.blendFunc(GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ONE)
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
}
override fun draw(tessellator: Tessellator) {
tessellator.draw()
}
override fun toString(): String {
return "OPAQUE"
}
}
}
@@ -14,10 +14,8 @@ import com.bedrockk.molang.ast.NumberExpression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.codec.CodecMapped
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.math.geometry.transformDirection
import com.cobblemon.mod.common.util.resolveDouble
import com.mojang.serialization.Codec
import com.mojang.serialization.DynamicOps
@@ -39,7 +37,7 @@ interface ParticleMotion : CodecMapped {
}
val type: ParticleMotionType
fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d): Vec3d
fun getInitialVelocity(runtime: MoLangRuntime, particlePos: Vec3d, emitterPos: Vec3d): Vec3d
fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d
}
@@ -75,8 +73,8 @@ class DynamicParticleMotion(
}
}
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d): Vec3d {
return direction.getDirectionVector(runtime, storm, emitterPos, particlePos).normalize().multiply(runtime.resolveDouble(speed))
override fun getInitialVelocity(runtime: MoLangRuntime, particlePos: Vec3d, emitterPos: Vec3d): Vec3d {
return direction.getDirectionVector(runtime, emitterPos, particlePos).multiply(runtime.resolveDouble(speed))
}
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d {
@@ -84,7 +82,7 @@ class DynamicParticleMotion(
runtime.resolveDouble(acceleration.first),
runtime.resolveDouble(acceleration.second),
runtime.resolveDouble(acceleration.third)
).subtract(velocity.multiply(runtime.resolveDouble(drag)))
).subtract(velocity.multiply(runtime.resolveDouble(drag) / 20)/* Drag is measured in blocks per tick per tick, I know it sounds dumb. */)
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -124,7 +122,7 @@ interface ParticleMotionDirection : CodecMapped {
}
}
val type: ParticleMotionDirectionType
fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d
fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d): Vec3d
}
class InwardsMotionDirection : ParticleMotionDirection {
@@ -136,13 +134,7 @@ class InwardsMotionDirection : ParticleMotionDirection {
}
override val type = ParticleMotionDirectionType.INWARDS
override fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
return if (particlePos == emitterPos) {
Vec3d(storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5)
} else {
particlePos.subtract(emitterPos)
}.normalize()
}
override fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d) = emitterPos.subtract(particlePos).normalize()
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {}
override fun writeToBuffer(buffer: PacketByteBuf) {}
@@ -157,12 +149,8 @@ class OutwardsMotionDirection : ParticleMotionDirection {
}
override val type = ParticleMotionDirectionType.OUTWARDS
override fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
return if (particlePos == emitterPos) {
Vec3d(storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5)
} else {
particlePos.subtract(emitterPos)
}.normalize()
override fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
return particlePos.subtract(emitterPos).normalize()
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -190,13 +178,12 @@ class CustomMotionDirection(
override val type = ParticleMotionDirectionType.CUSTOM
override fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
val v = Vec3d(
override fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
return Vec3d(
runtime.resolveDouble(direction.first),
runtime.resolveDouble(direction.second),
runtime.resolveDouble(direction.third)
)
return storm.matrixWrapper.matrix.transformDirection(v)
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -233,7 +220,7 @@ class StaticParticleMotion : ParticleMotion {
@Transient
override val type = ParticleMotionType.STATIC
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
override fun getInitialVelocity(runtime: MoLangRuntime, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d) = Vec3d.ZERO
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
override fun readFromBuffer(buffer: PacketByteBuf) {}
@@ -34,9 +34,6 @@ class ParticleSpace(
}
}
val isLocalSpace: Boolean
get() = localPosition || localRotation
fun readFromBuffer(buffer: PacketByteBuf) {
localPosition = buffer.readBoolean()
localRotation = buffer.readBoolean()
@@ -146,7 +146,7 @@ class AnimatedParticleUVMode(
} else {
val fps = runtime.resolveDouble(fps)
val effectiveFrame = ((age * fps) % maxFrame).toInt()
val frame = if (!loop && age * fps >= maxFrame) {
val frame = if (!loop && age * fps > maxFrame) {
maxFrame
} else {
effectiveFrame
@@ -9,13 +9,17 @@
package com.cobblemon.mod.common.api.snowstorm
import com.bedrockk.molang.Expression
import com.bedrockk.molang.MoLang
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.codec.CodecMapped
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readVec3d
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveVec3d
import com.cobblemon.mod.common.util.writeVec3d
import com.mojang.serialization.Codec
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.PrimitiveCodec
@@ -66,7 +70,7 @@ class FromMotionViewDirection(var minSpeed: Double = 0.01) : ParticleViewDirecti
}
override fun getDirection(runtime: MoLangRuntime, lastDirection: Vec3d, currentVelocity: Vec3d): Vec3d {
return if (currentVelocity.length() * 20 >= minSpeed) {
return if (currentVelocity.length() / 20 >= minSpeed) {
currentVelocity.normalize()
} else {
lastDirection
@@ -10,8 +10,19 @@ package com.cobblemon.mod.common.api.spawning
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.spawning.condition.*
import com.cobblemon.mod.common.api.spawning.context.*
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
import com.cobblemon.mod.common.api.spawning.context.calculators.GroundedSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.LavafloorSpawningContextCalculator
import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawningContextCalculator
@@ -21,8 +32,6 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawning
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.fallingstar.FallingStarSpawner
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
import com.cobblemon.mod.common.api.spawning.preset.BestSpawnerConfig
@@ -74,8 +83,6 @@ object BestSpawner {
var config = BestSpawnerConfig()
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
lateinit var fishingSpawner: FishingSpawner
lateinit var starSpawner: FallingStarSpawner
fun loadConfig() {
LOGGER.info("Starting the Best Spawner...")
@@ -84,8 +91,6 @@ object BestSpawner {
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
SpawningCondition.register(FallingStarSpawningCondition.NAME, FallingStarSpawningCondition::class.java)
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
@@ -99,8 +104,6 @@ object BestSpawner {
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
SpawningContext.register(name = "fallingstar", clazz = FallingStarSpawningContext::class.java, defaultCondition = FallingStarSpawningCondition.NAME)
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
@@ -115,7 +118,5 @@ object BestSpawner {
fun onServerStarted() {
spawnerManagers.forEach(SpawnerManager::onServerStarted)
fishingSpawner = FishingSpawner()
starSpawner = FallingStarSpawner()
}
}
@@ -1,23 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.spawning.context.FallingStarSpawningContext
class FallingStarSpawningCondition: SpawningCondition<FallingStarSpawningContext>() {
override fun contextClass() = FallingStarSpawningContext::class.java
override fun fits(ctx: FallingStarSpawningContext): Boolean {
return true
}
companion object {
const val NAME = "fallingstar"
}
}
@@ -1,44 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.util.itemRegistry
import net.minecraft.block.Block
import net.minecraft.item.Item
/**
* A spawning condition that applies to [FishingSpawningContext]s.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningCondition: SpawningCondition<FishingSpawningContext>() {
override fun contextClass() = FishingSpawningContext::class.java
var rod: RegistryLikeCondition<Item>? = null
var neededNearbyBlocks: MutableList<RegistryLikeCondition<Block>>? = null
override fun fits(ctx: FishingSpawningContext): Boolean {
if (!super.fits(ctx)) {
return false
} else if (rod != null && !rod!!.fits(ctx.rodItem ?: return false, ctx.world.itemRegistry)) {
return false
} else if (neededNearbyBlocks != null && neededNearbyBlocks!!.none { cond -> ctx.nearbyBlockTypes.any { cond.fits(it, ctx.blockRegistry) } }) {
return false
} else {
return true
}
}
companion object {
const val NAME = "fishing"
}
}
@@ -76,51 +76,4 @@ interface AreaContextResolver {
return contexts
}
// fun resolveFishing(
// spawner: Spawner,
// contextCalculators: List<AreaSpawningContextCalculator<*>>,
// slice: WorldSlice
// ): List<AreaSpawningContext> {
// var pos = BlockPos.Mutable(1, 2, 3)
// val input = AreaSpawningInput(spawner, pos, slice)
// val contexts = mutableListOf<AreaSpawningContext>()
//
// var x = slice.baseX
// var y = slice.baseY
// var z = slice.baseZ
//
// while (x < slice.baseX + slice.length) {
// while (y < slice.baseY + slice.height) {
// while (z < slice.baseZ + slice.width) {
// pos.set(x, y, z)
// val vec = pos.toVec3d()
// if (slice.nearbyEntityPositions.none { it.isInRange(vec, 50.0) && it != slice.cause.entity }) {
// val fittedContextCalculator = contextCalculators
// .firstOrNull { calc -> calc.fits(input) && input.spawner.influences.none { !it.isAllowedPosition(input.world, input.position, calc) } }
// if (fittedContextCalculator != null) {
// val context = fittedContextCalculator.calculate(input)
// if (context != null) {
// contexts.add(context)
// // The position BlockPos has been used in a context, editing the same one
// // will cause entities to spawn at the wrong location (buried in walls, usually)
// // I made it so that our context calculators specifically take a copy of the
// // BlockPos but it'd still be exposed in custom contexts so fixing it here too.
// pos = BlockPos.Mutable(1, 2, 3)
// input.position = pos
// }
// }
// }
// z++
// }
// y++
// z = slice.baseZ
// }
// x++
// y = slice.baseY
// z = slice.baseZ
// }
//
// return contexts
// }
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.fallingstar.FallingStarSpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
class FallingStarSpawningContext(
cause: FallingStarSpawnCause,
world: ServerWorld,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawningContext(
cause = cause,
world = world,
position = pos,
light = world.getLightLevel(pos),
skyLight = world.getLightLevel(pos.up()),
canSeeSky = world.isSkyVisibleAllowingSea(pos),
influences = influences
) {
}
@@ -1,44 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.util.getBlockStates
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.Block
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
/**
* A spawning context that was generated for a fishing action.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawningContext(
cause: FishingSpawnCause,
world: ServerWorld,
pos: BlockPos,
influences: MutableList<SpawningInfluence>
) : TriggerSpawningContext(
cause = cause,
world = world,
position = pos,
light = world.getLightLevel(pos),
skyLight = world.getLightLevel(pos.up()),
canSeeSky = world.isSkyVisibleAllowingSea(pos),
influences = influences
) {
val nearbyBlocks = world.getBlockStates(Box.of(pos.toVec3d(), 10.0, 10.0, 10.0))
val nearbyBlockTypes: List<Block> by lazy { nearbyBlocks.mapNotNull { it.block }.distinct() }
val rodStack = cause.rodStack
val rodItem = cause.rodItem
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.context
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawning context relating to triggered spawning. Uses a single-point structure cache which is
* the only contribution of this implementation.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class TriggerSpawningContext(
override val cause: SpawnCause,
override val world: ServerWorld,
override val position: BlockPos,
override val light: Int,
override val skyLight: Int,
override val canSeeSky: Boolean,
override val influences: MutableList<SpawningInfluence>
) : SpawningContext() {
val triggerStructureCache = StructureChunkCache()
override fun getStructureCache(pos: BlockPos) = triggerStructureCache
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.detail
import net.minecraft.entity.Entity
/**
* A spawn result for spawn actions which involves some number of entities. This is used to maintain
* spawned entity lists for Spawner implementations that cap the number of entities that may be spawned.
*
* @author Hiroku
* @since January 13th, 2024
*/
class EntitySpawnResult(val entities: List<Entity>)
@@ -27,7 +27,7 @@ class PokemonSpawnAction(
override val detail: PokemonSpawnDetail,
/** The [PokemonProperties] that are about to be used. */
var props: PokemonProperties = detail.pokemon.copy()
) : SingleEntitySpawnAction<PokemonEntity>(ctx, detail) {
) : SpawnAction<PokemonEntity>(ctx, detail) {
override fun createEntity(): PokemonEntity {
if (props.species == null) LOGGER.error("PokemonSpawnAction run with null species - Spawn detail: ${detail.id}")
if (props.level == null) {
@@ -108,7 +108,7 @@ class PokemonSpawnDetail : SpawnDetail() {
return super.isValid() && isValidSpecies
}
override fun doSpawn(ctx: SpawningContext): SingleEntitySpawnAction<PokemonEntity> {
override fun doSpawn(ctx: SpawningContext): SpawnAction<*> {
// TODO should do more maybe
return PokemonSpawnAction(ctx, this)
}
@@ -1,49 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.entity.Entity
/**
* A spawn action that spawns a single entity. This is the most common type of spawn action.
*
* @author Hiroku
* @since January 13th, 2024
*/
abstract class SingleEntitySpawnAction<T : Entity>(
ctx: SpawningContext,
detail: SpawnDetail
) : SpawnAction<EntitySpawnResult>(ctx, detail) {
abstract fun createEntity(): T?
override fun run(): EntitySpawnResult? {
val e = createEntity() ?: return null
e.setPosition(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
entity.emit(e)
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = { ctx.world.spawnEntity(e) })
return EntitySpawnResult(listOf(e))
}
/**
* An observable for the entity that will eventually be spawned by this action.
*
* You can safely register subscriptions here for anything you want to do to the
* entity after it's spawned, whenever that may be. This is tolerant of situations
* where the entity spawn has been canceled, so that your action will not occur.
*/
val entity: SimpleObservable<T> = SimpleObservable<T>().apply {
subscribe { entity -> ctx.afterSpawn(entity) }
}
}
@@ -8,8 +8,12 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import java.util.concurrent.CompletableFuture
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.entity.Entity
/**
@@ -18,31 +22,28 @@ import java.util.concurrent.CompletableFuture
* @author Hiroku
* @since February 4th, 2022
*/
abstract class SpawnAction<R>(
abstract class SpawnAction<T : Entity>(
val ctx: SpawningContext,
open val detail: SpawnDetail
) {
val future = CompletableFuture<R>().also { it.thenApply { result -> ctx.spawner.afterSpawn(this, result) } }
abstract fun createEntity(): T?
/**
* Does whatever action is required to spawn this detail into the context.
*
* @return The result of spawning
*/
protected abstract fun run(): R?
/**
* Runs the spawn action and processes any non-null result.
*/
open fun complete(): Boolean {
open fun run() {
ctx.influences.forEach { it.affectAction(this) }
val result = run()
return if (result != null) {
future.complete(result)
true
} else {
future.completeExceptionally(Exception("Nothing was spawned."))
false
}
val e = createEntity() ?: return
e.setPosition(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
entity.emit(e)
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = { ctx.world.spawnEntity(e) })
}
/**
* An observable for the entity that will eventually be spawned by this action.
*
* You can safely register subscriptions here for anything you want to do to the
* entity after it's spawned, whenever that may be. This is tolerant of situations
* where the entity spawn has been canceled, so that your action will not occur.
*/
val entity: SimpleObservable<T> = SimpleObservable<T>().apply {
subscribe { entity -> ctx.afterSpawn(entity) }
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fallingstar
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import net.minecraft.entity.Entity
class FallingStarSpawnCause(
spawner: Spawner,
bucket: SpawnBucket,
entity: Entity?
) : SpawnCause(spawner, bucket, entity)
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fallingstar
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.context.FallingStarSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
open class FallingStarSpawner(
override val name: String = "fallingstar",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<FallingStarSpawnCause>(name, pool) {
override fun parseContext(cause: FallingStarSpawnCause, world: ServerWorld, pos: BlockPos): FallingStarSpawningContext? {
return FallingStarSpawningContext(cause, world, pos, influences)
}
}
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import net.minecraft.entity.Entity
import net.minecraft.item.FishingRodItem
import net.minecraft.item.ItemStack
/**
* A spawning cause that is embellished with fishing information. Could probably also
* have the bobber entity or something.
*
* @author Hiroku
* @since February 3rd, 2024
*/
class FishingSpawnCause(
spawner: Spawner,
bucket: SpawnBucket,
entity: Entity?,
val rodStack: ItemStack
) : SpawnCause(spawner, bucket, entity) {
val rodItem = rodStack.item as? FishingRodItem
}
@@ -1,38 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.fishing
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A spawner that takes in a [FishingSpawnCause] and spawns things.
*
* @author Hiroku
* @since February 3rd, 2024
*/
open class FishingSpawner(
override val name: String = "fishing",
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
) : TriggerSpawner<FishingSpawnCause>(name, pool) {
override fun parseContext(cause: FishingSpawnCause, world: ServerWorld, pos: BlockPos): FishingSpawningContext? {
// Maybe confirm that it's water we're fishing in?
val ctx = FishingSpawningContext(
cause = cause,
world = world,
pos = pos,
influences = influences
)
return ctx
}
}
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
@@ -37,7 +36,8 @@ interface Spawner {
fun setSpawningSelector(selector: SpawningSelector)
fun getSpawnPool(): SpawnPool
fun setSpawnPool(spawnPool: SpawnPool)
fun <R> afterSpawn(action: SpawnAction<R>, result: R) {}
fun modifySpawn(entity: Entity) {}
fun afterSpawn(entity: Entity) {}
fun canSpawn(): Boolean
fun getMatchingSpawns(ctx: SpawningContext) = getSpawnPool().retrieve(ctx).filter { it.isSatisfiedBy(ctx) }
fun copyInfluences() = influences.filter { !it.isExpired() }.toMutableList()
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
@@ -52,6 +51,9 @@ abstract class TickingSpawner(
abstract var ticksBetweenSpawns: Float
var tickTimerMultiplier = 1F
@Volatile
var scheduledSpawn: SpawnAction<*>? = null
var removalCheckTicks = 0
open fun tick() {
@@ -66,27 +68,37 @@ abstract class TickingSpawner(
return
}
val scheduledSpawn = scheduledSpawn
if (scheduledSpawn != null) {
performSpawn(scheduledSpawn)
this.scheduledSpawn = null
}
ticksUntilNextSpawn -= tickTimerMultiplier
if (ticksUntilNextSpawn <= 0) {
// TODO some kind of async logic would be nice.
val spawn = run(SpawnCause(spawner = this, bucket = chooseBucket(), entity = getCauseEntity()))
ticksUntilNextSpawn = ticksBetweenSpawns
if (spawn != null) {
val ctx = spawn.first
val detail = spawn.second
val spawnAction = detail.doSpawn(ctx = ctx)
spawnAction.complete()
this.scheduledSpawn = spawnAction
}
}
}
override fun <R> afterSpawn(action: SpawnAction<R>, result: R) {
super.afterSpawn(action, result)
if (result is EntitySpawnResult) {
spawnedEntities.addAll(result.entities)
}
override fun afterSpawn(entity: Entity) {
super.afterSpawn(entity)
spawnedEntities.add(entity)
lastSpawnTime = System.currentTimeMillis()
}
fun performSpawn(spawnAction: SpawnAction<*>) {
spawnAction.entity.subscribe { afterSpawn(it) }
spawnAction.run()
}
open fun getCauseEntity(): Entity? = null
fun getAllInfluences() = this.influences + manager.influences
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.selection.FlatContextWeightedSelector
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
import java.util.concurrent.CompletableFuture
import net.minecraft.server.world.ServerWorld
import net.minecraft.util.math.BlockPos
/**
* A type of spawner that occurs at a single point and provides its own means of generating a
* context from a particular cause and position. The trigger spawner must be directly called to
* spawn things (almost as if it was being triggered) as opposed to being scheduled on a ticker.
*
* @author Hiroku
* @since February 3rd, 2024
*/
abstract class TriggerSpawner<T : SpawnCause>(override val name: String, var spawns: SpawnPool) : Spawner {
private var selector: SpawningSelector = FlatContextWeightedSelector()
override val influences = mutableListOf<SpawningInfluence>()
open fun run(cause: T, world: ServerWorld, pos: BlockPos): CompletableFuture<*>? {
val context = parseContext(cause, world, pos) ?: return null
selector.select(this, listOf(context))?.let { (_, spawn) ->
val action = spawn.doSpawn(context)
action.complete()
return action.future
}
return null
}
/** Parses a context, if possible, from the given cause and position. */
abstract fun parseContext(cause: T, world: ServerWorld, pos: BlockPos): SpawningContext?
override fun canSpawn() = true
override fun getSpawningSelector() = selector
override fun setSpawningSelector(selector: SpawningSelector) { this.selector = selector }
override fun getSpawnPool() = spawns
override fun setSpawnPool(spawnPool: SpawnPool) { spawns = spawnPool }
}
@@ -32,10 +32,11 @@ open class NBTStoreAdapter(
override fun save(file: File, serialized: NbtCompound) = NbtIo.writeCompressed(serialized, file)
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID): T? {
val store = try {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
} catch (exception: NoSuchMethodException) {
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
}
return try {
val nbt = NbtIo.readCompressed(file)
store.loadFromNBT(nbt)
@@ -43,8 +43,6 @@ open class PlayerPartyStore(
private var secondsSinceFriendshipUpdate = 0
constructor(playerUUID: UUID): this(playerUUID, playerUUID)
override fun initialize() {
super.initialize()
observerUUIDs.add(playerUUID)
@@ -148,8 +148,6 @@ object CobblemonBiomeTags {
@JvmField
val HAS_FIRE_STONE_ORE_RARE = create("has_ore/ore_fire_stone_rare")
@JvmField
val HAS_FIRE_STONE_ORE_NETHER = create("has_ore/ore_fire_stone_nether")
@JvmField
val HAS_ICE_STONE_ORE = create("has_ore/ore_ice_stone_normal")
@JvmField
val HAS_ICE_STONE_ORE_RARE = create("has_ore/ore_ice_stone_rare")
@@ -45,7 +45,6 @@ object CobblemonBlockTags {
@JvmField val SNOW_BLOCK = createTag("snow_block")
@JvmField val ROOTS = createTag("roots")
@JvmField val STANDING_SIGNS = createTag("standing_signs")
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
@JvmField val WALL_SIGNS = createTag("wall_signs")
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.tags
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.registry.Registries
import net.minecraft.registry.RegistryKeys
@@ -22,12 +21,11 @@ import net.minecraft.registry.tag.TagKey
*/
@Suppress("unused", "HasPlatformType")
object CobblemonItemTags {
@JvmField
val ANCIENT_POKE_BALLS = create("ancient_poke_balls")
@JvmField
val APRICORN_LOGS = create("apricorn_logs")
@JvmField
val APRICORN_SPROUTS = create("apricorn_sprouts")
val APRICORN_SEEDS = create("apricorn_seeds")
@JvmField
val APRICORNS = create("apricorns")
/**
@@ -42,15 +40,6 @@ object CobblemonItemTags {
val BOATS = create("boats")
@JvmField
val COBBLEMON_SEEDS = create("cobblemon_seeds")
/**
* This tag is used for Fossil Machine natural materials
*/
@JvmField
val COOKED_MEAT = create("cooked_meat")
@JvmField
val DAWN_STONE_ORES = create("dawn_stone_ores")
@JvmField
val DUSK_STONE_ORES = create("dusk_stone_ores")
@JvmField
val EVOLUTION_ITEMS = create("evolution_items")
@JvmField
@@ -58,54 +47,27 @@ object CobblemonItemTags {
@JvmField
val EXPERIENCE_CANDIES = create("experience_candies")
@JvmField
val FIRE_STONE_ORES = create("fire_stone_ores")
@JvmField
val FOSSILS = create("fossils")
@JvmField
val HANGING_SIGNS = create("hanging_signs")
@JvmField
val HERBS = create("herbs")
@JvmField
val ICE_STONE_ORES = create("ice_stone_ores")
@JvmField
val LEAF_STONE_ORES = create("leaf_stone_ores")
@JvmField
val MINT_LEAF = create("mint_leaf")
@JvmField
val MINT_SEEDS = create("mint_seeds")
@JvmField
val MINTS = create("mints")
@JvmField
val MOON_STONE_ORES = create("moon_stone_ores")
@JvmField
val MUTATED_BERRIES = create("mutated_berries")
@JvmField
val PLANTS = create("plants")
@JvmField
val POKE_BALLS = create("poke_balls")
val POKEBALLS = create("poke_balls")
@JvmField
val PROTEIN_INGREDIENTS = create("protein_ingredients")
/** See [COOKED_MEAT] */
@JvmField
val RAW_MEAT = create("raw_meat")
@JvmField
val SEEDS = create("seeds")
@JvmField
val SHINY_STONE_ORES = create("shiny_stone_ores")
@JvmField
val SIGNS = create("signs")
@JvmField
val SUN_STONE_ORES = create("sun_stone_ores")
@JvmField
val THUNDER_STONE_ORES = create("thunder_stone_ores")
@JvmField
val TUMBLESTONES = create("tumblestones")
@JvmField
val WATER_STONE_ORES = create("water_stone_ores")
@JvmField
val ZINC_INGREDIENTS = create("zinc_ingredients")
// Held Item Tags
@JvmField
val ANY_HELD_ITEM = create("held/is_held_item")
@JvmField
@@ -135,15 +97,6 @@ object CobblemonItemTags {
@JvmField
val CONSUMED_IN_WILD_BATTLE = create("held/consumed_in_wild_battle")
/**
* Tag that flags items as being able to "create" [CobblemonItems.LEFTOVERS].
*/
@JvmField
val LEAVES_LEFTOVERS = create("held/leaves_leftovers")
@JvmField
val POTTERY_SHERDS = create("decorated_pot_sherds")
private fun create(path: String) = TagKey.of(RegistryKeys.ITEM, cobblemonResource(path))
}
File diff suppressed because it is too large. Load diff
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeExpiredPacket
@@ -44,6 +45,7 @@ object BattleRegistry {
.registerTypeAdapter(ShowdownMoveset::class.java, ShowdownMovesetAdapter)
.create()
private val battleMap = ConcurrentHashMap<UUID, PokemonBattle>()
private val partyMap = ConcurrentHashMap<UUID, List<PartyStore>>()
// Challenger to challenge
val pvpChallenges = mutableMapOf<UUID, BattleChallenge>()
@@ -201,10 +203,11 @@ object BattleRegistry {
}
fun startBattle(
battleFormat: BattleFormat,
side1: BattleSide,
side2: BattleSide,
silent: Boolean = false
battleFormat: BattleFormat,
side1: BattleSide,
side2: BattleSide,
silent: Boolean = false,
clonePartyStores: List<PartyStore>? = null // used for cloned team battles
): BattleStartResult {
val battle = PokemonBattle(battleFormat, side1, side2)
if (silent) return SuccessfulBattleStart(battle)
@@ -212,6 +215,9 @@ object BattleRegistry {
val preBattleEvent = BattleStartedPreEvent(battle)
CobblemonEvents.BATTLE_STARTED_PRE.postThen(preBattleEvent) {
battleMap[battle.battleId] = battle
if(clonePartyStores != null) {
partyMap[battle.battleId] = clonePartyStores
}
startShowdown(battle)
CobblemonEvents.BATTLE_STARTED_POST.post(BattleStartedPostEvent(battle))
return SuccessfulBattleStart(battle)
@@ -221,6 +227,7 @@ object BattleRegistry {
fun closeBattle(battle: PokemonBattle) {
battleMap.remove(battle.battleId)
partyMap.remove(battle.battleId)
}
fun getBattle(id: UUID) : PokemonBattle? {
@@ -387,6 +387,8 @@ class ShowdownPokemon {
lateinit var baseAbility: String
lateinit var pokeball: String
lateinit var ability: String
var baseTypes = mutableListOf<String>()
var types = mutableListOf<String>()
var reviving: Boolean = false
val uuid: UUID by lazy { UUID.fromString(details.split(",")[1].trim()) }
@@ -401,6 +403,11 @@ class ShowdownPokemon {
buffer.writeString(baseAbility)
buffer.writeString(pokeball)
buffer.writeString(ability)
buffer.writeSizedInt(IntSize.U_BYTE, baseTypes.size)
baseTypes.forEach(buffer::writeString)
buffer.writeSizedInt(IntSize.U_BYTE, types.size)
types.forEach(buffer::writeString)
}
fun loadFromBuffer(buffer: PacketByteBuf): ShowdownPokemon {
@@ -415,6 +422,12 @@ class ShowdownPokemon {
baseAbility = buffer.readString()
pokeball = buffer.readString()
ability = buffer.readString()
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
baseTypes.add(buffer.readString())
}
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
types.add(buffer.readString())
}
return this
}
}
@@ -10,11 +10,7 @@ package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.interpreter.BasicContext
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.interpreter.Effect
import com.cobblemon.mod.common.api.battles.interpreter.MissingContext
import com.cobblemon.mod.common.api.battles.interpreter.*
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
@@ -24,11 +20,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.text.gold
import com.cobblemon.mod.common.api.text.plus
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
@@ -39,46 +31,24 @@ import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.interpreter.ContextManager
import com.cobblemon.mod.common.battles.interpreter.instructions.*
import com.cobblemon.mod.common.battles.interpreter.instructions.DamageInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.DeprecatedInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.DeprecatedSplitInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.FaintInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.IgnoredInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.MoveInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.TurnInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.UnknownInstruction
import com.cobblemon.mod.common.battles.interpreter.instructions.UpkeepInstruction
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMadeInvalidChoicePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattlePersistentStatusPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleQueueRequestPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwitchPokemonPacket
import com.cobblemon.mod.common.net.messages.client.battle.*
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.runOnServer
import com.cobblemon.mod.common.util.swap
import com.cobblemon.mod.common.util.*
import java.util.UUID
import java.util.concurrent.CompletableFuture
import kotlin.collections.Iterator
import kotlin.collections.MutableList
import kotlin.collections.any
import kotlin.collections.emptySet
import kotlin.collections.filter
import kotlin.collections.filterIsInstance
import kotlin.collections.find
import kotlin.collections.first
import kotlin.collections.firstOrNull
import kotlin.collections.forEach
import kotlin.collections.getOrNull
import kotlin.collections.listOf
import kotlin.collections.map
import kotlin.collections.mapNotNull
import kotlin.collections.mutableListOf
import kotlin.collections.mutableMapOf
import kotlin.collections.reduce
import kotlin.collections.set
import kotlin.collections.setOf
import kotlin.collections.toMutableList
import kotlin.collections.toTypedArray
import kotlin.math.roundToInt
import net.minecraft.entity.LivingEntity
import net.minecraft.server.world.ServerWorld
@@ -116,13 +86,12 @@ object ShowdownInterpreter {
updateInstructionParser["upkeep"] = { _, _, _, _ -> UpkeepInstruction() }
updateInstructionParser["faint"] = { battle, _, message, _ -> FaintInstruction(battle, message) }
updateInstructionParser["move"] = { _, instructionSet, message, _ -> MoveInstruction(instructionSet, message) }
updateInstructionParser["-activate"] = { _, instructionSet, message, _ -> ActivateInstruction(instructionSet, message) }
updateInstructionParser["-ability"] = { _, instructionSet, message, _ -> AbilityInstruction(instructionSet, message) }
updateInstructionParser["-miss"] = { battle, instructionSet, message, _ -> MissInstruction(battle, instructionSet, message) }
splitInstructionParser["-damage"] = { _, targetActor, instructionSet, publicMessage, privateMessage, _ ->
DamageInstruction(instructionSet, targetActor, publicMessage, privateMessage)
splitInstructionParser["-damage"] = { _, targetActor, _, publicMessage, privateMessage, _ ->
DamageInstruction(targetActor, publicMessage, privateMessage)
}
// Note '-cureteam' is a legacy thing that is only used in generation 2 and 4 mods for heal bell and aromatherapy respectively as such we can just ignore that
updateInstructions["|win|"] = this::handleWinInstruction
@@ -136,9 +105,11 @@ object ShowdownInterpreter {
updateInstructions["|-fail|"] = this::handleFailInstruction
updateInstructions["|-start|"] = this::handleStartInstructions
updateInstructions["|-block|"] = this::handleBlockInstructions
updateInstructions["|-activate|"] = this::handleActivateInstructions
updateInstructions["|-curestatus|"] = this::handleCureStatusInstruction
updateInstructions["|-fieldstart|"] = this::handleFieldStartInstructions
updateInstructions["|-fieldend|"] = this::handleFieldEndInstructions
updateInstructions["|-ability|"] = this::handleAbilityInstructions
updateInstructions["|-endability|"] = this::handleEndAbilityInstruction
updateInstructions["|-nothing"] = { battle, _, _ ->
battle.dispatchGo { battle.broadcastChatMessage(battleLang("nothing")) }
@@ -160,6 +131,7 @@ object ShowdownInterpreter {
updateInstructions["|-invertboost|"] = this::handleInvertBoostInstruction
updateInstructions["|-status|"] = this::handleStatusInstruction
updateInstructions["|-end|"] = this::handleEndInstruction
updateInstructions["|-miss|"] = this::handleMissInstruction
updateInstructions["|-hitcount|"] = this::handleHitCountInstruction
updateInstructions["|-item|"] = this::handleItemInstruction
updateInstructions["|-enditem|"] = this::handleEndItemInstruction
@@ -564,13 +536,9 @@ object ShowdownInterpreter {
fun handleStatusInstruction(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val (pnx, _) = message.pnxAndUuid(0) ?: return
val pokemon = message.getBattlePokemon(0, battle) ?: return
val otherPokemon = message.actorAndActivePokemonFromOptional(battle, "of")?.second?.battlePokemon
val statusLabel = message.argumentAt(1) ?: return
val status = Statuses.getStatus(statusLabel) ?: return LOGGER.error("Unrecognized status: $statusLabel")
if (otherPokemon == null)
broadcastOptionalAbility(battle, message.effect(), pokemon.getName())
else
broadcastOptionalAbility(battle, message.effect(), otherPokemon.getName())
broadcastOptionalAbility(battle, message.effect(), pokemon.getName())
battle.dispatchWaiting {
if (status is PersistentStatus) {
@@ -584,6 +552,20 @@ object ShowdownInterpreter {
}
}
/**
* Format:
* |-miss|SOURCE|TARGET
*
* The move used by the SOURCE Pokémon missed (maybe absent) the TARGET Pokémon.
*/
private fun handleMissInstruction(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
battle.dispatchWaiting(1.5F) {
val pokemon = message.getBattlePokemon(0, battle) ?: return@dispatchWaiting
battle.broadcastChatMessage(battleLang("missed").red())
battle.minorBattleActions[pokemon.uuid] = message
}
}
/**
* Format:
* |-immune|POKEMON
@@ -639,7 +621,7 @@ object ShowdownInterpreter {
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
val name = pokemon.getName()
// Move may be null as it's not always given
val moveName = message.moveAt(2)?.displayName ?: run { "(Unrecognized: ${message.argumentAt(2)})".text() }
val moveName = message.moveAt(2)?.displayName ?: run { println(message.argumentAt(2)); "(Unrecognized: ${message.argumentAt(2)})".text() }
val lang = when (effectID) {
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
@@ -971,6 +953,46 @@ object ShowdownInterpreter {
}
}
/**
* Format:
* |-activate|POKEMON|EFFECT
*
* A miscellaneous effect has activated.This is triggered whenever an effect could not be better described by one of the other minor messages.
*/
private fun handleActivateInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>){
val pokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = pokemon.getName()
val sourceName = message.getSourceBattlePokemon(battle)?.getName() ?: Text.literal("UNKNOWN")
val effect = message.effectAt(1) ?: return
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
broadcastOptionalAbility(battle, effect, pokemonName)
battle.dispatch{
this.lastCauser[battle.battleId] = message
battle.minorBattleActions[pokemon.uuid] = message
val lang = when (effect.id) {
// Includes a 3rd argument being the magnitude level as a number
"magnitude" -> battleLang("activate.magnitude", message.argumentAt(2)?.toIntOrNull() ?: 1)
// Includes spited move and the PP it was reduced by
"spite", "eeriespell" -> battleLang("activate.spite", pokemonName, extraEffect, message.argumentAt(3)!!)
// Don't need additional lang, announced elsewhere
"toxicdebris", "shedskin" -> return@dispatch GO
// Add activation to each Pokemon's history
"destinybond" -> {
battle.activePokemon.mapNotNull { it.battlePokemon?.uuid }.forEach { battle.minorBattleActions[it] = message }
battleLang("activate.destinybond", pokemonName)
}
"focussash", "focusband" -> battleLang("activate.focusband", pokemonName, effect.typelessData)
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
"shadowforce", "hyperspacefury", "hyperspacehole" -> battleLang("activate.phantomforce", pokemonName)
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
}
battle.broadcastChatMessage(lang)
WaitDispatch(1F)
}
}
/**
* Format:
* |-fieldstart|CONDITION
@@ -1028,6 +1050,54 @@ object ShowdownInterpreter {
}
}
/**
* Format:
* |-ability|POKEMON|ABILITY|(from)EFFECT
*
* The ABILITY of the POKEMON has been changed due to a move/ability EFFECT.
*
* Format:
* |-ability|POKEMON|ABILITY
*
* POKEMON has just switched-in, and its ability ABILITY is being announced to have a long-term effect.
*/
private fun handleAbilityInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val pokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = pokemon.getName()
val effect = message.effectAt(1) ?: return
val optionalEffect = message.effect()
val optionalPokemon = message.getSourceBattlePokemon(battle)
val optionalPokemonName = optionalPokemon?.getName()
// If there is an optional effect causing the activation, broadcast that instead of the standard effect
if (optionalEffect != null) {
broadcastAbility(battle, optionalEffect, pokemonName)
} else {
broadcastAbility(battle, effect, pokemonName)
}
battle.dispatch {
this.lastCauser[battle.battleId] = message
val lang = when (optionalEffect?.id) {
"trace" -> optionalPokemonName?.let { battleLang("ability.trace", pokemonName, it, effect.typelessData) }
"receiver", "powerofalchemy" -> optionalPokemonName?.let { battleLang("ability.receiver", it, effect.typelessData) } // Receiver and Power of Alchemy share the same text
else -> when (effect.id) {
"sturdy", "unnerve", "anticipation" -> battleLang("ability.${effect.id}", pokemonName) // Unique message
"airlock", "cloudnine" -> battleLang("ability.airlock") // Cloud Nine shares the same text as Air Lock
else -> null // Effect broadcasted by a succeeding instruction
}
}
battle.minorBattleActions[pokemon.uuid] = message
if (lang != null) {
battle.broadcastChatMessage(lang)
return@dispatch WaitDispatch(1F)
}
else return@dispatch GO
}
}
fun broadcastOptionalAbility(battle: PokemonBattle, effect: Effect?, pokemonName: MutableText) {
if (effect != null && effect.type == Effect.Type.ABILITY)
broadcastAbility(battle, effect, pokemonName)
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.battles.ai.RandomBattleAI
import com.cobblemon.mod.common.battles.ai.StrongBattleAI
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
@@ -29,7 +30,8 @@ open class PokemonBattleActor(
uuid: UUID,
val pokemon: BattlePokemon,
override val fleeDistance: Float,
artificialDecider: BattleAI = RandomBattleAI()
//artificialDecider: BattleAI = RandomBattleAI()
artificialDecider: BattleAI = StrongBattleAI(0)
) : AIBattleActor(uuid, listOf(pokemon), artificialDecider), EntityBackedBattleActor<PokemonEntity>, FleeableBattleActor {
override fun getName() = pokemon.effectedPokemon.species.translatedName
override fun nameOwned(name: String): MutableText = Text.literal(name)
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles.actor
import com.cobblemon.mod.common.api.battles.model.actor.AIBattleActor
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.battles.model.ai.BattleAI
import com.cobblemon.mod.common.battles.ai.StrongBattleAI
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.battleLang
@@ -18,10 +19,11 @@ import java.util.UUID
import net.minecraft.text.MutableText
class TrainerBattleActor(
val trainerName: String,
uuid: UUID,
pokemonList: List<BattlePokemon>,
artificialDecider: BattleAI
val trainerName: String,
uuid: UUID,
pokemonList: List<BattlePokemon>,
skill: Int,
artificialDecider: BattleAI = StrongBattleAI(skill)
) : AIBattleActor(uuid, pokemonList, artificialDecider) {
override fun getName() = trainerName.asTranslated()
override fun nameOwned(name: String): MutableText = battleLang("owned_pokemon", this.getName(), name)
File diff suppressed because it is too large. Load diff
Loaded 100 of 2268 files, more files were not shown because too many files have changed in this diff. Show more