mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 14:46:25 -04:00
Compare commits
187
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4faae33d43 | ||
|
|
bd8f761ddb | ||
|
|
46798d694e | ||
|
|
056dd0fd4b | ||
|
|
3ac00d976b | ||
|
|
b30a76855b | ||
|
|
b6c964e03b | ||
|
|
3ed46cb7af | ||
|
|
cf9efe9b8c | ||
|
|
02b9431731 | ||
|
|
a30ae0ccef | ||
|
|
a2905f4d37 | ||
|
|
b3aba1532c | ||
|
|
6369bdb612 | ||
|
|
b5db5605ef | ||
|
|
0207459a0f | ||
|
|
0e2fdfbce8 | ||
|
|
8c9da885c7 | ||
|
|
a5b6529f3c | ||
|
|
b430b7db76 | ||
|
|
8015ff0e20 | ||
|
|
ee78657e3d | ||
|
|
6cc84cd38c | ||
|
|
31d6ff541e | ||
|
|
e1a4086f24 | ||
|
|
b1992a94d3 | ||
|
|
b41c4c21a8 | ||
|
|
e1893f50ae | ||
|
|
a458b31c9f | ||
|
|
03b5336eb8 | ||
|
|
c24ab2049d | ||
|
|
b7b1850a4e | ||
|
|
8995c107f7 | ||
|
|
ac65910380 | ||
|
|
ba6847be9a | ||
|
|
5ddbcd1830 | ||
|
|
47605646d0 | ||
|
|
d9fdf089c2 | ||
|
|
e9a405694c | ||
|
|
d5af099120 | ||
|
|
825e1ee110 | ||
|
|
ee0dc24207 | ||
|
|
e5d702bb5a | ||
|
|
fb34d4bd23 | ||
|
|
6ca1337ff9 | ||
|
|
0d344337dd | ||
|
|
c6e514216c | ||
|
|
3d27681aaa | ||
|
|
352202203a | ||
|
|
62cfbd7087 | ||
|
|
5a64e8ea1b | ||
|
|
8dc9863a5d | ||
|
|
9280258413 | ||
|
|
51a11163dd | ||
|
|
d5ad84a495 | ||
|
|
b01086270a | ||
|
|
8d5eb5883d | ||
|
|
0d12bf1190 | ||
|
|
4f773f63cb | ||
|
|
3f3ae74535 | ||
|
|
7429fea802 | ||
|
|
ac5cd47715 | ||
|
|
c635a1d331 | ||
|
|
cf2173a281 | ||
|
|
710d37f453 | ||
|
|
1fb3416211 | ||
|
|
f80a2850bb | ||
|
|
64fe6cefde | ||
|
|
26f337c0fd | ||
|
|
ac1c7d8b78 | ||
|
|
92dd85643e | ||
|
|
8849de1bfd | ||
|
|
2a349697b4 | ||
|
|
9fc81c1b33 | ||
|
|
5c599dbb9a | ||
|
|
d5a94ff784 | ||
|
|
024f299486 | ||
|
|
9a2b588568 | ||
|
|
b6fb5e8776 | ||
|
|
987b9bb275 | ||
|
|
03bda4e27d | ||
|
|
b04ab8b95b | ||
|
|
5d8abbd4c7 | ||
|
|
6b167a7a17 | ||
|
|
dab0623d61 | ||
|
|
cb5b6ec68b | ||
|
|
ea57c9dc5d | ||
|
|
d36c24075c | ||
|
|
d1a8122296 | ||
|
|
a8e6e684ab | ||
|
|
188fc2b5d9 | ||
|
|
9abab091d9 | ||
|
|
e2e50d2f1b | ||
|
|
4ac88ba192 | ||
|
|
37eccce8b1 | ||
|
|
e44db7c380 | ||
|
|
d57bb42720 | ||
|
|
f0c44f040f | ||
|
|
2d6e2f87ed | ||
|
|
8b4193a5db | ||
|
|
667dfe32e3 | ||
|
|
218253765e | ||
|
|
7a15e6b004 | ||
|
|
6ee6beddeb | ||
|
|
b92960da77 | ||
|
|
ff082adf67 | ||
|
|
66959816ef | ||
|
|
0e85835a87 | ||
|
|
e165d4958d | ||
|
|
e0736e1988 | ||
|
|
724ff86a21 | ||
|
|
97427fa58b | ||
|
|
c9d47a3e21 | ||
|
|
c5c884ca2e | ||
|
|
f086c32b95 | ||
|
|
ecea176300 | ||
|
|
c00413bb32 | ||
|
|
be78012ce6 | ||
|
|
3b285cc8bd | ||
|
|
4dead98e6b | ||
|
|
db36f8da2a | ||
|
|
7215860d74 | ||
|
|
dc899ffc1b | ||
|
|
88fe55bbb9 | ||
|
|
3d3c421b5c | ||
|
|
3844dd6314 | ||
|
|
080077189f | ||
|
|
89a4edb327 | ||
|
|
ab37580258 | ||
|
|
c116c01fd6 | ||
|
|
e3ad8f8079 | ||
|
|
a491d508b6 | ||
|
|
02fb825328 | ||
|
|
8df6eea107 | ||
|
|
1376bb68f4 | ||
|
|
beb8f2ccf1 | ||
|
|
4fa86c7742 | ||
|
|
b0d57dd875 | ||
|
|
6c09e2b553 | ||
|
|
593104fb0a | ||
|
|
e314664731 | ||
|
|
d0a38a9704 | ||
|
|
3c9cff5027 | ||
|
|
6e155d7651 | ||
|
|
ae8f8a2fb8 | ||
|
|
5a6a83aac1 | ||
|
|
a1047e05d4 | ||
|
|
b7c1157c5c | ||
|
|
cc8a51abcd | ||
|
|
38174155bf | ||
|
|
2698006f93 | ||
|
|
aa38674fc8 | ||
|
|
9ca0742386 | ||
|
|
32d291890e | ||
|
|
188ae2ac85 | ||
|
|
381fd1d5e2 | ||
|
|
a2fbe16ede | ||
|
|
0b478ebd5a | ||
|
|
b0b07e8edd | ||
|
|
95a0f9603c | ||
|
|
824773150a | ||
|
|
3dc285cc70 | ||
|
|
d6fbb70db0 | ||
|
|
150a5c668a | ||
|
|
e12d0bea66 | ||
|
|
1bf01a2826 | ||
|
|
4d1bf677a1 | ||
|
|
069f56acfd | ||
|
|
19cdc8fbef | ||
|
|
1408b94f06 | ||
|
|
de081dc514 | ||
|
|
5bc4994091 | ||
|
|
2b992dfd51 | ||
|
|
1538f12934 | ||
|
|
857f67604c | ||
|
|
ae49e3f977 | ||
|
|
ec01a03820 | ||
|
|
96cc3671da | ||
|
|
ad7c9c560c | ||
|
|
ee15ff6b35 | ||
|
|
ca83103703 | ||
|
|
1aeb894b09 | ||
|
|
8e9ab26963 | ||
|
|
44b4fbcfe3 | ||
|
|
42cc1dd8a9 | ||
|
|
d69310e52c | ||
|
|
a0f89274d1 |
@@ -35,5 +35,10 @@ common/src/main/resources/assets/cobblemon/showdown
|
||||
/fabric/src/generated/resources/cobblemon-common.accesswidener
|
||||
/.ignored/
|
||||
|
||||
# Logs
|
||||
common/logs
|
||||
fabric/logs
|
||||
forge/logs
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
+12
-1
@@ -14,6 +14,7 @@
|
||||
- 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 a Display Case block! Use it to display your archaeological findings, or other items.
|
||||
- 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.
|
||||
@@ -23,11 +24,13 @@
|
||||
- Added the EV Boosting Feathers.
|
||||
- Added Type Gems.
|
||||
- Added various new tags for searchability and better compatibility.
|
||||
- Added the [Ability Capsule](https://bulbapedia.bulbagarden.net/wiki/Ability_Capsule) and [Patch](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch), these have no acquisition method for now and only work as intended with the traditional ability format of 1 to 2 common abilities and 1 hidden ability.
|
||||
- Added Gilded Chests, available in all Apricorn colors.
|
||||
- Added Relic Coins, Relic Coin Pouches, and Relic Coin Sacks.
|
||||
- Added Gimmighoul.
|
||||
- Using Relic Coins, you can increase Gimmighoul's Coin Stash. Upon reaching 999, it can be evolved into Gholdengo.
|
||||
- Additionally, if you give Gimmighoul a Netherite Scrap, you can increase its Netherite Stash. Who knows what might happen if it fills up?
|
||||
- Completed the advancement for placing a Tumblestone near Lava or Magma to make it grow.
|
||||
|
||||
### Changes
|
||||
- Pokémon now transition more smoothly between different poses.
|
||||
@@ -61,6 +64,8 @@
|
||||
- Fixed incompatibility with [Just Enough Resources](https://modrinth.com/mod/just-enough-resources-jer).
|
||||
- Fixed incompatibility with [Visual Overhaul](https://modrinth.com/mod/visual-overhaul).
|
||||
- Fixed an issue that allowed Pokemon to spawn on rails.
|
||||
- Fixed the data set on Pokémon when abilities are given via properties such as in ``/spawnpokemon``.
|
||||
- Fixed sometimes Pokémon losing hidden abilities when evolving, this fix will only take effect to newly created Pokémon.
|
||||
|
||||
### Developer
|
||||
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed).
|
||||
@@ -73,6 +78,11 @@
|
||||
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
|
||||
- API to support dynamic lighting in any mod that implements this feature has been added to all Species & Forms, see LightingData class for details.
|
||||
- Added a Mixin for the BrewingStandScreenHandler to fix the incorrect handling of quickMove in Fabric.
|
||||
- Corrections to the ability implementation have been made that make ``Ability.forced`` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
|
||||
- ``Pokemon.ability`` no longer has a public setter, please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
|
||||
- ``Ability.forced``, ``Ability.index`` and ``Ability.priority`` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
|
||||
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 to 2 regular abilities and 1 hidden.
|
||||
- ``Pokemon#rollAbility`` has been added, this rerolls a legal ability for the Pokémon.
|
||||
|
||||
### Datapack & Resourcepack Creators
|
||||
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
|
||||
@@ -107,6 +117,7 @@
|
||||
}
|
||||
```
|
||||
- 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.
|
||||
- Added the item tag ``cobblemon:ability_changers``, contains the ``cobblemon:ability_capsule`` & ``cobblemon:ability_patch`` by default.
|
||||
|
||||
## [1.4.1 (December 23rd, 2023)](#1-4-1)
|
||||
|
||||
@@ -1042,4 +1053,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.
|
||||
+2
-2
@@ -9,5 +9,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-${versioning.info.branch}-${versioning.info.build}-SNAPSHOT"
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[19:57:25] [Test worker/INFO]: Booting LaxPermissionValidator, permissions will be checked using Minecrafts permission level system, see https://minecraft.fandom.com/wiki/Permission_level
|
||||
@@ -9,18 +9,23 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.mob.PiglinEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PiglinEntity.class)
|
||||
public class PiglinEntityMixin {
|
||||
@Redirect(method = "equipToOffHand", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isOf(Lnet/minecraft/item/Item;)Z"))
|
||||
public boolean cobblemon$isValidBarteringItem(ItemStack stack, Item item) {
|
||||
//TODO: Make a tag?
|
||||
return stack.isOf(item) || stack.isOf(CobblemonItems.RELIC_COIN_POUCH);
|
||||
public abstract class PiglinEntityMixin {
|
||||
@Inject(method = "equipToOffHand", at = @At(value = "HEAD"), cancellable = true)
|
||||
public void cobblemon$isValidBarteringItem(ItemStack stack, CallbackInfo ci) {
|
||||
PiglinEntity entity = (PiglinEntity)(Object) this;
|
||||
if (stack.isOf(CobblemonItems.RELIC_COIN_POUCH)) {
|
||||
entity.equipStack(EquipmentSlot.OFFHAND, stack);
|
||||
entity.updateDropChances(EquipmentSlot.OFFHAND);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.cobblemon.mod.common.mixin.invoker;
|
||||
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.PressurePlateBlock;
|
||||
import net.minecraft.resource.featuretoggle.FeatureSet;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.screen.ScreenHandlerType.Factory;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(ScreenHandlerType.class)
|
||||
public interface ScreenHandlerTypeInvoker {
|
||||
@Invoker("<init>")
|
||||
static <T extends ScreenHandler> ScreenHandlerType<T> cobblemon$create(Factory<T> factory, FeatureSet requiredFeatures) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,7 @@ import com.cobblemon.mod.common.config.starter.StarterConfig
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.events.AdvancementHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
import com.cobblemon.mod.common.permission.LaxPermissionValidator
|
||||
@@ -231,6 +232,9 @@ object Cobblemon {
|
||||
}
|
||||
}
|
||||
|
||||
// Register the grow_tumblestone advancement
|
||||
PlatformEvents.RIGHT_CLICK_BLOCK.subscribe { AdvancementHandler.onTumbleStonePlaced(it) }
|
||||
|
||||
PlatformEvents.CHANGE_DIMENSION.subscribe {
|
||||
it.player.party().forEach { pokemon -> pokemon.entity?.recallWithAnimation() }
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import com.cobblemon.mod.common.block.entity.*
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.registry.Registries
|
||||
@@ -44,6 +45,8 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.create(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN).build(null))
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.create(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN).build(null))
|
||||
@JvmField
|
||||
val TM_BLOCK: BlockEntityType<TMBlockEntity> = this.create("tm_block", BlockEntityType.Builder.create(::TMBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.create(::GildedChestBlockEntity,
|
||||
@@ -77,4 +80,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
CobblemonBlocks.FOSSIL_ANALYZER
|
||||
).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
|
||||
BlockEntityType.Builder.create(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -385,8 +385,44 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.luminance { if ((it.get(PCBlock.ON) as Boolean) && (it.get(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }
|
||||
)
|
||||
)
|
||||
val TM_MACHINE = create(
|
||||
"tm_machine",
|
||||
TMBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.GREEN)
|
||||
.sounds(BlockSoundGroup.METAL)
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.nonOpaque()
|
||||
.luminance { if (it.get(TMBlock.ON) as Boolean) 10 else 0 }
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE = create(
|
||||
"display_case",
|
||||
DisplayCaseBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.sounds(CobblemonSounds.DISPLAY_CASE_SOUNDS)
|
||||
.nonOpaque()
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.mapColor(MapColor.STONE_GRAY)
|
||||
.strength(0.3f)
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MODIFICATION_STATION = create(
|
||||
"modification_station",
|
||||
ModStationBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.sounds(BlockSoundGroup.WOOD)
|
||||
.nonOpaque()
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.mapColor(MapColor.BROWN)
|
||||
.strength(0.3f)
|
||||
)
|
||||
)
|
||||
|
||||
val RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@@ -420,6 +456,67 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
|
||||
|
||||
@JvmField
|
||||
val NORMAL_TYPE_GEM_BLOCK = this.create("normal_type_gem_block", Block(AbstractBlock.Settings.copy(Blocks.AMETHYST_BLOCK)))
|
||||
@JvmField
|
||||
val NORMAL_TYPE_GEM = typeGem(
|
||||
"normal_type_gem",
|
||||
GrowableStoneBlock.STAGE_3,
|
||||
7,
|
||||
3,
|
||||
null,
|
||||
NORMAL_TYPE_GEM_BLOCK
|
||||
)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_NORMAL_TYPE_GEM = typeGem(
|
||||
"large_budding_normal_type_gem",
|
||||
GrowableStoneBlock.STAGE_2,
|
||||
5,
|
||||
3,
|
||||
NORMAL_TYPE_GEM,
|
||||
NORMAL_TYPE_GEM_BLOCK
|
||||
)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_NORMAL_TYPE_GEM = typeGem(
|
||||
"medium_budding_normal_type_gem",
|
||||
GrowableStoneBlock.STAGE_1,
|
||||
4,
|
||||
3,
|
||||
LARGE_BUDDING_NORMAL_TYPE_GEM,
|
||||
NORMAL_TYPE_GEM_BLOCK
|
||||
)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_NORMAL_TYPE_GEM = typeGem(
|
||||
"small_budding_normal_type_gem",
|
||||
GrowableStoneBlock.STAGE_0,
|
||||
3,
|
||||
4,
|
||||
MEDIUM_BUDDING_NORMAL_TYPE_GEM,
|
||||
NORMAL_TYPE_GEM_BLOCK
|
||||
)
|
||||
|
||||
private fun typeGem(
|
||||
name: String,
|
||||
stage: Int,
|
||||
height: Int,
|
||||
xzOffset: Int,
|
||||
nextStage: Block?,
|
||||
baseBlock: Block
|
||||
): TypeGemBlock {
|
||||
return this.create(name, TypeGemBlock(
|
||||
settings = AbstractBlock.Settings.create()
|
||||
.pistonBehavior(PistonBehavior.DESTROY)
|
||||
.nonOpaque()
|
||||
.strength(1.5F)
|
||||
.sounds(CobblemonSounds.TYPE_GEM_SOUNDS),
|
||||
stage = stage,
|
||||
height = height,
|
||||
xzOffset = xzOffset,
|
||||
nextStage = nextStage,
|
||||
baseBlock = baseBlock
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
|
||||
*
|
||||
@@ -430,8 +527,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
APRICORN_LOG to STRIPPED_APRICORN_LOG
|
||||
)
|
||||
|
||||
|
||||
|
||||
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
|
||||
|
||||
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?) : Block {
|
||||
|
||||
@@ -26,6 +26,7 @@ import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.ResourceReloader
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.screen.ScreenHandlerType
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
@@ -158,6 +159,8 @@ interface CobblemonImplementation {
|
||||
*/
|
||||
fun registerResourceReloader(identifier: Identifier, reloader: ResourceReloader, type: ResourceType, dependencies: Collection<Identifier>)
|
||||
|
||||
fun registerScreenHandlerType(identifier: Identifier, screenHandlerType: ScreenHandlerType<*>)
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
@@ -29,6 +30,7 @@ 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.ability.AbilityChangeItem
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
@@ -256,8 +258,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE)
|
||||
@JvmField
|
||||
val PC = blockItem("pc", CobblemonBlocks.PC)
|
||||
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
|
||||
@JvmField
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@JvmField
|
||||
val MOD_STATION = blockItem("modification_station", CobblemonBlocks.MODIFICATION_STATION)
|
||||
|
||||
// Evolution items
|
||||
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
|
||||
@@ -558,6 +565,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false))
|
||||
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true))
|
||||
|
||||
@JvmField
|
||||
val ABILITY_CAPSULE = this.create("ability_capsule", AbilityChangeItem(AbilityChanger.COMMON_ABILITY))
|
||||
@JvmField
|
||||
val ABILITY_PATCH = this.create("ability_patch", AbilityChangeItem(AbilityChanger.HIDDEN_ABILITY))
|
||||
|
||||
/**
|
||||
* Evolution Ores and Stones
|
||||
*/
|
||||
@@ -858,6 +870,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
|
||||
@JvmField
|
||||
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Item.Settings()))
|
||||
@JvmField
|
||||
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Settings()))
|
||||
|
||||
// Type Gems
|
||||
@JvmField
|
||||
|
||||
@@ -43,6 +43,8 @@ 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.CraftBlankTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.CraftTMPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
|
||||
@@ -131,6 +133,8 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacke
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
@@ -432,6 +436,10 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createServerBound(PartyPokemonMoveSelectedPacket.ID, PartyPokemonMoveSelectedPacket::decode, PartyPokemonMoveSelectedHandler)
|
||||
this.createServerBound(PartyMoveSelectCancelledPacket.ID, PartyMoveSelectCancelledPacket::decode, PartyMoveSelectCancelledHandler)
|
||||
|
||||
// TM Machine packets
|
||||
this.createServerBound(CraftTMPacket.ID, CraftTMPacket::decode, CraftTMPacketHandler)
|
||||
this.createServerBound(CraftBlankTMPacket.ID, CraftBlankTMPacket::decode, CraftBlankTMPacketHandler)
|
||||
|
||||
// Dialogue packets
|
||||
this.createServerBound(EscapeDialoguePacket.ID, EscapeDialoguePacket::decode, EscapeDialogueHandler)
|
||||
this.createServerBound(InputToDialoguePacket.ID, InputToDialoguePacket::decode, InputToDialogueHandler)
|
||||
|
||||
@@ -240,6 +240,19 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
TUMBLESTONE_HIT,
|
||||
TUMBLESTONE_STEP
|
||||
)
|
||||
@JvmField
|
||||
val TM_USE = this.create("tm.use")
|
||||
@JvmField
|
||||
val TMM_CRAFT = this.create("tmm.craft")
|
||||
@JvmField
|
||||
val TMM_CRAFT_BLANK = this.create("tmm.craft_blank")
|
||||
@JvmField
|
||||
val TMM_OFF = this.create("tmm.off")
|
||||
@JvmField
|
||||
val TMM_ON = this.create("tmm.on")
|
||||
|
||||
@JvmField
|
||||
val TYPE_GEM_SOUNDS = TUMBLESTONE_BLOCK_SOUNDS
|
||||
|
||||
@JvmField
|
||||
val BERRY_BUSH_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
@@ -295,5 +308,27 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
GILDED_CHEST_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE_ADD_ITEM = this.create("display_case.add_item")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_REMOVE_ITEM = this.create("display_case.remove_item")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_BREAK = this.create("display_case.break")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_HIT = this.create("display_case.hit")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_PLACE = this.create("display_case.place")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_STEP = this.create("display_case.step")
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
DISPLAY_CASE_BREAK,
|
||||
DISPLAY_CASE_STEP,
|
||||
DISPLAY_CASE_PLACE,
|
||||
DISPLAY_CASE_HIT,
|
||||
DISPLAY_CASE_STEP
|
||||
)
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
|
||||
}
|
||||
@@ -113,6 +113,14 @@ object CobblemonCriteria {
|
||||
)
|
||||
)
|
||||
|
||||
// Advancement criteria for [grow_tumblestone.json]
|
||||
val PLANT_TUMBLESTONE = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("plant_tumblestone"),
|
||||
PlantTumblestoneCriterionCondition::class.java
|
||||
)
|
||||
)
|
||||
|
||||
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.block.TumblestoneBlock
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
/**
|
||||
* A criterion condition for when a tumblestone block is planted
|
||||
* @param id The identifier of the criterion
|
||||
* @param predicate The predicate for the criterion
|
||||
*
|
||||
* @author Aethen
|
||||
* @since 02/28/2024
|
||||
*/
|
||||
class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
|
||||
SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
|
||||
override fun toJson(json: JsonObject) {
|
||||
// Add properties to json if needed. None needed for this criterion
|
||||
}
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
// Parse properties from json if needed. None needed for this criterion
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the tumblestone block can grow at the given position
|
||||
* @param player The player that planted the tumblestone block
|
||||
* @param context The context of the criterion
|
||||
* @return True if the tumblestone block can grow at the given position, false otherwise
|
||||
*/
|
||||
override fun matches(player: ServerPlayerEntity, context: PlantTumblestoneContext): Boolean {
|
||||
return context.tumbleStoneBlock.canGrow(context.pos, player.world)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The context of the [PlantTumblestoneCriterionCondition]
|
||||
* @param pos The position of the tumblestone block
|
||||
* @param tumbleStoneBlock The tumblestone block
|
||||
*/
|
||||
open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.abilities
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
@@ -21,7 +22,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
* @author Qu
|
||||
* @since January 9th, 2022
|
||||
*/
|
||||
open class Ability internal constructor(var template: AbilityTemplate, var forced: Boolean) {
|
||||
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean) {
|
||||
|
||||
val name: String
|
||||
get() = template.name
|
||||
@@ -34,16 +35,26 @@ open class Ability internal constructor(var template: AbilityTemplate, var force
|
||||
|
||||
/**
|
||||
* This represents the last known index of this backing ability in the species data.
|
||||
* If -1 the ability will attempt to find the correct index or reroll itself, this is done in order to be compatible with existing data
|
||||
* The value can be mutated however this is not recommended by API users.
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var forced: Boolean = forced
|
||||
internal set
|
||||
|
||||
/**
|
||||
* This represents the last known index of this backing ability in the species data.
|
||||
*
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var index: Int = -1
|
||||
internal set
|
||||
|
||||
/**
|
||||
* The last known priority of this ability in the species data.
|
||||
* The value can be mutated however this is not recommended by API users.
|
||||
*
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var priority = Priority.LOWEST
|
||||
internal set
|
||||
|
||||
open fun saveToNBT(nbt: NbtCompound): NbtCompound {
|
||||
nbt.putString(DataKeys.POKEMON_ABILITY_NAME, name)
|
||||
@@ -62,19 +73,23 @@ open class Ability internal constructor(var template: AbilityTemplate, var force
|
||||
}
|
||||
|
||||
open fun loadFromNBT(nbt: NbtCompound): Ability {
|
||||
forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
|
||||
this.template = Abilities.getOrException(nbt.getString(DataKeys.POKEMON_ABILITY_NAME))
|
||||
this.forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
|
||||
if (nbt.contains(DataKeys.POKEMON_ABILITY_INDEX) && nbt.contains(DataKeys.POKEMON_ABILITY_PRIORITY)) {
|
||||
this.index = nbt.getInt(DataKeys.POKEMON_ABILITY_INDEX)
|
||||
this.priority = Priority.valueOf(nbt.getString(DataKeys.POKEMON_ABILITY_PRIORITY))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
open fun loadFromJSON(json: JsonObject): Ability {
|
||||
forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
|
||||
this.template = Abilities.getOrException(json.get(DataKeys.POKEMON_ABILITY_NAME).asString)
|
||||
this.forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
|
||||
if (json.has(DataKeys.POKEMON_ABILITY_INDEX) && json.has(DataKeys.POKEMON_ABILITY_PRIORITY)) {
|
||||
this.index = json.get(DataKeys.POKEMON_ABILITY_INDEX).asInt
|
||||
this.priority = Priority.valueOf(json.get(DataKeys.POKEMON_ABILITY_PRIORITY).asString)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,6 @@ class AbilityTemplate(
|
||||
*/
|
||||
fun create(forced: Boolean = false) = builder(this, forced)
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Ability and loads the given NBT Tag into it.
|
||||
*
|
||||
@@ -42,4 +41,5 @@ class AbilityTemplate(
|
||||
* Ability extensions need to write and read their needed data from here.
|
||||
*/
|
||||
fun create(json: JsonObject) = create().loadFromJSON(json)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.item.ability
|
||||
|
||||
import com.cobblemon.mod.common.api.abilities.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityTypeChanger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
|
||||
|
||||
/**
|
||||
* Represents a change operation for a [Pokemon.ability].
|
||||
* This is backed by the associated [PotentialAbility].
|
||||
*
|
||||
* @param T The type of [PotentialAbility].
|
||||
*/
|
||||
interface AbilityChanger<T : PotentialAbility> {
|
||||
|
||||
/**
|
||||
* The [PotentialAbilityType] of type [T].
|
||||
*/
|
||||
val type: PotentialAbilityType<T>
|
||||
|
||||
/**
|
||||
* Collects all the possible targets for the [type] and the given [pokemon].
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @return A set containing all their legal abilities associated with [type], can be empty.
|
||||
*/
|
||||
fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate>
|
||||
|
||||
/**
|
||||
* Attempts to update the ability of the given [pokemon].
|
||||
*
|
||||
* @param pokemon The [Pokemon] attempting to change an ability.
|
||||
* @return If the operation was successful.
|
||||
*/
|
||||
fun performChange(pokemon: Pokemon): Boolean
|
||||
|
||||
/**
|
||||
* Checks if the current [PotentialAbilityType] of a [Pokemon.ability] is possible to swap from with this.
|
||||
*
|
||||
* @param type The [PotentialAbilityType] or null if the ability is forced and/or illegal.
|
||||
* @return If the operation can be performed.
|
||||
*/
|
||||
fun canChangeFrom(type: PotentialAbilityType<*>?): Boolean
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* An implementation of [AbilityChanger] for [CommonAbility].
|
||||
*
|
||||
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 2 common abilities.
|
||||
*/
|
||||
@JvmStatic
|
||||
val COMMON_ABILITY: AbilityChanger<CommonAbility> = AbilityTypeChanger(CommonAbilityType) { other -> other == CommonAbilityType }
|
||||
|
||||
/**
|
||||
* An implementation of [AbilityChanger] for [HiddenAbility].
|
||||
*
|
||||
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 1 hidden ability.
|
||||
*/
|
||||
@JvmStatic
|
||||
val HIDDEN_ABILITY: AbilityChanger<HiddenAbility> = AbilityTypeChanger(HiddenAbilityType) { other -> other == CommonAbilityType || other == HiddenAbilityType }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.abilities.Ability
|
||||
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
|
||||
@@ -294,6 +295,18 @@ open class PokemonProperties {
|
||||
)
|
||||
|
||||
fun apply(pokemon: Pokemon) {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
this.customProperties.forEach { it.apply(pokemon) }
|
||||
this.commonApply(pokemon)
|
||||
}
|
||||
|
||||
fun apply(pokemonEntity: PokemonEntity) {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
this.customProperties.forEach { it.apply(pokemonEntity) }
|
||||
this.commonApply(pokemonEntity.pokemon)
|
||||
}
|
||||
|
||||
private fun commonApply(pokemon: Pokemon) {
|
||||
species?.let {
|
||||
return@let try {
|
||||
if (it == "random") {
|
||||
@@ -313,8 +326,8 @@ open class PokemonProperties {
|
||||
friendship?.let { pokemon.setFriendship(it) }
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
|
||||
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemon.ability = ability } }
|
||||
status?.let { Statuses.getStatus(it)?.let { pokemon.applyStatus(it as PersistentStatus) } }
|
||||
ability?.let { this.createAbility(it, pokemon.form)?.let(pokemon::updateAbility) }
|
||||
status?.let { Statuses.getStatus(it)?.let { status -> if (status is PersistentStatus) pokemon.applyStatus(status) } }
|
||||
customProperties.forEach { it.apply(pokemon) }
|
||||
ivs?.let { ivs ->
|
||||
ivs.forEach { stat ->
|
||||
@@ -355,69 +368,17 @@ open class PokemonProperties {
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
|
||||
fun apply(pokemonEntity: PokemonEntity) {
|
||||
species?.let {
|
||||
return@let try {
|
||||
if (it == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace())
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {
|
||||
null
|
||||
}
|
||||
}?.let { pokemonEntity.pokemon.species = it }
|
||||
nickname?.let { pokemonEntity.pokemon.nickname = it }
|
||||
form?.let { formID -> pokemonEntity.pokemon.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }?.let { form -> pokemonEntity.pokemon.form = form }
|
||||
level?.let { pokemonEntity.pokemon.level = it }
|
||||
shiny?.let { pokemonEntity.pokemon.shiny = it }
|
||||
gender?.let { pokemonEntity.pokemon.gender = it }
|
||||
friendship?.let { pokemonEntity.pokemon.setFriendship(it) }
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemonEntity.pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemonEntity.pokemon.nature = nature } }
|
||||
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemonEntity.pokemon.ability = ability } }
|
||||
status?.let { Statuses.getStatus(it)?.let { pokemonEntity.pokemon.applyStatus(it as PersistentStatus) } }
|
||||
customProperties.forEach { it.apply(pokemonEntity) }
|
||||
ivs?.let { ivs ->
|
||||
ivs.forEach { stat ->
|
||||
pokemonEntity.pokemon.setIV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
evs?.let { evs ->
|
||||
evs.forEach { stat ->
|
||||
pokemonEntity.pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
|
||||
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
|
||||
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
|
||||
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
|
||||
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
|
||||
originalTrainerType?.let {
|
||||
if (it == OriginalTrainerType.NONE) {
|
||||
pokemonEntity.pokemon.removeOriginalTrainer()
|
||||
originalTrainer = null
|
||||
}
|
||||
}
|
||||
originalTrainer?.let { ot ->
|
||||
val type = originalTrainerType ?: pokemonEntity.pokemon.originalTrainerType
|
||||
when (type) {
|
||||
OriginalTrainerType.PLAYER -> {
|
||||
when (ot.length) {
|
||||
in 3..16 -> server()?.userCache?.findByName(ot)?.get()?.id // OT is a Username
|
||||
36 -> UUID.fromString(ot) // OT is a UUID
|
||||
else -> null // OT is invalid
|
||||
}?.let { uuid -> pokemonEntity.pokemon.setOriginalTrainer(uuid) }
|
||||
}
|
||||
OriginalTrainerType.NPC -> pokemonEntity.pokemon.setOriginalTrainer(ot)
|
||||
else -> {}
|
||||
}
|
||||
pokemonEntity.pokemon.refreshOriginalTrainer()
|
||||
}
|
||||
pokemonEntity.pokemon.updateAspects()
|
||||
fun matches(pokemon: Pokemon): Boolean {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
return this.commonMatches(pokemon) && this.customProperties.none { !it.matches(pokemon) }
|
||||
}
|
||||
|
||||
fun matches(pokemon: Pokemon): Boolean {
|
||||
fun matches(pokemonEntity: PokemonEntity): Boolean {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
return this.commonMatches(pokemonEntity.pokemon) && this.customProperties.none { !it.matches(pokemonEntity) }
|
||||
}
|
||||
|
||||
private fun commonMatches(pokemon: Pokemon): Boolean {
|
||||
level?.takeIf { it != pokemon.level }?.let { return false }
|
||||
shiny?.takeIf { it != pokemon.shiny }?.let { return false }
|
||||
gender?.takeIf { it != pokemon.gender }?.let { return false }
|
||||
@@ -454,53 +415,17 @@ open class PokemonProperties {
|
||||
tradeable?.takeIf { it != pokemon.tradeable }?.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) }
|
||||
}
|
||||
|
||||
fun matches(pokemonEntity: PokemonEntity): Boolean {
|
||||
level?.takeIf { it != pokemonEntity.pokemon.level }?.let { return false }
|
||||
shiny?.takeIf { it != ("shiny" in pokemonEntity.pokemon.aspects) }?.let { return false }
|
||||
gender?.takeIf { it != pokemonEntity.pokemon.gender }?.let { return false }
|
||||
species?.run {
|
||||
try {
|
||||
val species = if (this == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace()) ?: return@run
|
||||
}
|
||||
if (pokemonEntity.pokemon.species != species) {
|
||||
return false
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {}
|
||||
}
|
||||
nickname?.takeIf { it != pokemonEntity.pokemon.nickname }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemonEntity.pokemon.form.name, true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemonEntity.pokemon.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemonEntity.pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
nature?.takeIf { it != pokemonEntity.pokemon.nature.name.toString() }?.let { return false }
|
||||
ability?.takeIf { it != pokemonEntity.pokemon.ability.name }?.let { return false }
|
||||
status?.takeIf { it != pokemonEntity.pokemon.status?.status?.showdownName }?.let { return false }
|
||||
ivs?.forEach{ stat ->
|
||||
if (stat.value != pokemonEntity.pokemon.ivs[stat.key]) { return false }
|
||||
}
|
||||
evs?.forEach{ stat ->
|
||||
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
|
||||
}
|
||||
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
|
||||
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 }
|
||||
originalTrainer?.takeIf { it != pokemonEntity.pokemon.originalTrainer }?.let{ return false }
|
||||
originalTrainerType?.takeIf { it != pokemonEntity.pokemon.originalTrainerType }?.let{ return false }
|
||||
return customProperties.none { !it.matches(pokemonEntity) }
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO Review the need for this, usage for us is in commands that deviate from the behavior established by multiple Pokémon query equivalent
|
||||
fun isSubSetOf(properties: PokemonProperties): Boolean {
|
||||
level?.takeIf { it != properties.level }?.let { return false }
|
||||
shiny?.takeIf { it != ("shiny" in properties.aspects) }?.let { return false }
|
||||
gender?.takeIf { it != properties.gender }?.let { return false }
|
||||
species?.run {
|
||||
try {
|
||||
// TODO get context on this bit, it's highly unlikely two randoms would result in the same Pokémon, doesn't mean the prop is not equal?
|
||||
val species = if (this == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
@@ -509,7 +434,7 @@ open class PokemonProperties {
|
||||
if (properties.species != species.toString()) {
|
||||
return false
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {}
|
||||
} catch (_: InvalidIdentifierException) {}
|
||||
}
|
||||
nickname?.takeIf { it != properties.nickname }?.let { return false }
|
||||
form?.takeIf { !it.equals(properties.form, true) }?.let { return false }
|
||||
@@ -558,13 +483,11 @@ open class PokemonProperties {
|
||||
else baseTypes.random()
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
|
||||
|
||||
fun createEntity(world: World): PokemonEntity {
|
||||
return PokemonEntity(world, create())
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun saveToNBT(): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
originalString.let { nbt.putString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
|
||||
@@ -593,6 +516,7 @@ open class PokemonProperties {
|
||||
return nbt
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun loadFromNBT(tag: NbtCompound): PokemonProperties {
|
||||
originalString = tag.getString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)
|
||||
level = if (tag.contains(DataKeys.POKEMON_LEVEL)) tag.getInt(DataKeys.POKEMON_LEVEL) else null
|
||||
@@ -621,6 +545,7 @@ open class PokemonProperties {
|
||||
return this
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun saveToJSON(): JsonObject {
|
||||
val json = JsonObject()
|
||||
originalString.let { json.addProperty(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
|
||||
@@ -650,6 +575,7 @@ open class PokemonProperties {
|
||||
return json
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun loadFromJSON(json: JsonObject): PokemonProperties {
|
||||
originalString = json.get(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)?.asString ?: ""
|
||||
level = json.get(DataKeys.POKEMON_LEVEL)?.asInt
|
||||
@@ -727,4 +653,21 @@ open class PokemonProperties {
|
||||
fun copy(): PokemonProperties {
|
||||
return PokemonProperties().loadFromJSON(saveToJSON())
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
|
||||
|
||||
/**
|
||||
* Attempts to find an ability by ID and resolve if it should be forced or if it's legal for the given form.
|
||||
*
|
||||
* @param id The literal ID of the [Ability] being mapped.
|
||||
* @param form The [FormData] whose [FormData.abilities] is queried for legality.
|
||||
* @return The [Ability] with the [Ability.forced] state necessary for the given [form].
|
||||
*/
|
||||
private fun createAbility(id: String, form: FormData): Ability? {
|
||||
val ability = Abilities.get(id) ?: return null
|
||||
val potentialAbility = form.abilities.firstOrNull { potential -> potential.template == ability } ?: return ability.create(true)
|
||||
return potentialAbility.template.create(false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.moves
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
@@ -93,6 +94,20 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
.flatMap { it.value }
|
||||
.toSet()
|
||||
|
||||
fun tmLearnableMoves(): MutableList<MoveTemplate> {
|
||||
val moves = mutableListOf<MoveTemplate>()
|
||||
|
||||
moves.addAll(tmMoves)
|
||||
moves.addAll(tutorMoves)
|
||||
moves.addAll(eggMoves)
|
||||
moves.addAll(getLevelUpMovesUpTo(Cobblemon.config.maxPokemonLevel))
|
||||
moves.addAll(evolutionMoves)
|
||||
moves.addAll(formChangeMoves)
|
||||
|
||||
return moves
|
||||
}
|
||||
|
||||
|
||||
// We only sync level up moves atm
|
||||
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* Effect details for the actual particles of a particle effect.
|
||||
@@ -44,48 +43,106 @@ class BedrockParticle(
|
||||
var cameraMode: ParticleCameraMode = RotateXYZCameraMode(),
|
||||
var tinting: ParticleTinting = ExpressionParticleTinting(),
|
||||
var collision: ParticleCollision = ParticleCollision(),
|
||||
var environmentLighting: Boolean = false
|
||||
var environmentLighting: Boolean = false,
|
||||
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var timeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf())
|
||||
) {
|
||||
class ExpressionSet(
|
||||
val sizeX: Expression,
|
||||
val sizeY: Expression,
|
||||
val maxAge: Expression,
|
||||
val killExpression: Expression,
|
||||
val updateExpressions: MutableList<Expression>,
|
||||
val renderExpressions: MutableList<Expression>
|
||||
)
|
||||
|
||||
/*
|
||||
"creation_event": "event_sendspark",
|
||||
"expiration_event": ["event_spark", "event_afterspark", "event_capturestar"],
|
||||
"timeline": {
|
||||
"21.00": "event_spark"
|
||||
}
|
||||
*/
|
||||
|
||||
class EventSet(
|
||||
val creationEvents: MutableList<SimpleEventTrigger>,
|
||||
val expirationEvents: MutableList<SimpleEventTrigger>,
|
||||
val timeline: EventTriggerTimeline
|
||||
)
|
||||
|
||||
// kill plane maybe
|
||||
companion object {
|
||||
val CODEC: Codec<BedrockParticle> = RecordCodecBuilder.create { instance ->
|
||||
val EXPRESSION_SET_CODEC = RecordCodecBuilder.create<ExpressionSet> { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("texture").forGetter { it.texture },
|
||||
PrimitiveCodec.STRING.fieldOf("material").forGetter { it.material.name },
|
||||
ParticleUVMode.codec.fieldOf("uvMode").forGetter { it.uvMode },
|
||||
EXPRESSION_CODEC.fieldOf("sizeX").forGetter { it.sizeX },
|
||||
EXPRESSION_CODEC.fieldOf("sizeY").forGetter { it.sizeY },
|
||||
EXPRESSION_CODEC.fieldOf("maxAge").forGetter { it.maxAge },
|
||||
EXPRESSION_CODEC.fieldOf("killExpression").forGetter { it.killExpression },
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions },
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions }
|
||||
).apply(instance, ::ExpressionSet)
|
||||
}
|
||||
|
||||
val EVENT_SET_CODEC = RecordCodecBuilder.create<EventSet> { instance ->
|
||||
instance.group(
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
|
||||
EventTriggerTimeline.CODEC.fieldOf("timeline").forGetter { it.timeline }
|
||||
).apply(instance, ::EventSet)
|
||||
}
|
||||
|
||||
val CODEC: Codec<BedrockParticle> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("texture").forGetter { it.texture },
|
||||
PrimitiveCodec.STRING.fieldOf("material").forGetter { it.material.name },
|
||||
ParticleUVMode.codec.fieldOf("uvMode").forGetter { it.uvMode },
|
||||
EXPRESSION_SET_CODEC.fieldOf("expressionSet").forGetter {
|
||||
ExpressionSet(
|
||||
it.sizeX,
|
||||
it.sizeY,
|
||||
it.maxAge,
|
||||
it.killExpression,
|
||||
it.updateExpressions,
|
||||
it.renderExpressions
|
||||
)
|
||||
},
|
||||
ParticleMotion.codec.fieldOf("motion").forGetter { it.motion },
|
||||
ParticleRotation.codec.fieldOf("rotation").forGetter { it.rotation },
|
||||
ParticleViewDirection.codec.fieldOf("viewDirection").forGetter { it.viewDirection },
|
||||
ParticleCameraMode.codec.fieldOf("cameraMode").forGetter { it.cameraMode },
|
||||
ParticleTinting.codec.fieldOf("tinting").forGetter { it.tinting },
|
||||
ParticleCollision.CODEC.fieldOf("collision").forGetter { it.collision },
|
||||
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting }
|
||||
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting },
|
||||
EVENT_SET_CODEC.fieldOf("eventSet").forGetter {
|
||||
EventSet(
|
||||
it.creationEvents,
|
||||
it.expirationEvents,
|
||||
it.timeline
|
||||
)
|
||||
}
|
||||
).apply(instance) {
|
||||
texture, materialStr, uvMode, sizeX, sizeY, maxAge, killExpression, updateExpressions,
|
||||
renderExpressions, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting ->
|
||||
texture, materialStr, uvMode, expressionSet, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting, eventSet ->
|
||||
BedrockParticle(
|
||||
texture = texture,
|
||||
material = ParticleMaterial.valueOf(materialStr),
|
||||
uvMode = uvMode,
|
||||
sizeX = sizeX,
|
||||
sizeY = sizeY,
|
||||
maxAge = maxAge,
|
||||
killExpression = killExpression,
|
||||
updateExpressions = updateExpressions,
|
||||
renderExpressions = renderExpressions,
|
||||
sizeX = expressionSet.sizeX,
|
||||
sizeY = expressionSet.sizeY,
|
||||
maxAge = expressionSet.maxAge,
|
||||
killExpression = expressionSet.killExpression,
|
||||
updateExpressions = expressionSet.updateExpressions,
|
||||
renderExpressions = expressionSet.renderExpressions,
|
||||
motion = motion,
|
||||
rotation = rotation,
|
||||
viewDirection = viewDirection,
|
||||
cameraMode = cameraMode,
|
||||
tinting = tinting,
|
||||
collision = collision,
|
||||
environmentLighting = environmentLighting
|
||||
environmentLighting = environmentLighting,
|
||||
creationEvents = eventSet.creationEvents,
|
||||
expirationEvents = eventSet.expirationEvents,
|
||||
timeline = eventSet.timeline
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -108,6 +165,9 @@ class BedrockParticle(
|
||||
ParticleTinting.writeToBuffer(buffer, tinting)
|
||||
collision.writeToBuffer(buffer)
|
||||
buffer.writeBoolean(environmentLighting)
|
||||
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
|
||||
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
|
||||
timeline.encode(buffer)
|
||||
}
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
@@ -127,5 +187,8 @@ class BedrockParticle(
|
||||
tinting = ParticleTinting.readFromBuffer(buffer)
|
||||
collision.readFromBuffer(buffer)
|
||||
environmentLighting = buffer.readBoolean()
|
||||
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
timeline.decode(buffer)
|
||||
}
|
||||
}
|
||||
+11
-4
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common.api.snowstorm
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
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 net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@@ -27,7 +29,8 @@ class BedrockParticleEffect(
|
||||
var emitter: BedrockParticleEmitter = BedrockParticleEmitter(),
|
||||
var particle: BedrockParticle = BedrockParticle(),
|
||||
var curves: MutableList<MoLangCurve> = mutableListOf(),
|
||||
var space: ParticleSpace = ParticleSpace()
|
||||
var space: ParticleSpace = ParticleSpace(),
|
||||
var events: MutableMap<String, ParticleEvent> = mutableMapOf()
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<BedrockParticleEffect> = RecordCodecBuilder.create { instance ->
|
||||
@@ -36,14 +39,16 @@ class BedrockParticleEffect(
|
||||
BedrockParticleEmitter.CODEC.fieldOf("emitter").forGetter { it.emitter },
|
||||
BedrockParticle.CODEC.fieldOf("particle").forGetter { it.particle },
|
||||
ListCodec(MoLangCurve.codec).fieldOf("curves").forGetter { it.curves },
|
||||
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space }
|
||||
).apply(instance) { id, emitter, particle, curves, space ->
|
||||
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space },
|
||||
UnboundedMapCodec(PrimitiveCodec.STRING, ParticleEvent.CODEC).fieldOf("events").forGetter { it.events }
|
||||
).apply(instance) { id, emitter, particle, curves, space, events ->
|
||||
BedrockParticleEffect(
|
||||
id = id,
|
||||
emitter = emitter,
|
||||
particle = particle,
|
||||
curves = curves.toMutableList(),
|
||||
space = space
|
||||
space = space,
|
||||
events = events
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -55,6 +60,7 @@ class BedrockParticleEffect(
|
||||
particle.writeToBuffer(buffer)
|
||||
buffer.writeCollection(curves) { pb, curve -> MoLangCurve.writeToBuffer(buffer, curve) }
|
||||
space.writeToBuffer(buffer)
|
||||
buffer.writeMap(events, { _, v -> buffer.writeString(v) }) { _, event -> event.encode(buffer) }
|
||||
}
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
@@ -63,5 +69,6 @@ class BedrockParticleEffect(
|
||||
particle.readFromBuffer(buffer)
|
||||
curves = buffer.readList { MoLangCurve.readFromBuffer(buffer) }
|
||||
space.readFromBuffer(buffer)
|
||||
events = buffer.readMap({ buffer.readString() }) { ParticleEvent().also { it.decode(buffer) } }
|
||||
}
|
||||
}
|
||||
+29
-4
@@ -29,7 +29,12 @@ class BedrockParticleEmitter(
|
||||
var updateExpressions: MutableList<Expression> = mutableListOf(),
|
||||
var rate: ParticleEmitterRate = InstantParticleEmitterRate(),
|
||||
var shape: ParticleEmitterShape = SphereParticleEmitterShape(),
|
||||
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0))
|
||||
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0)),
|
||||
var eventTimeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
|
||||
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var travelDistanceEvents: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
|
||||
var loopingTravelDistanceEvents: MutableList<LoopingTravelDistanceEventTrigger> = mutableListOf()
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<BedrockParticleEmitter> = RecordCodecBuilder.create { instance ->
|
||||
@@ -38,14 +43,24 @@ class BedrockParticleEmitter(
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
|
||||
ParticleEmitterRate.codec.fieldOf("rate").forGetter { it.rate },
|
||||
ParticleEmitterShape.codec.fieldOf("shape").forGetter { it.shape },
|
||||
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime }
|
||||
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime ->
|
||||
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime },
|
||||
EventTriggerTimeline.CODEC.fieldOf("eventTimeline").forGetter { it.eventTimeline },
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
|
||||
EventTriggerTimeline.CODEC.fieldOf("travelDistanceEvents").forGetter { it.travelDistanceEvents },
|
||||
ListCodec(LoopingTravelDistanceEventTrigger.CODEC).fieldOf("loopingTravelDistanceEvents").forGetter { it.loopingTravelDistanceEvents }
|
||||
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
|
||||
BedrockParticleEmitter(
|
||||
startExpressions = startExpressions,
|
||||
updateExpressions = updateExpressions,
|
||||
shape = shape,
|
||||
rate = rate,
|
||||
lifetime = lifetime
|
||||
lifetime = lifetime,
|
||||
eventTimeline = eventTimeline,
|
||||
creationEvents = creationEvents,
|
||||
expirationEvents = expirationEvents,
|
||||
travelDistanceEvents = travelDistanceEvents,
|
||||
loopingTravelDistanceEvents = loopingTravelDistanceEvents
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -57,6 +72,11 @@ class BedrockParticleEmitter(
|
||||
ParticleEmitterRate.writeToBuffer(buffer, rate)
|
||||
ParticleEmitterShape.writeToBuffer(buffer, shape)
|
||||
ParticleEmitterLifetime.writeToBuffer(buffer, lifetime)
|
||||
eventTimeline.encode(buffer)
|
||||
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
|
||||
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
|
||||
travelDistanceEvents.encode(buffer)
|
||||
buffer.writeCollection(loopingTravelDistanceEvents) { pb, event -> event.encode(pb) }
|
||||
}
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
@@ -65,5 +85,10 @@ class BedrockParticleEmitter(
|
||||
rate = ParticleEmitterRate.readFromBuffer(buffer)
|
||||
shape = ParticleEmitterShape.readFromBuffer(buffer)
|
||||
lifetime = ParticleEmitterLifetime.readFromBuffer(buffer)
|
||||
eventTimeline.decode(buffer)
|
||||
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
travelDistanceEvents.decode(buffer)
|
||||
loopingTravelDistanceEvents = buffer.readList { LoopingTravelDistanceEventTrigger(0.0, mutableListOf()).also { it.decode(buffer) } }
|
||||
}
|
||||
}
|
||||
+5
@@ -136,6 +136,11 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
|
||||
val interval = activeTimeValue + sleepTime
|
||||
val displacement = emitterAge % interval
|
||||
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
|
||||
|
||||
if (emitterAge > activeTimeValue && sleepTime == 0.0) {
|
||||
return ParticleEmitterAction.STOP
|
||||
}
|
||||
|
||||
return if (displacement < activeTimeValue) {
|
||||
ParticleEmitterAction.GO
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ class PointParticleEmitterShape(
|
||||
override val type = ParticleEmitterShapeType.POINT
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3d {
|
||||
return getNewParticlePosition(runtime, entity) // Questionable
|
||||
return Vec3d.ZERO
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* An event that can be referenced from various particle event triggers. The events are not necessarily particles,
|
||||
* but can be combinations of particles, sounds, and MoLang expressions.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class ParticleEvent(
|
||||
var particleEffect: EventParticleEffect? = null,
|
||||
var soundEffect: EventSoundEffect? = null,
|
||||
var expression: ExpressionLike? = null
|
||||
): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC: Codec<ParticleEvent> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
EventParticleEffect.CODEC.optionalFieldOf("particle_effect", null).forGetter { it.particleEffect },
|
||||
EventSoundEffect.CODEC.optionalFieldOf("sound_effect", null).forGetter { it.soundEffect },
|
||||
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
|
||||
).apply(instance) { particleEffect, soundEffect, expression ->
|
||||
ParticleEvent(
|
||||
particleEffect,
|
||||
soundEffect,
|
||||
expression?.asExpressionLike()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeNullable(particleEffect) { pb, effect ->
|
||||
pb.writeIdentifier(effect.effect)
|
||||
pb.writeEnumConstant(effect.type)
|
||||
pb.writeNullable(effect.expression) { pb, expr -> pb.writeString(expr.toString()) }
|
||||
}
|
||||
buffer.writeNullable(soundEffect) { pb, effect -> pb.writeIdentifier(effect.sound) }
|
||||
buffer.writeNullable(expression) { pb, expr -> pb.writeString(expr.toString()) }
|
||||
}
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
particleEffect = buffer.readNullable { pb -> EventParticleEffect(
|
||||
pb.readIdentifier(),
|
||||
pb.readEnumConstant(EventParticleEffect.EventParticleType::class.java),
|
||||
pb.readNullable { pb.readString().asExpressionLike() }
|
||||
) }
|
||||
soundEffect = buffer.readNullable { pb -> EventSoundEffect(pb.readIdentifier()) }
|
||||
expression = buffer.readNullable { pb -> pb.readString().asExpressionLike() }
|
||||
}
|
||||
|
||||
fun run(storm: ParticleStorm, particle: SnowstormParticle?) {
|
||||
particleEffect?.let { effect ->
|
||||
val bedrockParticleEffect = BedrockParticleEffectRepository.getEffect(effect.effect) ?: return@let
|
||||
val rootMatrix = when (effect.type) {
|
||||
EventParticleEffect.EventParticleType.EMITTER -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
|
||||
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
|
||||
EventParticleEffect.EventParticleType.PARTICLE,
|
||||
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
|
||||
}
|
||||
|
||||
val sourceVelocity = when (effect.type) {
|
||||
EventParticleEffect.EventParticleType.EMITTER -> storm.sourceVelocity().let { { it } }
|
||||
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.sourceVelocity
|
||||
EventParticleEffect.EventParticleType.PARTICLE -> { { Vec3d.ZERO } }
|
||||
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getVelocityX(), it.getVelocityY(), it.getVelocityZ()) } ?: Vec3d.ZERO).let { { it } }
|
||||
}
|
||||
|
||||
val newStorm = ParticleStorm(
|
||||
effect = bedrockParticleEffect,
|
||||
matrixWrapper = rootMatrix,
|
||||
world = storm.world,
|
||||
sourceVelocity = sourceVelocity,
|
||||
sourceAlive = storm.sourceAlive,
|
||||
sourceVisible = storm.sourceVisible,
|
||||
onDespawn = {},
|
||||
runtime = MoLangRuntime().setup().also { it.environment.structs["query"] = storm.runtime.environment.getQueryStruct() },
|
||||
entity = storm.entity
|
||||
)
|
||||
|
||||
effect.expression?.resolve(newStorm.runtime)
|
||||
|
||||
newStorm.spawn()
|
||||
}
|
||||
soundEffect?.let { effect ->
|
||||
val position = particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())
|
||||
val world = storm.world
|
||||
val soundEvent = SoundEvent.of(effect.sound)
|
||||
world.playSound(position.x, position.y, position.z, soundEvent, SoundCategory.NEUTRAL, 1F, 1F, true)
|
||||
}
|
||||
expression?.resolve(storm.runtime)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A particle component of a [ParticleEvent]. It contains the effect to play, the spawning type, and a pre-run
|
||||
* expression to run in the new storm's runtime.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class EventParticleEffect(
|
||||
val effect: Identifier,
|
||||
val type: EventParticleType,
|
||||
val expression: ExpressionLike? = null
|
||||
) {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<EventParticleEffect> { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("effect").forGetter { it.effect },
|
||||
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
|
||||
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
|
||||
).apply(instance) { effect, type, expression ->
|
||||
EventParticleEffect(
|
||||
effect,
|
||||
EventParticleType.valueOf(type),
|
||||
expression?.asExpressionLike()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class EventParticleType {
|
||||
EMITTER,
|
||||
EMITTER_BOUND,
|
||||
PARTICLE,
|
||||
PARTICLE_WITH_VELOCITY
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple sound effect component of a [ParticleEvent]. Plays a sound at the particle location, or if run from an
|
||||
* emitter event, at the emitter's location.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class EventSoundEffect(
|
||||
val sound: Identifier,
|
||||
) {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<EventSoundEffect> { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("sound").forGetter { it.sound }
|
||||
).apply(instance, ::EventSoundEffect)
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import com.mojang.serialization.codecs.UnboundedMapCodec
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/*
|
||||
* The different references to events that can be triggered by an effect.
|
||||
*/
|
||||
|
||||
/**
|
||||
* It really is simple.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class SimpleEventTrigger(var event: String): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<SimpleEventTrigger> { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.STRING.fieldOf("event").forGetter { it.event }
|
||||
).apply(instance, ::SimpleEventTrigger)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(event)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
event = buffer.readString()
|
||||
}
|
||||
|
||||
fun trigger(storm: ParticleStorm, particle: SnowstormParticle?) {
|
||||
val event = storm.effect.events[event] ?: return
|
||||
event.run(storm, particle)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An event trigger that outlines specific times at which point groups of effects will play. This can be used
|
||||
* on the basis of time but is also used in the case of travel distance.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class EventTriggerTimeline(var map: MutableMap<Double, MutableList<String>>): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<EventTriggerTimeline> { instance ->
|
||||
instance.group(
|
||||
UnboundedMapCodec(PrimitiveCodec.DOUBLE, PrimitiveCodec.STRING.listOf()).fieldOf("map").forGetter { it.map }
|
||||
).apply(instance, ::EventTriggerTimeline)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeMap(map, { pb, k -> pb.writeDouble(k) }, { pb, v -> pb.writeCollection(v) { _, s -> pb.writeString(s) } })
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
map = buffer.readMap({ pb -> pb.readDouble() }, { pb -> pb.readList { pb.readString() } })
|
||||
}
|
||||
|
||||
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousTime: Double, newTime: Double) {
|
||||
val events = map.entries.filter { it.key in previousTime..newTime }.flatMap { it.value }
|
||||
events.forEach { event ->
|
||||
val event = storm.effect.events[event] ?: return
|
||||
event.run(storm, particle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An event trigger that takes the total distance travelled by the emitter and modulos it across [distance] to know
|
||||
* if we need to play it again.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class LoopingTravelDistanceEventTrigger(var distance: Double, var events: MutableList<String>): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<LoopingTravelDistanceEventTrigger> { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.DOUBLE.fieldOf("distance").forGetter { it.distance },
|
||||
PrimitiveCodec.STRING.listOf().fieldOf("events").forGetter { it.events }
|
||||
).apply(instance, ::LoopingTravelDistanceEventTrigger)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeDouble(distance)
|
||||
buffer.writeCollection(events) { _, s -> buffer.writeString(s) }
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
distance = buffer.readDouble()
|
||||
events = buffer.readList { buffer.readString() }
|
||||
}
|
||||
|
||||
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousDistance: Double, currentDistance: Double) {
|
||||
if (previousDistance < distance && currentDistance >= distance) {
|
||||
events.mapNotNull { storm.effect.events[it] }.forEach { it.run(storm, particle) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class InwardsMotionDirection : ParticleMotionDirection {
|
||||
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)
|
||||
emitterPos.subtract(particlePos)
|
||||
}.normalize()
|
||||
}
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
|
||||
+10
-3
@@ -30,9 +30,16 @@ abstract class SingleEntitySpawnAction<T : Entity>(
|
||||
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))
|
||||
var shouldSpawn = false
|
||||
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = {
|
||||
ctx.world.spawnEntity(e)
|
||||
shouldSpawn = true
|
||||
})
|
||||
|
||||
return if (shouldSpawn) {
|
||||
this.entity.emit(e)
|
||||
EntitySpawnResult(listOf(e))
|
||||
} else null
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-2
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.adapter.conversions
|
||||
|
||||
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.Natures
|
||||
@@ -98,8 +99,9 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
|
||||
result.level = nbt.getInt("Level")
|
||||
result.addExperience(SidemodExperienceSource("Reforged"), nbt.getInt("EXP"))
|
||||
result.setFriendship(nbt.getInt("Friendship"))
|
||||
result.ability = (result.form.abilities.find { it.template.name == nbt.getString("Ability") } ?: result.form.abilities.first())
|
||||
.template.create()
|
||||
Abilities.get(nbt.getString("Ability"))?.let { template ->
|
||||
result.updateAbility(template.create(forced = result.form.abilities.none { it.template == template }))
|
||||
}
|
||||
result.nature = Natures.getNature(Identifier(ReforgedNatures.values()[nbt.getInt("Nature")].name.lowercase())) ?: Natures.getRandomNature()
|
||||
result.mintedNature = Natures.getNature(Identifier(ReforgedNatures.values()[nbt.getInt("MintNature")].name.lowercase()))
|
||||
result.currentHealth = nbt.getInt("Health")
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.storage.player
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.net.messages.client.starter.SetClientPlayerDataPacket
|
||||
import java.util.UUID
|
||||
@@ -23,6 +24,7 @@ data class PlayerData(
|
||||
var starterSelected: Boolean,
|
||||
var starterUUID: UUID?,
|
||||
var keyItems: MutableSet<Identifier>,
|
||||
var tmSet: MutableSet<Identifier>,
|
||||
var battleTheme: Identifier?,
|
||||
val extraData: MutableMap<String, PlayerDataExtension>,
|
||||
) {
|
||||
@@ -42,6 +44,7 @@ data class PlayerData(
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
battleTheme = CobblemonSounds.PVP_BATTLE.id,
|
||||
tmSet = mutableSetOf(),
|
||||
extraData = mutableMapOf()
|
||||
)
|
||||
}
|
||||
|
||||
+3
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.storage.player.adapter
|
||||
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethod
|
||||
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -31,6 +33,7 @@ class JsonPlayerData: PlayerDataStoreAdapter {
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(PlayerDataExtension::class.java, PlayerDataExtensionAdapter)
|
||||
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
}
|
||||
|
||||
@@ -144,6 +144,9 @@ object CobblemonItemTags {
|
||||
@JvmField
|
||||
val POTTERY_SHERDS = create("decorated_pot_sherds")
|
||||
|
||||
@JvmField
|
||||
val ABILITY_CHANGERS = create("ability_changers")
|
||||
|
||||
private fun create(path: String) = TagKey.of(RegistryKeys.ITEM, cobblemonResource(path))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
interface ObtainMethod {
|
||||
/**
|
||||
* Whether to check this [ObtainMethod] once every second.
|
||||
*/
|
||||
val passive: Boolean
|
||||
|
||||
/**
|
||||
* Checks if the [ObtainMethod] condition is true.
|
||||
*
|
||||
* @param player The player to use when checking.
|
||||
* @return Whether the check succeeded.
|
||||
*/
|
||||
fun matches(player: ServerPlayerEntity): Boolean
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonSerializer
|
||||
import net.minecraft.util.Identifier
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* A type adapter for [ObtainMethod]s.
|
||||
* For the default implementation see [CobblemonObtainMethodAdapter].
|
||||
*
|
||||
* @author Apion
|
||||
* @since November 22nd, 2023
|
||||
*/
|
||||
interface ObtainMethodAdapter : JsonDeserializer<ObtainMethod>, JsonSerializer<ObtainMethod> {
|
||||
/**
|
||||
* Register a [ObtainMethod] to be used by this adapter.
|
||||
*
|
||||
* @param type The [KClass] of the [ObtainMethod].
|
||||
* @param identifier The expected [Identifier] in the parsed JSON.
|
||||
*/
|
||||
fun register(type: KClass<out ObtainMethod>, identifier: Identifier)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.tms
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class TechnicalMachine(
|
||||
val moveName: String,
|
||||
val recipe: TechnicalMachineRecipe?,
|
||||
val obtainMethods: List<ObtainMethod> = emptyList(),
|
||||
val type: String,
|
||||
val primaryColor: Int?,
|
||||
val secondaryColor: Int?
|
||||
) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Filters all available [TechnicalMachines] based on three optional arguments
|
||||
*
|
||||
* @param search A [String] to check against move names
|
||||
* @param type An [ElementalType] to check for
|
||||
* @param pokemon A [Pokemon] to check the [Learnset] of
|
||||
* @return A [MutableSet] of [TechnicalMachine] that passed the filter
|
||||
* @author whatsy
|
||||
*/
|
||||
fun filterTms(search: String?, type: ElementalType?, pokemon: Pokemon?): MutableSet<TechnicalMachine> {
|
||||
val tms = TechnicalMachines.tmMap.values.toMutableSet()
|
||||
|
||||
type?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (ElementalTypes.get(tm.type) != type) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pokemon?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
val move = Moves.getByName(tm.moveName)
|
||||
if (!pokemon.species.moves.tmLearnableMoves().contains(move)) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
search?.let {
|
||||
val iterator = tms.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val tm = iterator.next()
|
||||
if (!tm.translatedMoveName().toString().contains(search, true)) {
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tms
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the [Identifier] of this [TechnicalMachine]
|
||||
*
|
||||
* @return The [Identifier] of this [TechnicalMachine] (or null if not found)
|
||||
* @author whatsy
|
||||
*/
|
||||
fun id(): Identifier? {
|
||||
TechnicalMachines.tmMap.forEach {
|
||||
if (TechnicalMachines.tmMap[it.key] == this) return it.key
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks this [TechnicalMachine] for the player.
|
||||
*
|
||||
* @param player The [ServerPlayerEntity] to give this [TechnicalMachine] to.
|
||||
* @return Whether the player was successfully granted the [TechnicalMachine]
|
||||
* @author whatsy
|
||||
*/
|
||||
fun unlock(player: ServerPlayerEntity): Boolean {
|
||||
if (id() == null) return false
|
||||
Cobblemon.playerData.get(player).tmSet.add(id()!!)
|
||||
if (!obtainMethods.any { it is NoneObtainMethod }) player.sendMessage(lang("tms.unlock_tm", lang("move.$moveName")))
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [MutableText] of the translated move name of this [TechnicalMachine]
|
||||
*
|
||||
* @return This [TechnicalMachine]'s move name, translated
|
||||
* @author whatsy
|
||||
*/
|
||||
fun translatedMoveName(): MutableText {
|
||||
return lang("move." + this.moveName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.tms
|
||||
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Represents the ingredients necessary to craft a [TechnicalMachine] in the [TMBlock]
|
||||
*/
|
||||
data class TechnicalMachineRecipe(
|
||||
val item: Identifier,
|
||||
val count: Int
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.tms
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.item.TechnicalMachineItem
|
||||
import com.cobblemon.mod.common.registry.ItemTagCondition
|
||||
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
|
||||
override val gson = GsonBuilder()
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
|
||||
.create()
|
||||
override val typeToken = TypeToken.get(TechnicalMachine::class.java)
|
||||
override val resourcePath = "tms"
|
||||
override val id = cobblemonResource("technical_machines")
|
||||
override val type = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<TechnicalMachines>()
|
||||
|
||||
val tmMap = mutableMapOf<Identifier, TechnicalMachine>()
|
||||
|
||||
val tagMap = mutableMapOf<ItemTagCondition, TechnicalMachine>()
|
||||
|
||||
val passiveTms = mutableMapOf<Identifier, TechnicalMachine>()
|
||||
override fun reload(data: Map<Identifier, TechnicalMachine>) {
|
||||
data.forEach {id, tm ->
|
||||
tmMap[id] = tm
|
||||
if (tm.obtainMethods.any { it.passive }) passiveTms.put(id, tm)
|
||||
}
|
||||
}
|
||||
override fun sync(player: ServerPlayerEntity) { }
|
||||
|
||||
fun getTechnicalMachineFromStack(item: ItemStack?): TechnicalMachine? {
|
||||
if (item == null) {
|
||||
return null
|
||||
}
|
||||
return TechnicalMachineItem.getMoveNbt(item)
|
||||
}
|
||||
|
||||
fun getStackFromTechnicalMachine(tm: TechnicalMachine): ItemStack {
|
||||
val tmItem = CobblemonItems.TECHNICAL_MACHINE.defaultStack
|
||||
return CobblemonItems.TECHNICAL_MACHINE.setNbt(tmItem,tm.id().toString())
|
||||
}
|
||||
|
||||
fun isTechnicalMachine(item: ItemStack): Boolean {
|
||||
return item.item is TechnicalMachineItem
|
||||
}
|
||||
|
||||
|
||||
fun checkPassives(player: ServerPlayerEntity) {
|
||||
val playerTms = Cobblemon.playerData.get(player).tmSet
|
||||
passiveTms.forEach { (id, tm) ->
|
||||
if (tm.obtainMethods.all { it.matches(player) } && !playerTms.contains(id)) tm.unlock(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,23 @@ import net.minecraft.text.MutableText
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Class representing a type of a Pokemon or Move
|
||||
* Class representing a type of Pokémon or Move
|
||||
*
|
||||
* @param name: The English name used to load / find it (spaces -> _)
|
||||
* @param displayName: A Component used to display the name, normally a TranslatableText
|
||||
* @param textureXMultiplier: The multiplier by which the TypeWidget shall move the display
|
||||
* @param resourceLocation: The location of the resource used in the TypeWidget
|
||||
* @param primaryColor: The primary color of this type used to color [TechnicalMachineItem].
|
||||
* @param secondaryColor: The secondary color of this type used to color [TechnicalMachineItem]
|
||||
* @param typeGem: The Type Gem item used to craft a [TechnicalMachineItem] of this type.
|
||||
*/
|
||||
class ElementalType(
|
||||
val name: String,
|
||||
val displayName: MutableText,
|
||||
val hue: Int,
|
||||
val textureXMultiplier: Int,
|
||||
val resourceLocation: Identifier = Identifier(Cobblemon.MODID, "ui/types.png")
|
||||
val resourceLocation: Identifier = Identifier(Cobblemon.MODID, "ui/types.png"),
|
||||
val primaryColor: Int,
|
||||
val secondaryColor: Int,
|
||||
val typeGem: Identifier
|
||||
)
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.types
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
|
||||
@@ -22,134 +23,180 @@ object ElementalTypes {
|
||||
name = "normal",
|
||||
displayName = Text.translatable("cobblemon.type.normal"),
|
||||
hue = 0xDDDDCF,
|
||||
textureXMultiplier = 0
|
||||
textureXMultiplier = 0,
|
||||
primaryColor = 0xCCCCC1,
|
||||
secondaryColor = 0xF9FFFC
|
||||
)
|
||||
|
||||
val FIRE = register(
|
||||
name = "fire",
|
||||
displayName = Text.translatable("cobblemon.type.fire"),
|
||||
hue = 0xE55C32,
|
||||
textureXMultiplier = 1
|
||||
textureXMultiplier = 1,
|
||||
primaryColor = 0xFF8766,
|
||||
secondaryColor = 0xFFD9A5
|
||||
)
|
||||
|
||||
val WATER = register(
|
||||
name = "water",
|
||||
displayName = Text.translatable("cobblemon.type.water"),
|
||||
hue = 0x4A9BE8,
|
||||
textureXMultiplier = 2
|
||||
textureXMultiplier = 2,
|
||||
primaryColor = 0x7F9DFF,
|
||||
secondaryColor = 0xA5EEFF
|
||||
)
|
||||
|
||||
val GRASS = register(
|
||||
name = "grass",
|
||||
displayName = Text.translatable("cobblemon.type.grass"),
|
||||
hue = 0x4DBC3C,
|
||||
textureXMultiplier = 3
|
||||
textureXMultiplier = 3,
|
||||
primaryColor = 0x66CC99,
|
||||
secondaryColor = 0xD6FFA5
|
||||
)
|
||||
|
||||
val ELECTRIC = register(
|
||||
name = "electric",
|
||||
displayName = Text.translatable("cobblemon.type.electric"),
|
||||
hue = 0xEFD128,
|
||||
textureXMultiplier = 4
|
||||
textureXMultiplier = 4,
|
||||
primaryColor = 0xFFCF3F,
|
||||
secondaryColor = 0xFFFFBF
|
||||
)
|
||||
|
||||
val ICE = register(
|
||||
name = "ice",
|
||||
displayName = Text.translatable("cobblemon.type.ice"),
|
||||
hue = 0x6BC3EF,
|
||||
textureXMultiplier = 5
|
||||
textureXMultiplier = 5,
|
||||
primaryColor = 0x8CC7FF,
|
||||
secondaryColor = 0xD6FFFF
|
||||
)
|
||||
|
||||
val FIGHTING = register(
|
||||
name = "fighting",
|
||||
displayName = Text.translatable("cobblemon.type.fighting"),
|
||||
hue = 0xC44C5C,
|
||||
textureXMultiplier = 6
|
||||
textureXMultiplier = 6,
|
||||
primaryColor = 0xCC6673,
|
||||
secondaryColor = 0xFFADA5
|
||||
)
|
||||
|
||||
val POISON = register(
|
||||
name = "poison",
|
||||
displayName = Text.translatable("cobblemon.type.poison"),
|
||||
hue = 0xA24BD8,
|
||||
textureXMultiplier = 7
|
||||
textureXMultiplier = 7,
|
||||
primaryColor = 0x8D66CC,
|
||||
secondaryColor = 0xEDA5FF
|
||||
)
|
||||
|
||||
val GROUND = register(
|
||||
name = "ground",
|
||||
displayName = Text.translatable("cobblemon.type.ground"),
|
||||
hue = 0xD89950,
|
||||
textureXMultiplier = 8
|
||||
textureXMultiplier = 8,
|
||||
primaryColor = 0xE5A27E,
|
||||
secondaryColor = 0xFFE1A5
|
||||
)
|
||||
|
||||
val FLYING = register(
|
||||
name = "flying",
|
||||
displayName = Text.translatable("cobblemon.type.flying"),
|
||||
hue = 0xBCC1FF,
|
||||
textureXMultiplier = 9
|
||||
textureXMultiplier = 9,
|
||||
primaryColor = 0x8CB6FF,
|
||||
secondaryColor = 0xE8DDFF
|
||||
)
|
||||
|
||||
val PSYCHIC = register(
|
||||
name = "psychic",
|
||||
displayName = Text.translatable("cobblemon.type.psychic"),
|
||||
hue = 0xD86AD6,
|
||||
textureXMultiplier = 10
|
||||
textureXMultiplier = 10,
|
||||
primaryColor = 0xE48CFF,
|
||||
secondaryColor = 0xFFCCF6
|
||||
)
|
||||
|
||||
val BUG = register(
|
||||
name = "bug",
|
||||
displayName = Text.translatable("cobblemon.type.bug"),
|
||||
hue = 0xA2C831,
|
||||
textureXMultiplier = 11
|
||||
textureXMultiplier = 11,
|
||||
primaryColor = 0xAFCC66,
|
||||
secondaryColor = 0xF6FFBF
|
||||
)
|
||||
|
||||
val ROCK = register(
|
||||
name = "rock",
|
||||
displayName = Text.translatable("cobblemon.type.rock"),
|
||||
hue = 0xAA9666,
|
||||
textureXMultiplier = 12
|
||||
textureXMultiplier = 12,
|
||||
primaryColor = 0x997F4C,
|
||||
secondaryColor = 0xDBC6A4
|
||||
)
|
||||
|
||||
val GHOST = register(
|
||||
name = "ghost",
|
||||
displayName = Text.translatable("cobblemon.type.ghost"),
|
||||
hue = 0x9572E5,
|
||||
textureXMultiplier = 13
|
||||
textureXMultiplier = 13,
|
||||
primaryColor = 0x8E8CFF,
|
||||
secondaryColor = 0xE8A5FF
|
||||
)
|
||||
|
||||
val DRAGON = register(
|
||||
name = "dragon",
|
||||
displayName = Text.translatable("cobblemon.type.dragon"),
|
||||
hue = 0x535DE8,
|
||||
textureXMultiplier = 14
|
||||
textureXMultiplier = 14,
|
||||
primaryColor = 0xBA8CFF,
|
||||
secondaryColor = 0xA5E5FF
|
||||
)
|
||||
|
||||
val DARK = register(
|
||||
name = "dark",
|
||||
displayName = Text.translatable("cobblemon.type.dark"),
|
||||
hue = 0x5C6CB2,
|
||||
textureXMultiplier = 15
|
||||
textureXMultiplier = 15,
|
||||
primaryColor = 0x5966B2,
|
||||
secondaryColor = 0xA5B1FF
|
||||
)
|
||||
|
||||
val STEEL = register(
|
||||
name = "steel",
|
||||
displayName = Text.translatable("cobblemon.type.steel"),
|
||||
hue = 0xC3CCE0,
|
||||
textureXMultiplier = 16
|
||||
textureXMultiplier = 16,
|
||||
primaryColor = 0x858EB2,
|
||||
secondaryColor = 0xF4FFFD
|
||||
)
|
||||
|
||||
val FAIRY = register(
|
||||
name = "fairy",
|
||||
displayName = Text.translatable("cobblemon.type.fairy"),
|
||||
hue = 0xEA727E,
|
||||
textureXMultiplier = 17
|
||||
textureXMultiplier = 17,
|
||||
primaryColor = 0xFF8CB6,
|
||||
secondaryColor = 0xFFD0CC
|
||||
)
|
||||
|
||||
fun register(name: String, displayName: MutableText, hue: Int, textureXMultiplier: Int): ElementalType {
|
||||
fun register(
|
||||
name: String,
|
||||
displayName: MutableText,
|
||||
hue: Int,
|
||||
textureXMultiplier: Int,
|
||||
primaryColor: Int,
|
||||
secondaryColor: Int
|
||||
): ElementalType {
|
||||
return ElementalType(
|
||||
name = name,
|
||||
displayName = displayName,
|
||||
hue = hue,
|
||||
textureXMultiplier = textureXMultiplier
|
||||
textureXMultiplier = textureXMultiplier,
|
||||
primaryColor = primaryColor,
|
||||
secondaryColor = secondaryColor,
|
||||
typeGem = cobblemonResource("${name}_gem")
|
||||
).also {
|
||||
allTypes.add(it)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.HorizontalFacingBlock.*
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.BlockItem
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.ItemScatterer
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class DisplayCaseBlock(settings: Settings) : BlockWithEntity(settings) {
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(FACING, Direction.NORTH)
|
||||
}
|
||||
|
||||
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
|
||||
var blockState = defaultState
|
||||
val worldView = ctx.world
|
||||
val blockPos = ctx.blockPos
|
||||
ctx.placementDirections.forEach { direction ->
|
||||
if (direction.axis.isHorizontal) {
|
||||
blockState = blockState.with(FACING, direction) as BlockState
|
||||
if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState): DisplayCaseBlockEntity {
|
||||
return DisplayCaseBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
return if (direction == state.get(FACING) && !state.canPlaceAt(world, pos)) Blocks.AIR.defaultState
|
||||
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun onUse(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
hand: Hand,
|
||||
hit: BlockHitResult
|
||||
): ActionResult {
|
||||
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
|
||||
val result = entity.updateItem(player, hand)
|
||||
if ((hit.side != Direction.UP && hit.side != Direction.DOWN) && result == ActionResult.SUCCESS) {
|
||||
world.setBlockState(pos, state.with(FACING, hit.side.opposite))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
|
||||
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
|
||||
if (!entity.getStack().isEmpty && !player.isCreative) {
|
||||
ItemScatterer.spawn(world, pos, entity.inv)
|
||||
}
|
||||
super.onBreak(world, pos, state, player)
|
||||
}
|
||||
|
||||
override fun getRenderType(state: BlockState?) = BlockRenderType.MODEL
|
||||
|
||||
override fun getComparatorOutput(state: BlockState, world: World, pos: BlockPos): Int {
|
||||
val stack = (world.getBlockEntity(pos) as DisplayCaseBlockEntity).getStack()
|
||||
|
||||
if (stack.isEmpty) return 0
|
||||
if (stack.item is PokeBallItem) return 3
|
||||
if (stack.item is BlockItem) return 2
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun hasComparatorOutput(state: BlockState?) = true
|
||||
|
||||
override fun canPathfindThrough(state: BlockState, world: BlockView, pos: BlockPos, type: NavigationType) = false
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import net.minecraft.block.AbstractBlock.Settings
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.HorizontalFacingBlock
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.util.*
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
class ModStationBlock(settings: Settings) : Block(settings) {
|
||||
|
||||
companion object {
|
||||
val PART = EnumProperty.of("part", ModStationPart::class.java)
|
||||
}
|
||||
|
||||
enum class ModStationPart(private val label: String) : StringIdentifiable {
|
||||
TOP("top"),
|
||||
BOTTOM("bottom");
|
||||
|
||||
override fun asString() = label
|
||||
}
|
||||
|
||||
init {
|
||||
defaultState = this.stateManager.defaultState.with(
|
||||
HorizontalFacingBlock.FACING, Direction.SOUTH
|
||||
).with(PART, ModStationPart.BOTTOM)
|
||||
}
|
||||
|
||||
override fun onPlaced(
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
itemStack: ItemStack
|
||||
) {
|
||||
world.setBlockState(pos.up(), state
|
||||
.with(PART, ModStationPart.TOP)
|
||||
)
|
||||
world.updateNeighbors(pos, Blocks.AIR)
|
||||
state.updateNeighbors(world, pos, 3)
|
||||
}
|
||||
|
||||
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
|
||||
val abovePosition = blockPlaceContext.blockPos.up()
|
||||
val world = blockPlaceContext.world
|
||||
if (world.getBlockState(abovePosition).canReplace(blockPlaceContext) && !world.isOutOfHeightLimit(abovePosition)) {
|
||||
return defaultState
|
||||
.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing)
|
||||
.with(PART, ModStationPart.BOTTOM)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
val blockPos = pos.down()
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
return if (state.get(PART) == ModStationPart.BOTTOM) blockState.isSideSolidFullSquare(world, blockPos, Direction.UP) else blockState.isOf(this)
|
||||
}
|
||||
|
||||
override fun canPathfindThrough(
|
||||
state: BlockState?,
|
||||
world: BlockView?,
|
||||
pos: BlockPos?,
|
||||
type: NavigationType?
|
||||
) = false
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(HorizontalFacingBlock.FACING)
|
||||
builder.add(PART)
|
||||
}
|
||||
|
||||
override fun rotate(blockState: BlockState, rotation: BlockRotation) = blockState.with(HorizontalFacingBlock.FACING, rotation.rotate(blockState.get(HorizontalFacingBlock.FACING)))
|
||||
|
||||
override fun mirror(blockState: BlockState, mirror: BlockMirror): BlockState {
|
||||
return blockState.rotate(mirror.getRotation(blockState.get(HorizontalFacingBlock.FACING)))
|
||||
}
|
||||
|
||||
override fun onStateReplaced(state: BlockState, world: World, pos: BlockPos?, newState: BlockState, moved: Boolean) {
|
||||
if (!state.isOf(newState.block)) super.onStateReplaced(state, world, pos, newState, moved)
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
val isStation = neighborState.isOf(this)
|
||||
val part = state.get(PART)
|
||||
if (!isStation && part == ModStationPart.TOP && neighborPos == pos.down()) {
|
||||
return Blocks.AIR.defaultState
|
||||
} else if (!isStation && part == ModStationPart.BOTTOM && neighborPos == pos.up()) {
|
||||
return Blocks.AIR.defaultState
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
override fun onUse(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
hand: Hand,
|
||||
hit: BlockHitResult
|
||||
): ActionResult {
|
||||
player.openHandledScreen(state.createScreenHandlerFactory(world, pos))
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.block.entity.TMBlockEntity
|
||||
import com.cobblemon.mod.common.gui.TMMScreenHandler
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BrewingStandBlockEntity
|
||||
import net.minecraft.entity.ItemEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory
|
||||
import net.minecraft.screen.SimpleNamedScreenHandlerFactory
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.stat.Stats
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.BlockMirror
|
||||
import net.minecraft.util.BlockRotation
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
class TMBlock(properties: Settings): BlockWithEntity(properties), Waterloggable, InventoryProvider {
|
||||
/*val FACING = Properties.FACING
|
||||
val TRIGGERED = Properties.TRIGGERED
|
||||
val WATERLOGGED = Properties.WATERLOGGED*/
|
||||
|
||||
//var filterTM: TechnicalMachine? = null
|
||||
//var previousFilterTM: TechnicalMachine? = null
|
||||
var loadedMaterials: MutableList<ItemStack> = mutableListOf()
|
||||
//val inv = TMBlockInventory(this)
|
||||
|
||||
/*class TMBlockInventory(val tmBlock: TMBlock) : SidedInventory {
|
||||
override fun clear() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getAvailableSlots(side: Direction?): IntArray {
|
||||
return IntArray(1)
|
||||
}
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
// todo only allow for hopper to insert materials if it has a filterTM in it
|
||||
if (tmBlock.filterTM != null && stack != null) {
|
||||
|
||||
// if material is needed then load it
|
||||
if (tmBlock.materialNeeded(tmBlock.filterTM!!, stack)) {
|
||||
tmBlock.loadMaterial(stack)
|
||||
return true
|
||||
}
|
||||
else
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}*/
|
||||
companion object {
|
||||
//val WATERLOGGED = BooleanProperty.of("waterlogged")
|
||||
val ON = BooleanProperty.of("on")
|
||||
val FACING = Properties.FACING
|
||||
val TRIGGERED = Properties.TRIGGERED
|
||||
val WATERLOGGED = Properties.WATERLOGGED
|
||||
|
||||
private var NORTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0, 1.0, 0.3125, 0.9375),
|
||||
VoxelShapes.cuboid(0.0, 0.3125, 0.75, 1.0, 0.9375, 0.9375),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
private var SOUTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0625, 1.0, 0.3125, 1.0),
|
||||
VoxelShapes.cuboid(0.0, 0.3125, 0.0625, 1.0, 0.9375, 0.25),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
private var WEST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0, 0.9375, 0.3125, 1.0),
|
||||
VoxelShapes.cuboid(0.75, 0.3125, 0.0, 0.9375, 0.9375, 1.0),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
private var EAST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0625, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0, 0.25, 0.9375, 1.0),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
defaultState = this.stateManager.defaultState.with(FACING, Direction.NORTH)
|
||||
.with(WATERLOGGED, false)
|
||||
.with(ON, false)
|
||||
.with(TRIGGERED, false)
|
||||
}
|
||||
|
||||
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
|
||||
return defaultState
|
||||
.with(FACING, blockPlaceContext.horizontalPlayerFacing)
|
||||
.with(WATERLOGGED, blockPlaceContext.world.getFluidState(blockPlaceContext.blockPos).fluid == Fluids.WATER)
|
||||
.with(ON, false)
|
||||
}
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
super.onBreak(world, pos, state, player)
|
||||
// todo dump materials and filterTM on the ground
|
||||
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, sourceBlock: Block?, sourcePos: BlockPos?, notify: Boolean) {
|
||||
val bl = world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.up())
|
||||
val bl2 = state.get(TRIGGERED)
|
||||
if (bl && !bl2) {
|
||||
world.scheduleBlockTick(pos, this, 4)
|
||||
world.setBlockState(pos, state.with(TRIGGERED, true) as BlockState, NO_REDRAW)
|
||||
} else if (!bl && bl2) {
|
||||
world.setBlockState(pos, state.with(TRIGGERED, false) as BlockState, NO_REDRAW)
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun scheduledTick(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: net.minecraft.util.math.random.Random?) {
|
||||
val inventory = (world!!.getBlockEntity(pos) as TMBlockEntity).tmmInventory
|
||||
val tmEntity = (world.getBlockEntity(pos) as TMBlockEntity)
|
||||
|
||||
if(world == null || pos == null) {
|
||||
return
|
||||
}
|
||||
//val tankEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
|
||||
if (tmEntity.blockState.get(ON)) {
|
||||
//print("TMM is in use so scheduled Tick is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
//this.onTriggerEvent(state, world, pos, random)
|
||||
// todo if the output slot is empty then try to craft a TM to that slot
|
||||
if (ItemStack.areItemsEqual(inventory.items!!.get(3), ItemStack.EMPTY)) {
|
||||
if (state?.let { getInventory(it, world, pos) } != null) {
|
||||
val itemStack: ItemStack
|
||||
itemStack = if (inventory.filterTM != null)
|
||||
inventory.filterTM!!.copy()
|
||||
else
|
||||
ItemStack(CobblemonItems.BLANK_TM, 1)
|
||||
val tm = TechnicalMachines.getTechnicalMachineFromStack(itemStack)
|
||||
// todo use isReadyToCraftTM to finalize the creation
|
||||
if (((inventory.filterTM != null && tm != null) && isReadyToCraftTM(state, world, pos, tm)) || isReadyToCraftBlankTM(state, world, pos)) {
|
||||
|
||||
// create TM item to the output slot
|
||||
inventory.items!!.set(3, itemStack)
|
||||
tmEntity.markDirty()
|
||||
|
||||
// Play sound for TM creation
|
||||
world.playSoundServer(tmEntity.blockPos.toVec3d(), CobblemonSounds.TMM_CRAFT, SoundCategory.BLOCKS)
|
||||
}
|
||||
}
|
||||
}
|
||||
// todo if there is an item in the output slot then spit it out
|
||||
else {
|
||||
if (state?.let { getInventory(it, world, pos) } != null) {
|
||||
|
||||
val itemStack = inventory.items!!.get(3)
|
||||
|
||||
// Get the direction the block is facing
|
||||
val facingDirection = state.get(Properties.FACING)?.opposite ?: return
|
||||
|
||||
// Calculate the center position of the block
|
||||
val frontOffset = 0.5 // Half block offset to the front
|
||||
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
|
||||
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
|
||||
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
|
||||
|
||||
// Create the ItemEntity at the center of the block
|
||||
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, itemStack).copy()
|
||||
|
||||
// Create the ItemEntity
|
||||
itemEntity.setVelocity(0.0, 0.0, 0.0)
|
||||
|
||||
// Add the ItemEntity to the world
|
||||
world.spawnEntity(itemEntity)
|
||||
|
||||
//clear inventory of SidedInventory
|
||||
inventory.items?.clear()
|
||||
tmEntity.markDirty()
|
||||
//getInventory(state, world, pos).clear()
|
||||
}
|
||||
}
|
||||
|
||||
/*if (state?.let { getInventory(it, world, pos) } != null) {
|
||||
val itemStack: ItemStack
|
||||
itemStack = if (inventory.filterTM != null)
|
||||
inventory.filterTM!!
|
||||
else
|
||||
ItemStack(CobblemonItems.BLANK_TM, 1)
|
||||
val tm = TechnicalMachines.getTechnicalMachineFromStack(itemStack)
|
||||
// todo use isReadyToCraftTM to finalize the creation
|
||||
if ((inventory.filterTM != null && isReadyToCraftTM(state, world, pos, tm!!)) || isReadyToCraftBlankTM(state, world, pos)) {
|
||||
// Get the direction the block is facing
|
||||
val facingDirection = state.get(Properties.FACING)?.opposite ?: return
|
||||
|
||||
// Calculate the center position of the block
|
||||
val frontOffset = 0.5 // Half block offset to the front
|
||||
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
|
||||
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
|
||||
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
|
||||
|
||||
// Create the ItemEntity at the center of the block
|
||||
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, itemStack).copy()
|
||||
|
||||
// Create the ItemEntity
|
||||
itemEntity.setVelocity(0.0, 0.0, 0.0)
|
||||
|
||||
// Add the ItemEntity to the world
|
||||
world.spawnEntity(itemEntity)
|
||||
|
||||
//clear inventory of SidedInventory
|
||||
inventory.items?.clear()
|
||||
//getInventory(state, world, pos).clear()
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/*val currentTm = filterTM ?: return sendPacketToServer(CraftBlankTMPacket(handler.input.getStack(2)))
|
||||
sendPacketToServer(
|
||||
CraftTMPacket(
|
||||
currentTm,
|
||||
handler.input.getStack(0),
|
||||
handler.input.getStack(1),
|
||||
handler.input.getStack(2)
|
||||
)
|
||||
)*/
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun hasComparatorOutput(state: BlockState?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getComparatorOutput(state: BlockState, world: World?, pos: BlockPos?): Int {
|
||||
|
||||
if(world == null || pos == null) {
|
||||
return 0
|
||||
}
|
||||
val tmBlockEntity = world.getBlockEntity(pos) as TMBlockEntity
|
||||
val tm = TechnicalMachines.getTechnicalMachineFromStack(tmBlockEntity.tmmInventory.filterTM)
|
||||
|
||||
// if TMM has already crafted something and something is in the output slot do not send signal out
|
||||
if (!ItemStack.areItemsEqual(tmBlockEntity.tmmInventory.items?.get(3), ItemStack.EMPTY))
|
||||
return 0
|
||||
|
||||
// if the TMM is ready to craft send signal of 15 out
|
||||
if ((tm != null && isReadyToCraftTM(state, world, pos, tm)) || isReadyToCraftBlankTM(state, world, pos))
|
||||
return 15
|
||||
|
||||
/*if (tmBlockEntity.automationDelay > 0)
|
||||
tmBlockEntity.automationDelay--
|
||||
|
||||
if ((tmBlockEntity.tmmInventory.filterTM != null && isReadyToCraftTM(state, world, pos, tmBlockEntity.tmmInventory.filterTM!!)) || isReadyToCraftBlankTM(state, world, pos)) {
|
||||
if (tmBlockEntity.automationDelay == 0) {
|
||||
tmBlockEntity.resetAutomationDelay()
|
||||
return 15
|
||||
}
|
||||
else
|
||||
return 0
|
||||
}*/
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
override fun getInventory(
|
||||
state: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos
|
||||
): SidedInventory {
|
||||
val tmBlockEntity = world.getBlockEntity(pos) as TMBlockEntity
|
||||
|
||||
return tmBlockEntity.tmmInventory
|
||||
}
|
||||
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
builder.add(WATERLOGGED)
|
||||
builder.add(ON)
|
||||
builder.add(TRIGGERED)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return TMBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun rotate(blockState: BlockState, rotation: BlockRotation) =
|
||||
blockState.with(FACING, rotation.rotate(blockState.get(FACING)))
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun mirror(blockState: BlockState, mirror: BlockMirror): BlockState {
|
||||
return blockState.rotate(mirror.getRotation(blockState.get(FACING)))
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onStateReplaced(state: BlockState, world: World, pos: BlockPos?, newState: BlockState, moved: Boolean) {
|
||||
if (!state.isOf(newState.block)) super.onStateReplaced(state, world, pos, newState, moved)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onUse(
|
||||
blockState: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
interactionHand: Hand,
|
||||
blockHitResult: BlockHitResult
|
||||
): ActionResult {
|
||||
|
||||
// clear filter TM on client
|
||||
if (world.isClient) {
|
||||
val tmBlockEntity = world.getBlockEntity(pos)
|
||||
|
||||
val inventory = (tmBlockEntity as TMBlockEntity).tmmInventory
|
||||
|
||||
inventory.filterTM = null
|
||||
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
val tmBlockEntity = world.getBlockEntity(pos)
|
||||
|
||||
if (tmBlockEntity is TMBlockEntity) {
|
||||
// Set the block state to ON when the block is used
|
||||
tmBlockEntity.stateManager.openContainer(player, world, pos, blockState)
|
||||
player.openHandledScreen(tmBlockEntity as TMBlockEntity?)
|
||||
}
|
||||
|
||||
val inventory = (tmBlockEntity as TMBlockEntity).tmmInventory // (world.getBlockEntity(pos) as TMBlockEntity).tmmInventory
|
||||
|
||||
// remove all machine from machine upon use
|
||||
// inventory.items?.forEach {
|
||||
// // Get the direction the block is facing
|
||||
// val facingDirection = blockState.get(Properties.FACING).opposite
|
||||
//
|
||||
// // Calculate the center position of the block
|
||||
// val frontOffset = 0.5 // Half block offset to the front
|
||||
// val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
|
||||
// val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
|
||||
// val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
|
||||
//
|
||||
// // Create the ItemEntity at the center of the block
|
||||
// val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, it)
|
||||
//
|
||||
// // Create the ItemEntity
|
||||
// itemEntity.setVelocity(0.0, 0.0, 0.0)
|
||||
//
|
||||
// // Add the ItemEntity to the world
|
||||
// world.spawnEntity(itemEntity)
|
||||
// }
|
||||
//
|
||||
// inventory.items?.clear()
|
||||
|
||||
// spit out any disc filters
|
||||
if (inventory.filterTM != null) {
|
||||
// spit out the filter TM and set filterTM to null
|
||||
|
||||
val filterTMStack = inventory.filterTM!!
|
||||
|
||||
// Get the direction the block is facing
|
||||
val facingDirection = blockState.get(Properties.FACING).opposite
|
||||
|
||||
// Calculate the center position of the block
|
||||
val frontOffset = 0.5 // Half block offset to the front
|
||||
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
|
||||
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
|
||||
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
|
||||
|
||||
// Create the ItemEntity at the center of the block
|
||||
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, filterTMStack).copy()
|
||||
|
||||
// Create the ItemEntity
|
||||
itemEntity.setVelocity(0.0, 0.0, 0.0)
|
||||
|
||||
// Add the ItemEntity to the world
|
||||
world.spawnEntity(itemEntity)
|
||||
|
||||
inventory.filterTM = null
|
||||
}
|
||||
|
||||
val blockEntity = world.getBlockEntity(pos)
|
||||
if (blockEntity is TMBlockEntity) {
|
||||
//player.openHandledScreen(blockEntity as TMBlockEntity?)
|
||||
player.playSound(CobblemonSounds.TMM_ON, SoundCategory.BLOCKS, 1.0f, 1.0f)
|
||||
|
||||
// todo maybe uncomment this
|
||||
//val serverPlayer = player as ServerPlayerEntity
|
||||
//serverPlayer.openHandledScreen(blockState.createScreenHandlerFactory(world, pos))
|
||||
}
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
|
||||
// override fun createScreenHandlerFactory(
|
||||
// state: BlockState?,
|
||||
// world: World?,
|
||||
// pos: BlockPos?
|
||||
// ): NamedScreenHandlerFactory {
|
||||
// return SimpleNamedScreenHandlerFactory(::TMMScreenHandler, Text.of("TM Machine"))
|
||||
// }
|
||||
|
||||
/*fun loadMaterial(itemStack: ItemStack) {
|
||||
loadedMaterials.add(itemStack)
|
||||
}*/
|
||||
|
||||
fun isReadyToCraftTM(state: BlockState?, world: WorldAccess, pos: BlockPos, tm: TechnicalMachine): Boolean {
|
||||
val typeGem = Registries.ITEM.get(tm.let { ElementalTypes.get(it.type)?.typeGem }).defaultStack
|
||||
val recipeItem = Registries.ITEM.get(tm.recipe?.item)
|
||||
|
||||
val inventory = state?.let { getInventory(it, world, pos) }
|
||||
|
||||
return (inventory!!.count(CobblemonItems.BLANK_TM) == 1
|
||||
&& inventory.count(typeGem.item) == 1
|
||||
&& ((inventory.count(recipeItem) == tm.recipe?.count) || (ItemStack.areItemsEqual(recipeItem.defaultStack, ItemStack.EMPTY))))
|
||||
}
|
||||
|
||||
fun isReadyToCraftBlankTM(state: BlockState?, world: WorldAccess, pos: BlockPos): Boolean {
|
||||
val inventory = state?.let { getInventory(it, world, pos) }
|
||||
|
||||
return (inventory!!.count(Items.AMETHYST_SHARD) == 1)
|
||||
}
|
||||
|
||||
/*fun materialNeeded(tm: TechnicalMachine, itemStack: ItemStack): Boolean {
|
||||
val typeGem = Registries.ITEM.get(ElementalTypes.get(tm.type)?.typeGem).defaultStack
|
||||
val recipeItem = Registries.ITEM.get(tm.recipe?.item)
|
||||
|
||||
// if blank TM is needed still
|
||||
if ((itemStack !in loadedMaterials && itemStack == CobblemonItems.BLANK_TM.defaultStack))
|
||||
return true // load in blank TM
|
||||
// if type gem is needed still
|
||||
else if ((itemStack !in loadedMaterials && itemStack == typeGem))
|
||||
return true // load in Type Gem
|
||||
// if the itemStack is listed as needed in the recipe and the count is not currently met
|
||||
else if (itemStack.item == recipeItem && loadedMaterials.count { it == itemStack } < tm.recipe?.count!!)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
}*/
|
||||
|
||||
/*fun clearLoadedMaterials() {
|
||||
loadedMaterials.clear()
|
||||
}*/
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getRenderType(blockState: BlockState): BlockRenderType {
|
||||
return BlockRenderType.MODEL
|
||||
}
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState? {
|
||||
return if (state.get(WATERLOGGED)) {
|
||||
Fluids.WATER.getStill(false)
|
||||
} else super.getFluidState(state)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(
|
||||
state: BlockState,
|
||||
world: BlockView,
|
||||
pos: BlockPos,
|
||||
context: ShapeContext
|
||||
): VoxelShape {
|
||||
return when (state.get(FACING)) {
|
||||
Direction.NORTH -> NORTH_OUTLINE
|
||||
Direction.SOUTH -> SOUTH_OUTLINE
|
||||
Direction.WEST -> WEST_OUTLINE
|
||||
else -> EAST_OUTLINE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.BlockView
|
||||
|
||||
class TypeGemBlock(
|
||||
settings: Settings,
|
||||
stage: Int,
|
||||
height: Int,
|
||||
xzOffset: Int,
|
||||
nextStage: Block?,
|
||||
val baseBlock: Block
|
||||
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
|
||||
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
|
||||
val state = world.getBlockState(pos)
|
||||
val dir = state.get(FACING).opposite
|
||||
return (world.getBlockState(pos.offset(dir)).block == baseBlock)
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.Packet
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.collection.DefaultedList
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.World
|
||||
|
||||
class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DISPLAY_CASE, pos, state), SidedInventory {
|
||||
|
||||
val inv: DefaultedList<ItemStack> = DefaultedList.ofSize(1, ItemStack.EMPTY)
|
||||
|
||||
/**
|
||||
* Updates the [ItemStack] stored in this entity
|
||||
*
|
||||
* @param player The [PlayerEntity] that performed this interaction
|
||||
* @param hand The [Hand] the player used to perform this interaction
|
||||
* @author whatsy
|
||||
*/
|
||||
fun updateItem(player: PlayerEntity, hand: Hand): ActionResult {
|
||||
val playerStack = player.getStackInHand(hand)
|
||||
|
||||
// Player and case item are the same - do nothing
|
||||
if (playerStack.item == getStack().item) {
|
||||
return if (playerStack.item != Items.AIR) ActionResult.SUCCESS else ActionResult.FAIL
|
||||
}
|
||||
|
||||
// Player's hand is empty, case is not empty - give player the item in the case
|
||||
if (playerStack.isEmpty && !getStack().isEmpty) {
|
||||
if (!player.isCreative) player.setStackInHand(hand, getStack())
|
||||
setCaseStack(ItemStack.EMPTY)
|
||||
return ActionResult.success(true)
|
||||
}
|
||||
|
||||
// Case is empty, player's hand is not - put playerStack in the case
|
||||
if (getStack().isEmpty && !playerStack.isEmpty) {
|
||||
setCaseStack(playerStack.copy())
|
||||
if (!player.isCreative) playerStack.decrement(1)
|
||||
return ActionResult.success(true)
|
||||
}
|
||||
|
||||
// Player has item, case has item - swap items
|
||||
if (!getStack().isEmpty && !playerStack.isEmpty) {
|
||||
val oldCaseStack = getStack()
|
||||
setCaseStack(playerStack.copy())
|
||||
if (!player.isCreative) {
|
||||
playerStack.decrement(1)
|
||||
player.giveItemStack(oldCaseStack)
|
||||
}
|
||||
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
fun getStack(): ItemStack {
|
||||
return inv[0]
|
||||
}
|
||||
|
||||
private fun setCaseStack(newStack: ItemStack) {
|
||||
if (world == null) return
|
||||
val oldState = world!!.getBlockState(pos)
|
||||
newStack.count = 1
|
||||
inv[0] = newStack
|
||||
if (newStack.isEmpty) {
|
||||
world!!.playSound(null, pos, CobblemonSounds.DISPLAY_CASE_REMOVE_ITEM, SoundCategory.BLOCKS)
|
||||
} else {
|
||||
world!!.playSound(null, pos, CobblemonSounds.DISPLAY_CASE_ADD_ITEM, SoundCategory.BLOCKS)
|
||||
}
|
||||
world!!.updateListeners(pos, oldState, world!!.getBlockState(pos), Block.NOTIFY_LISTENERS)
|
||||
world!!.updateComparators(pos, world!!.getBlockState(pos).block)
|
||||
}
|
||||
|
||||
override fun writeNbt(nbt: NbtCompound) {
|
||||
super.writeNbt(nbt)
|
||||
Inventories.writeNbt(nbt, inv, true)
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound) {
|
||||
super.readNbt(nbt)
|
||||
inv.clear()
|
||||
Inventories.readNbt(nbt, inv)
|
||||
}
|
||||
|
||||
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
|
||||
return BlockEntityUpdateS2CPacket.create(this)
|
||||
}
|
||||
|
||||
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||
return this.createNbt()
|
||||
}
|
||||
|
||||
private fun onItemUpdated(world: World, oldState: BlockState, newState: BlockState) {
|
||||
markDirty()
|
||||
world.updateListeners(pos, oldState, newState, Block.NOTIFY_LISTENERS)
|
||||
world.updateComparators(pos, world.getBlockState(pos).block)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
inv.clear()
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return inv.size
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return getStack().isEmpty
|
||||
}
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
return getStack()
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
val oldState = cachedState
|
||||
val result = Inventories.splitStack(inv, slot, amount)
|
||||
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
|
||||
return result
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
val oldState = cachedState
|
||||
val result = Inventories.removeStack(inv, slot)
|
||||
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
|
||||
return result
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack) {
|
||||
val oldState = cachedState
|
||||
inv[slot] = stack
|
||||
if (stack.count > stack.maxCount) {
|
||||
stack.count = stack.maxCount
|
||||
}
|
||||
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?) = false
|
||||
|
||||
override fun getAvailableSlots(side: Direction?): IntArray {
|
||||
val result = IntArray(inv.size)
|
||||
for (i in result.indices) {
|
||||
result[i] = i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getMaxCountPerStack() = 1
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if(dir == Direction.DOWN) return false
|
||||
return getStack().isEmpty
|
||||
}
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean = (dir == Direction.DOWN)
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.block.TMBlock
|
||||
import com.cobblemon.mod.common.gui.TMMScreenHandler
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.LockableContainerBlockEntity
|
||||
import net.minecraft.block.entity.ViewerCountManager
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.inventory.Inventory
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.Packet
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.collection.DefaultedList
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.World
|
||||
|
||||
|
||||
class TMBlockEntity(
|
||||
val blockPos: BlockPos,
|
||||
val blockState: BlockState
|
||||
) : LockableContainerBlockEntity(CobblemonBlockEntities.TM_BLOCK, blockPos, blockState) {
|
||||
var tmmInventory = TMBlockInventory(this)
|
||||
var automationDelay: Int = AUTOMATION_DELAY
|
||||
var partialTicks = 0.0f
|
||||
|
||||
val stateManager: ViewerCountManager = object : ViewerCountManager() {
|
||||
override fun onContainerOpen(world: World, pos: BlockPos, state: BlockState) {
|
||||
this@TMBlockEntity.setOpen(state, true)
|
||||
}
|
||||
|
||||
override fun onContainerClose(world: World, pos: BlockPos, state: BlockState) {
|
||||
this@TMBlockEntity.setOpen(state, false)
|
||||
}
|
||||
|
||||
override fun onViewerCountUpdate(world: World, pos: BlockPos, state: BlockState, oldViewerCount: Int, newViewerCount: Int) {}
|
||||
override fun isPlayerViewing(player: PlayerEntity): Boolean {
|
||||
if (player.currentScreenHandler is TMMScreenHandler) {
|
||||
val inventory = (player.currentScreenHandler as TMMScreenHandler).inventory
|
||||
return inventory === this@TMBlockEntity
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
const val AUTOMATION_DELAY = 4
|
||||
const val FILTER_TM_NBT = "FilterTM"
|
||||
}
|
||||
|
||||
fun setOpen(state: BlockState, open: Boolean) {
|
||||
world!!.setBlockState(getPos(), state.with(TMBlock.ON, open), Block.NOTIFY_ALL)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
if (!removed) {
|
||||
stateManager.updateViewerCount(getWorld(), getPos(), cachedState)
|
||||
}
|
||||
}
|
||||
|
||||
//var filterTM: TechnicalMachine? = null
|
||||
//private var inventory: DefaultedList<ItemStack> = DefaultedList.ofSize(size(), ItemStack.EMPTY)
|
||||
|
||||
|
||||
/*override fun size(): Int { return 3 }
|
||||
override fun getContainerName(): Text {
|
||||
return Text.translatable("container.tmblock")
|
||||
}
|
||||
|
||||
override fun getInvStackList(): DefaultedList<ItemStack> {
|
||||
return inventory
|
||||
}
|
||||
|
||||
override fun setInvStackList(list: DefaultedList<ItemStack>?) {
|
||||
if (list != null) {
|
||||
this.inventory = list
|
||||
}
|
||||
}*/
|
||||
|
||||
fun resetAutomationDelay() {
|
||||
automationDelay = AUTOMATION_DELAY
|
||||
}
|
||||
|
||||
|
||||
fun materialNeeded(tm: TechnicalMachine?, slot: Int, itemStack: ItemStack): Boolean {
|
||||
val typeGem = Registries.ITEM.get(tm?.type?.let { ElementalTypes.get(it)?.typeGem }).defaultStack
|
||||
val recipeItem = Registries.ITEM.get(tm?.recipe?.item)
|
||||
//val inventory = tmmInventory.inventory
|
||||
val inventory = tmmInventory.items
|
||||
|
||||
//inventory.count { it.value == CobblemonItems.BLANK_TM.defaultStack }
|
||||
//ItemStack.areEqual()
|
||||
|
||||
|
||||
if (inventory != null) {
|
||||
if (tmmInventory.filterTM != null) {
|
||||
// if blank TM is needed still
|
||||
if (slot == 0 && (inventory.none { it?.count == 1 && it.item == CobblemonItems.BLANK_TM }) && ItemStack.areEqual(itemStack, ItemStack(CobblemonItems.BLANK_TM, 1)))
|
||||
return true // load in blank TM
|
||||
// if type gem is needed still
|
||||
else if (slot == 1 && inventory.none { ItemStack.areEqual(it, typeGem) } && ItemStack.areEqual(itemStack, typeGem))
|
||||
return true // load in Type Gem
|
||||
// if the itemStack is listed as needed in the recipe and the count is not currently met
|
||||
else if (slot == 2 && inventory.none { it?.count == tm?.recipe?.count && ItemStack.areItemsEqual(it, ItemStack(recipeItem, 1)) } && ItemStack.areEqual(itemStack, ItemStack(recipeItem, 1)))
|
||||
return true
|
||||
else
|
||||
return false
|
||||
}
|
||||
else if (tmmInventory.filterTM == null && ItemStack.areItemsEqual(itemStack,Items.AMETHYST_SHARD.defaultStack)) {
|
||||
return (slot == 2 && inventory.none { it?.count == 1 && ItemStack.areItemsEqual(it, Items.AMETHYST_SHARD.defaultStack) })
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||
val res = NbtCompound()
|
||||
writeNbt(res)
|
||||
return res
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
tmmInventory.clear()
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return this.tmmInventory.size()
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return this.tmmInventory.isEmpty()
|
||||
}
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
return this.tmmInventory.getStack(slot)
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
return this.tmmInventory.removeStack(slot, amount)
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
return this.tmmInventory.removeStack(slot)
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack?) {
|
||||
return this.tmmInventory.setStack(slot, stack)
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?): Boolean {
|
||||
return this.tmmInventory.canPlayerUse(player)
|
||||
}
|
||||
|
||||
override fun getContainerName(): Text {
|
||||
return Text.translatable("container.brewing")
|
||||
}
|
||||
|
||||
|
||||
override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory?): ScreenHandler {
|
||||
return TMMScreenHandler(syncId, playerInventory!!, this.tmmInventory, this)
|
||||
}
|
||||
|
||||
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
|
||||
return BlockEntityUpdateS2CPacket.create(this)
|
||||
}
|
||||
|
||||
override fun writeNbt(nbt: NbtCompound) {
|
||||
super.writeNbt(nbt)
|
||||
Inventories.writeNbt(nbt, tmmInventory.items)
|
||||
val filterTmCompound = tmmInventory.filterTM?.writeNbt(NbtCompound())
|
||||
if (filterTmCompound != null)
|
||||
nbt.put(FILTER_TM_NBT, filterTmCompound)
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound) {
|
||||
super.readNbt(nbt)
|
||||
Inventories.readNbt(nbt, tmmInventory.items)
|
||||
if (nbt.contains(FILTER_TM_NBT)) {
|
||||
val itemStack = CobblemonItems.TECHNICAL_MACHINE.defaultStack
|
||||
|
||||
tmmInventory.filterTM = ItemStack.fromNbt(nbt.getCompound(FILTER_TM_NBT))
|
||||
|
||||
val test = 2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
super.markDirty()
|
||||
if (this.world != null && !this.world!!.isClient) {
|
||||
val currentState = world!!.getBlockState(pos)
|
||||
world!!.setBlockState(pos, currentState, 3) // Flags: 2 | 1 = Block update | Render update
|
||||
this.world!!.updateNeighborsAlways(this.pos, this.cachedState.block)
|
||||
}
|
||||
}
|
||||
|
||||
class TMBlockInventory(val tmBlockEntity: TMBlockEntity) : SidedInventory {
|
||||
|
||||
private val BLANK_DISC_SLOT_INDEX = 0
|
||||
private val GEM_SLOT_INDEX = 1
|
||||
private val MISC_SLOT_INDEX = 2
|
||||
private val OUTPUT_SLOT_INDEX = 3
|
||||
private val INPUT_SLOTS = intArrayOf(0, 1, 2, 3)
|
||||
val entityPos = this.tmBlockEntity.blockPos
|
||||
val entityState = this.tmBlockEntity.blockState
|
||||
val entityWorld = this.tmBlockEntity.world
|
||||
|
||||
var filterTM: ItemStack? = null
|
||||
//var inventory: MutableMap<Int, ItemStack?> = mutableMapOf(0 to ItemStack.EMPTY, 1 to ItemStack.EMPTY, 2 to ItemStack.EMPTY)
|
||||
var items: DefaultedList<ItemStack?>? = DefaultedList.ofSize(4, ItemStack.EMPTY)
|
||||
|
||||
fun hasFilterTM(): Boolean {
|
||||
return filterTM != null
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
tmBlockEntity.markDirty()
|
||||
this.items?.clear()
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return this.items?.size ?: 0;
|
||||
}
|
||||
|
||||
/*fun getInvSize(): Int {
|
||||
return size()
|
||||
}*/
|
||||
|
||||
fun getInvStack(slot: Int): ItemStack {
|
||||
return items?.get(slot) ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
for (i in 0 until size()) {
|
||||
val stack: ItemStack = getInvStack(i)
|
||||
if (!stack.isEmpty) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*override fun isEmpty(): Boolean {
|
||||
return !inventory.any { it.value != ItemStack.EMPTY }
|
||||
}*/
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
return items?.get(slot) ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun getAvailableSlots(side: Direction?): IntArray? {
|
||||
return if (side == Direction.DOWN) {
|
||||
return listOf(this.OUTPUT_SLOT_INDEX).toIntArray()
|
||||
}
|
||||
else this.INPUT_SLOTS
|
||||
|
||||
|
||||
// val result = this.items?.size?.let { IntArray(it) }
|
||||
// if (result != null) {
|
||||
// for (i in result.indices) {
|
||||
// result[i] = i
|
||||
// }
|
||||
// }
|
||||
// return result
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
tmBlockEntity.markDirty()
|
||||
return Inventories.splitStack(items, slot, amount)
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
val slotStack = items?.get(slot)
|
||||
items?.set(slot, ItemStack.EMPTY)
|
||||
tmBlockEntity.markDirty()
|
||||
return slotStack ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack?) {
|
||||
items?.set(slot, stack)
|
||||
tmBlockEntity.markDirty()
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
if (tmBlockEntity.world != null)
|
||||
tmBlockEntity.markDirty()
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?): Boolean {
|
||||
return Inventory.canPlayerUse(this.tmBlockEntity, player)
|
||||
}
|
||||
|
||||
/*override fun getAvailableSlots(side: Direction?): IntArray {
|
||||
return IntArray(3)
|
||||
}*/
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if (this.entityState.get(TMBlock.ON)) {
|
||||
// print("TMM is in use so canInsert is disabled")
|
||||
return false
|
||||
}
|
||||
|
||||
if (stack != null && slot < this.INPUT_SLOTS.size) {
|
||||
val tm = TechnicalMachines.getTechnicalMachineFromStack(tmBlockEntity.tmmInventory.filterTM)
|
||||
// if material is needed then load it
|
||||
if (tmBlockEntity.materialNeeded(tm, slot, stack) && (ItemStack.areEqual(items?.get(slot) ?: ItemStack.EMPTY, ItemStack.EMPTY) || ItemStack.areItemsEqual(items?.get(slot) ?: ItemStack.EMPTY, stack))) {
|
||||
//tmBlockEntity.loadMaterial(stack)
|
||||
return true
|
||||
}
|
||||
else
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if (this.entityState.get(TMBlock.ON)) {
|
||||
//print("TMM is in use so canExtract is disabled")
|
||||
return false
|
||||
}
|
||||
|
||||
return dir == Direction.DOWN && slot == this.OUTPUT_SLOT_INDEX
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,6 +62,7 @@ object CobblemonBakingOverrides {
|
||||
cobblemonModel("restoration_tank_connector", "none")
|
||||
)
|
||||
|
||||
|
||||
fun registerOverride(modelLocation: Identifier, modelIdentifier: ModelIdentifier): BakingOverride {
|
||||
val result = BakingOverride(modelLocation, modelIdentifier)
|
||||
models.add(result)
|
||||
|
||||
@@ -18,16 +18,22 @@ import com.cobblemon.mod.common.CobblemonEntities
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.text.gray
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.gui.modstation.ModStationHandledScreen
|
||||
import com.cobblemon.mod.common.client.gui.tm.TMMHandledScreen
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.*
|
||||
import com.cobblemon.mod.common.client.render.block.TMBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
|
||||
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
|
||||
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
|
||||
@@ -44,13 +50,19 @@ import com.cobblemon.mod.common.client.storage.ClientStorageManager
|
||||
import com.cobblemon.mod.common.client.trade.ClientTrade
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.gui.CobblemonScreenHandlers
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.item.TechnicalMachineItem
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import net.minecraft.client.color.block.BlockColorProvider
|
||||
import net.minecraft.client.color.item.ItemColorProvider
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreens
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.block.entity.HangingSignBlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.SignBlockEntityRenderer
|
||||
@@ -104,9 +116,10 @@ object CobblemonClient {
|
||||
|
||||
this.registerBlockEntityRenderers()
|
||||
registerBlockRenderTypes()
|
||||
//registerColors()
|
||||
registerColors()
|
||||
registerFlywheelRenderers()
|
||||
this.registerEntityRenderers()
|
||||
registerHandledScreens()
|
||||
Berries.observable.subscribe {
|
||||
BerryModelRepository.patchModels()
|
||||
}
|
||||
@@ -117,7 +130,6 @@ object CobblemonClient {
|
||||
PlatformEvents.CLIENT_ITEM_TOOLTIP.subscribe { event ->
|
||||
val stack = event.stack
|
||||
val lines = event.lines
|
||||
val size = lines.size
|
||||
@Suppress("DEPRECATION")
|
||||
if (stack.item.registryEntry.key.isPresent && stack.item.registryEntry.key.get().value.namespace == Cobblemon.MODID) {
|
||||
if (stack.nbt?.getBoolean(DataKeys.HIDE_TOOLTIP) == true) {
|
||||
@@ -125,14 +137,14 @@ object CobblemonClient {
|
||||
}
|
||||
val language = Language.getInstance()
|
||||
val key = this.baseLangKeyForItem(stack)
|
||||
val offset = if (size > 1) 1 else 0
|
||||
val offset = if (lines.size > 1) 1 else 0
|
||||
if (language.hasTranslation(key)) {
|
||||
lines.add(size - offset, key.asTranslated().gray())
|
||||
lines.add(lines.size - offset, key.asTranslated().gray())
|
||||
}
|
||||
var i = 1
|
||||
var listKey = "${key}_$i"
|
||||
while(language.hasTranslation(listKey)) {
|
||||
lines.add(size - offset, listKey.asTranslated().gray())
|
||||
lines.add(lines.size - offset, listKey.asTranslated().gray())
|
||||
listKey = "${key}_${++i}"
|
||||
}
|
||||
}
|
||||
@@ -147,21 +159,24 @@ object CobblemonClient {
|
||||
// .apply()
|
||||
}
|
||||
|
||||
/*
|
||||
fun registerColors() {
|
||||
this.implementation.registerBlockColors(BlockColorProvider { _, _, _, _ ->
|
||||
return@BlockColorProvider 0xE0A33A
|
||||
}, CobblemonBlocks.APRICORN_LEAVES)
|
||||
this.implementation.registerItemColors(ItemColorProvider { _, _ ->
|
||||
|
||||
this.implementation.registerItemColors(ItemColorProvider { stack, tint ->
|
||||
return@ItemColorProvider 0xE0A33A
|
||||
}, CobblemonItems.APRICORN_LEAVES)
|
||||
}, CobblemonItems.TECHNICAL_MACHINE)
|
||||
|
||||
this.implementation.registerItemColors(TechnicalMachineItem::getItemColor, CobblemonItems.TECHNICAL_MACHINE)
|
||||
}
|
||||
*/
|
||||
|
||||
private fun registerBlockRenderTypes() {
|
||||
|
||||
this.implementation.registerBlockRenderType(RenderLayer.getCutoutMipped(), CobblemonBlocks.APRICORN_LEAVES)
|
||||
|
||||
this.implementation.registerBlockRenderType(RenderLayer.getTranslucent(), CobblemonBlocks.TM_MACHINE)
|
||||
|
||||
this.implementation.registerBlockRenderType(
|
||||
RenderLayer.getCutout(),
|
||||
CobblemonBlocks.GILDED_CHEST,
|
||||
@@ -218,7 +233,8 @@ object CobblemonClient {
|
||||
CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST,
|
||||
CobblemonBlocks.DISPLAY_CASE
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
@@ -248,7 +264,9 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HANGING_SIGN, ::HangingSignBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_ANALYZER, ::FossilAnalyzerRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.TM_BLOCK, ::TMBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -260,6 +278,8 @@ object CobblemonClient {
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
|
||||
LOGGER.info("Registering Boat with Chest renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
|
||||
LOGGER.info("Registering Generic Bedrock Entity renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
|
||||
}
|
||||
|
||||
fun reloadCodedAssets(resourceManager: ResourceManager) {
|
||||
@@ -267,13 +287,14 @@ object CobblemonClient {
|
||||
BedrockParticleEffectRepository.loadEffects(resourceManager)
|
||||
BedrockAnimationRepository.loadAnimations(
|
||||
resourceManager = resourceManager,
|
||||
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories
|
||||
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories + GenericBedrockEntityModelRepository.animationDirectories
|
||||
)
|
||||
PokemonModelRepository.reload(resourceManager)
|
||||
PokeBallModelRepository.reload(resourceManager)
|
||||
BerryModelRepository.reload(resourceManager)
|
||||
FossilModelRepository.reload(resourceManager)
|
||||
BlockEntityModelRepository.reload(resourceManager)
|
||||
GenericBedrockEntityModelRepository.reload(resourceManager)
|
||||
MiscModelRepository.reload(resourceManager)
|
||||
LOGGER.info("Loaded assets")
|
||||
}
|
||||
@@ -299,4 +320,15 @@ object CobblemonClient {
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerHandledScreens() {
|
||||
HandledScreens.register(
|
||||
CobblemonScreenHandlers.TMM_SCREEN,
|
||||
::TMMHandledScreen
|
||||
)
|
||||
HandledScreens.register(
|
||||
CobblemonScreenHandlers.MOD_STATION_SCREEN,
|
||||
::ModStationHandledScreen
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
|
||||
class TypeReturnIcon(
|
||||
val x: Number,
|
||||
val y: Number,
|
||||
val centeredX: Boolean = false,
|
||||
val small: Boolean = false,
|
||||
val secondaryOffset: Float = 15F,
|
||||
val doubleCenteredOffset: Float = 7.5F,
|
||||
val opacity: Float = 1F
|
||||
) {
|
||||
companion object {
|
||||
private const val TYPE_ICON_DIAMETER = 36
|
||||
private const val SCALE = 0.5F
|
||||
|
||||
private val typeReturnResource = cobblemonResource("textures/gui/type_return.png")
|
||||
private val smallTypeReturnResource = cobblemonResource("textures/gui/type_return_small.png")
|
||||
}
|
||||
|
||||
fun render(context: DrawContext) {
|
||||
val diameter = if (small) (TYPE_ICON_DIAMETER / 2) else TYPE_ICON_DIAMETER
|
||||
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = if (small) smallTypeReturnResource else typeReturnResource,
|
||||
x = (x.toFloat()) / SCALE - 9,
|
||||
y = y.toFloat() / SCALE,
|
||||
height = diameter,
|
||||
width = diameter,
|
||||
alpha = opacity,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
@@ -21,7 +23,6 @@ import net.minecraft.client.gui.Selectable
|
||||
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.util.Identifier
|
||||
class BattleGeneralActionSelection(
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.cobblemon.mod.common.client.gui.modstation
|
||||
|
||||
import com.cobblemon.mod.common.gui.ModStationScreenHandler
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.ScreenHandlerListener
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class ModStationHandledScreen(
|
||||
val handler: ModStationScreenHandler,
|
||||
val inventory: PlayerInventory,
|
||||
title: Text?
|
||||
) : HandledScreen<ModStationScreenHandler>(handler, inventory, title), ScreenHandlerListener {
|
||||
override fun drawBackground(context: DrawContext?, delta: Float, mouseX: Int, mouseY: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onSlotUpdate(handler: ScreenHandler?, slotId: Int, stack: ItemStack?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPropertyUpdate(handler: ScreenHandler?, property: Int, value: Int) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class DiscButton(
|
||||
pX: Int, pY: Int,
|
||||
onPress: PressAction,
|
||||
): ButtonWidget(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Text.literal("All Types"), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 53F
|
||||
private const val HEIGHT = 53F
|
||||
val DISC_BUTTON = cobblemonResource("textures/gui/tm/disc_button.png")
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = DISC_BUTTON,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {}
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class EjectButton(
|
||||
pX: Int, pY: Int,
|
||||
val small: Boolean = false,
|
||||
onPress: PressAction
|
||||
): ButtonWidget(
|
||||
pX,
|
||||
pY,
|
||||
if (small) SMALL_WIDTH.toInt() else LARGE_WIDTH.toInt(),
|
||||
if (small) SMALL_HEIGHT.toInt() else LARGE_HEIGHT.toInt(),
|
||||
Text.literal("Eject"),
|
||||
onPress,
|
||||
DEFAULT_NARRATION_SUPPLIER
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private const val LARGE_WIDTH = 54F
|
||||
private const val LARGE_HEIGHT = 14F
|
||||
private const val SMALL_WIDTH = 14F
|
||||
private const val SMALL_HEIGHT = 14F
|
||||
val SMALL_BUTTON = cobblemonResource("textures/gui/tm/eject_button_small.png")
|
||||
val LARGE_BUTTON = cobblemonResource("textures/gui/tm/eject_button_large.png")
|
||||
}
|
||||
|
||||
val height = if (small) SMALL_HEIGHT else LARGE_HEIGHT
|
||||
val width = if (small) SMALL_WIDTH else LARGE_WIDTH
|
||||
val texture = if (small) SMALL_BUTTON else LARGE_BUTTON
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = texture,
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) height else 0,
|
||||
textureHeight = height * 2
|
||||
)
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {}
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + width)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + height))
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class TMListingButton(
|
||||
pX: Int, pY: Int,
|
||||
onPress: PressAction,
|
||||
val tm: TechnicalMachine
|
||||
): ButtonWidget(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Text.literal("All Types"), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
|
||||
companion object {
|
||||
private const val HEIGHT = 20F
|
||||
private const val WIDTH = 140F
|
||||
val TM_LISTING_BUTTON = cobblemonResource("textures/gui/tm/tm_selection_listing.png")
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = TM_LISTING_BUTTON,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
|
||||
val item = CobblemonItems.TECHNICAL_MACHINE
|
||||
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = item.setNbt(item.defaultStack, tm.id().toString()),
|
||||
x = x.toDouble() + 2,
|
||||
y = y.toDouble() + 2
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = tm.translatedMoveName(),
|
||||
x = x + 25,
|
||||
y = y + 7.5,
|
||||
scale = 0.75f
|
||||
)
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {}
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,611 @@
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToServer
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.ColourLibrary
|
||||
import com.cobblemon.mod.common.api.gui.MultiLineLabelK
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.reactive.SettableObservable
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachineRecipe
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.block.TMBlock
|
||||
import com.cobblemon.mod.common.block.entity.TMBlockEntity
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
||||
import com.cobblemon.mod.common.gui.TMMScreenHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.PokemonState
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.Drawable
|
||||
import net.minecraft.client.gui.Element
|
||||
import net.minecraft.client.gui.Selectable
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.ScreenHandlerListener
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class TMMHandledScreen(
|
||||
val handler: TMMScreenHandler,
|
||||
val inventory: PlayerInventory,
|
||||
title: Text?
|
||||
) : HandledScreen<TMMScreenHandler>(handler, inventory, title), ScreenHandlerListener {
|
||||
|
||||
companion object {
|
||||
val TEXTURE_HEIGHT = 222
|
||||
val TEXTURE_WIDTH = 254
|
||||
|
||||
val TYPE_MENU_MODE = 1
|
||||
val TM_BROWSING_MODE = 2
|
||||
|
||||
val TYPE_SELECTION_BASE = cobblemonResource("textures/gui/tm/type_selection_base.png")
|
||||
val TM_SELECTION_BASE = cobblemonResource("textures/gui/tm/tm_selection_base.png")
|
||||
val TM_SELECTION_BORDER = cobblemonResource("textures/gui/tm/tm_selection_border.png")
|
||||
val TM_SELECTION_LISTING = cobblemonResource("textures/gui/tm/tm_selection_listing.png")
|
||||
val EJECT_BUTTON_SMALL = cobblemonResource("textures/gui/tm/eject_button_small.png")
|
||||
val EJECT_BUTTON_LARGE = cobblemonResource("textures/gui/tm/eject_button_large.png")
|
||||
val PARTY_SLOT = cobblemonResource("textures/gui/tm/party_slot.png")
|
||||
val TEACH_BUTTON = cobblemonResource("textures/gui/tm/teach_button.png")
|
||||
}
|
||||
|
||||
// val selectedTM: TechnicalMachine? = TechnicalMachines.tmMap[cobblemonResource("thunderbolt")]!!
|
||||
var selectedTM: TechnicalMachine? = null
|
||||
var tmList: SettableObservable<MutableList<TechnicalMachine>> = SettableObservable(mutableListOf())
|
||||
var output: ItemStack? = null
|
||||
val children: MutableMap<String, Element> = mutableMapOf()
|
||||
var mode: Int = TYPE_MENU_MODE
|
||||
var sortType: ElementalType? = null
|
||||
var selectedPokemon: Pokemon? = null
|
||||
var scroll: TMScrollingList? = null
|
||||
|
||||
init {
|
||||
scroll = TMScrollingList(
|
||||
x = x + 132,
|
||||
y = y + 45,
|
||||
parent = this
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// val selectedTM = TechnicalMachine("stompingtantrum", null, emptyList(), "ground", null, null)
|
||||
|
||||
/**
|
||||
* Used instead of `addDrawableChild()` to avoid re-drawing children.
|
||||
*/
|
||||
private fun <T> addChild(drawableElement: T, identifier: String) where T : Element, T : Drawable, T : Selectable {
|
||||
if (children.contains(identifier)) return
|
||||
addDrawableChild(drawableElement)
|
||||
children[identifier] = drawableElement
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (scroll?.isHovered(mouseX, mouseY) == true) {
|
||||
scroll!!.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
|
||||
if (scroll?.isHovered(mouseX, mouseY) == true) {
|
||||
scroll!!.mouseScrolled(mouseX, mouseY, amount)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (mode != TYPE_MENU_MODE) scroll?.mouseDragged(
|
||||
mouseX,
|
||||
mouseY,
|
||||
button,
|
||||
deltaX,
|
||||
deltaY
|
||||
)
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
fun drawBlankCraftingRecipe(context: DrawContext) {
|
||||
val x = (width - TEXTURE_WIDTH) / 2
|
||||
val y = (height - TEXTURE_HEIGHT) / 2
|
||||
// ingredient
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = Items.AMETHYST_SHARD.defaultStack,
|
||||
x = (x + 197 + 36).toDouble(),
|
||||
y = (y + 127).toDouble(),
|
||||
zTranslation = 1f
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.tms.material_cost"),
|
||||
x = (x + 223),
|
||||
y = (y + 117.5),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.translatable(CobblemonItems.BLANK_TM.translationKey).bold(),
|
||||
x = (x + 223),
|
||||
y = (y + 12),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.power"),
|
||||
x = (x + 199),
|
||||
y = (y + 25),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.literal("-"),
|
||||
x = (x + 241),
|
||||
y = (y + 25),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.accuracy_short"),
|
||||
x = (x + 199),
|
||||
y = (y + 35),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.literal("-"),
|
||||
x = (x + 241),
|
||||
y = (y + 35),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.pp"),
|
||||
x = (x + 199),
|
||||
y = (y + 45),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.literal("-"),
|
||||
x = (x + 241),
|
||||
y = (y + 45),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.summary.title"),
|
||||
x = (x + 199),
|
||||
y = (y + 54.5),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
val scale = 0.5f
|
||||
|
||||
context.matrices.push()
|
||||
context.matrices.scale(scale, scale, 1f)
|
||||
MultiLineLabelK.create(
|
||||
component = lang("ui.tms.blank_tm_desc"),
|
||||
width = 49 / scale,
|
||||
maxLines = 7
|
||||
).renderLeftAligned(
|
||||
context = context,
|
||||
x = (x + 199) / scale,
|
||||
y = (y + 62) / scale,
|
||||
ySpacing = 5.5 / scale,
|
||||
colour = ColourLibrary.WHITE,
|
||||
shadow = true
|
||||
)
|
||||
context.matrices.pop()
|
||||
}
|
||||
|
||||
fun drawMoveInfo(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
|
||||
val currentTm = selectedTM
|
||||
if (currentTm == null) {
|
||||
return drawBlankCraftingRecipe(context)
|
||||
}
|
||||
|
||||
val x = (width - TEXTURE_WIDTH) / 2
|
||||
val y = (height - TEXTURE_HEIGHT) / 2
|
||||
val move = Moves.getByNameOrDummy(currentTm.moveName)
|
||||
// blank tm
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = CobblemonItems.BLANK_TM.defaultStack,
|
||||
x = (x + 197).toDouble(),
|
||||
y = (y + 127).toDouble(),
|
||||
)
|
||||
|
||||
val typeGem = Registries.ITEM.get(ElementalTypes.get(currentTm.type)?.typeGem).defaultStack
|
||||
// type gem
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = typeGem ?: ItemStack.EMPTY,
|
||||
x = (x + 197 + 18).toDouble(),
|
||||
y = (y + 127).toDouble(),
|
||||
)
|
||||
|
||||
val recipe = currentTm.recipe ?: TechnicalMachineRecipe(Identifier.of("minecraft", "air")!!, 1)
|
||||
// ingredient
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = Registries.ITEM.get(recipe.item).defaultStack,
|
||||
x = (x + 197 + 36).toDouble(),
|
||||
y = (y + 127).toDouble(),
|
||||
zTranslation = 1f
|
||||
)
|
||||
|
||||
context.matrices.push()
|
||||
context.matrices.translate(0f, 0f, 2f)
|
||||
|
||||
if (recipe.count > 1) {
|
||||
val xIncrease = if (recipe.count < 10) 47 else 41
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.literal(recipe.count.toString()),
|
||||
x = (x + 197 + xIncrease).toDouble(),
|
||||
y = (y + 136).toDouble(),
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
context.matrices.pop()
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.tms.material_cost"),
|
||||
x = (x + 223),
|
||||
y = (y + 117.5),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = currentTm.translatedMoveName().bold(),
|
||||
x = (x + 223),
|
||||
y = (y + 12),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
MoveCategoryIcon(
|
||||
x = (x + 237.5),
|
||||
y = (y + 52.5),
|
||||
category = Moves.getByNameOrDummy(currentTm.moveName).damageCategory
|
||||
).render(context)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.power"),
|
||||
x = (x + 199),
|
||||
y = (y + 25),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.literal(move.power.toInt().toString()),
|
||||
x = (x + 241),
|
||||
y = (y + 25),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.accuracy_short"),
|
||||
x = (x + 199),
|
||||
y = (y + 35),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.literal(move.accuracy.toInt().toString() + "%"),
|
||||
x = (x + 241),
|
||||
y = (y + 35),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.pp"),
|
||||
x = (x + 199),
|
||||
y = (y + 45),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = Text.literal(move.pp.toString()),
|
||||
x = (x + 241),
|
||||
y = (y + 45),
|
||||
scale = 0.5f,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.summary.title"),
|
||||
x = (x + 199),
|
||||
y = (y + 54.5),
|
||||
scale = 0.5f
|
||||
)
|
||||
|
||||
val scale = 0.5f
|
||||
|
||||
context.matrices.push()
|
||||
context.matrices.scale(scale, scale, 1f)
|
||||
MultiLineLabelK.create(
|
||||
component = lang("move.${currentTm.moveName}.desc"),
|
||||
width = 49 / scale,
|
||||
maxLines = 7
|
||||
).renderLeftAligned(
|
||||
context = context,
|
||||
x = (x + 199) / scale,
|
||||
y = (y + 62) / scale,
|
||||
ySpacing = 5.5 / scale,
|
||||
colour = ColourLibrary.WHITE,
|
||||
shadow = true
|
||||
)
|
||||
context.matrices.pop()
|
||||
}
|
||||
|
||||
fun drawTypeMenu(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
|
||||
val x = (width - TEXTURE_WIDTH) / 2
|
||||
val y = (height - TEXTURE_HEIGHT) / 2
|
||||
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = TYPE_SELECTION_BASE,
|
||||
x = x, // horizontal placement of GUI
|
||||
y = y, // vertical placement of GUI
|
||||
|
||||
width = TEXTURE_WIDTH, // scale of the GUI width
|
||||
height = TEXTURE_HEIGHT // scale of the GUI height
|
||||
)
|
||||
|
||||
fun drawTypeRow(drawX: Int, drawY: Int, types: List<ElementalType>) {
|
||||
var xOffset = 0
|
||||
for (type in types) {
|
||||
addChild(TypeButton(
|
||||
pX = drawX + 32 + xOffset,
|
||||
pY = drawY + 16,
|
||||
type = type,
|
||||
onPress = {
|
||||
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
|
||||
tmList.set(TechnicalMachine.filterTms(null, type, null).toMutableList())
|
||||
sortType = type
|
||||
mode = TM_BROWSING_MODE
|
||||
clearGUI()
|
||||
scroll?.scrollAmount = 0.0
|
||||
}
|
||||
), "${type.name}type")
|
||||
xOffset += 24
|
||||
}
|
||||
}
|
||||
|
||||
drawTypeRow(x, y, listOf(
|
||||
ElementalTypes.NORMAL,
|
||||
ElementalTypes.FIRE,
|
||||
ElementalTypes.WATER,
|
||||
ElementalTypes.GRASS,
|
||||
ElementalTypes.ELECTRIC,
|
||||
ElementalTypes.ICE
|
||||
))
|
||||
|
||||
drawTypeRow(x + 12, y + 24, listOf(
|
||||
ElementalTypes.FIGHTING,
|
||||
ElementalTypes.POISON,
|
||||
ElementalTypes.GROUND,
|
||||
ElementalTypes.FLYING,
|
||||
))
|
||||
|
||||
drawTypeRow(x, y + 48, listOf(
|
||||
ElementalTypes.PSYCHIC,
|
||||
ElementalTypes.BUG,
|
||||
ElementalTypes.ROCK,
|
||||
ElementalTypes.GHOST,
|
||||
))
|
||||
|
||||
drawTypeRow(x + 12, y + 72, listOf(
|
||||
ElementalTypes.DRAGON,
|
||||
ElementalTypes.DARK,
|
||||
ElementalTypes.STEEL,
|
||||
ElementalTypes.FAIRY,
|
||||
))
|
||||
|
||||
addChild(
|
||||
DiscButton(
|
||||
pX = x + 134,
|
||||
pY = y + 50,
|
||||
onPress = {
|
||||
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
|
||||
tmList.set(TechnicalMachine.filterTms(null, null, null).toMutableList())
|
||||
mode = TM_BROWSING_MODE
|
||||
sortType = null
|
||||
scroll?.scrollAmount = 0.0
|
||||
clearGUI()
|
||||
}
|
||||
), "disc"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun drawTmSelectMenu(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
|
||||
val x = (width - TEXTURE_WIDTH) / 2
|
||||
val y = (height - TEXTURE_HEIGHT) / 2
|
||||
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = TM_SELECTION_BASE,
|
||||
x = x, // horizontal placement of GUI
|
||||
y = y, // vertical placement of GUI
|
||||
|
||||
width = TEXTURE_WIDTH, // scale of the GUI width
|
||||
height = TEXTURE_HEIGHT // scale of the GUI height
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = TM_SELECTION_BORDER,
|
||||
x = x + 31, // horizontal placement of GUI
|
||||
y = y + 14, // vertical placement of GUI
|
||||
width = 157,
|
||||
height = 101
|
||||
)
|
||||
|
||||
val displayType = sortType
|
||||
|
||||
addChild(
|
||||
TypeButton(
|
||||
pX = x + 31,
|
||||
pY = y + 16,
|
||||
type = displayType,
|
||||
onPress = {
|
||||
mode = TYPE_MENU_MODE
|
||||
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
|
||||
clearGUI()
|
||||
}
|
||||
), "returnTypeMenu"
|
||||
)
|
||||
|
||||
scroll?.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
|
||||
fun clearGUI() {
|
||||
val iterator = children.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val (id, _) = iterator.next()
|
||||
iterator.remove()
|
||||
children.remove(id)
|
||||
}
|
||||
clearChildren()
|
||||
}
|
||||
|
||||
override fun drawBackground(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
|
||||
super.renderBackground(context)
|
||||
val x = (width - TEXTURE_WIDTH) / 2
|
||||
val y = (height - TEXTURE_HEIGHT) / 2
|
||||
|
||||
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = TYPE_SELECTION_BASE,
|
||||
x = x, // horizontal placement of GUI
|
||||
y = y, // vertical placement of GUI
|
||||
|
||||
width = TEXTURE_WIDTH, // scale of the GUI width
|
||||
height = TEXTURE_HEIGHT // scale of the GUI height
|
||||
)
|
||||
|
||||
when (mode) {
|
||||
TYPE_MENU_MODE -> drawTypeMenu(context, delta, mouseX, mouseY)
|
||||
else -> drawTmSelectMenu(context, delta, mouseX, mouseY)
|
||||
}
|
||||
drawMoveInfo(context, delta, mouseX, mouseY)
|
||||
inventory.markDirty()
|
||||
|
||||
addChild(
|
||||
EjectButton(
|
||||
pX = x + 196,
|
||||
pY = y + 168,
|
||||
small = false,
|
||||
onPress = {
|
||||
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
|
||||
val currentTm = selectedTM ?: return@EjectButton sendPacketToServer(CraftBlankTMPacket(handler.input.getStack(2)))
|
||||
|
||||
sendPacketToServer(
|
||||
CraftTMPacket(
|
||||
currentTm,
|
||||
handler.input.getStack(0),
|
||||
handler.input.getStack(1),
|
||||
handler.input.getStack(2)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
this.handler.syncState()
|
||||
}
|
||||
), "eject"
|
||||
)
|
||||
|
||||
addChild(
|
||||
ExitButton(
|
||||
pX = x + 228,
|
||||
pY = y + 185,
|
||||
onPress = {
|
||||
this.close()
|
||||
}
|
||||
), "exit"
|
||||
)
|
||||
|
||||
val startY = y + 13
|
||||
val offset = 28
|
||||
var iterations = 0
|
||||
CobblemonClient.storage.myParty.forEach { pokemon ->
|
||||
if (pokemon != null) {
|
||||
addChild(TMPartySlotWidget(
|
||||
pX = x - 23,
|
||||
pY = startY + (offset * iterations),
|
||||
pokemon = pokemon,
|
||||
onPress = {
|
||||
selectedPokemon = if (selectedPokemon == pokemon) null else pokemon
|
||||
}
|
||||
), "partyslot_${pokemon.uuid}")
|
||||
}
|
||||
iterations++
|
||||
}
|
||||
}
|
||||
|
||||
override fun drawForeground(context: DrawContext?, mouseX: Int, mouseY: Int) {
|
||||
//Text is usually drawn here, we dont want that
|
||||
this.drawMouseoverTooltip(context, mouseX - 158, mouseY - 28)
|
||||
//this.renderWithTooltip(context, mouseX, mouseY, 1F)
|
||||
}
|
||||
|
||||
override fun onSlotUpdate(handler: ScreenHandler?, slotId: Int, stack: ItemStack?) {
|
||||
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
super.close()
|
||||
val inventory = handler.inventory
|
||||
|
||||
if (inventory is TMBlockEntity.TMBlockInventory) {
|
||||
inventory.tmBlockEntity.blockState.with(TMBlock.ON,false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPropertyUpdate(handler: ScreenHandler?, property: Int, value: Int) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.gui.TMMScreenHandler
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.Inventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.screen.slot.Slot
|
||||
|
||||
class TMMOutputSlot(val screen: TMMScreenHandler, index: Int, x: Int, y: Int) : Slot(screen.playerInventory, index, x, y) {
|
||||
|
||||
override fun canInsert(stack: ItemStack?) = false
|
||||
|
||||
override fun onTakeItem(player: PlayerEntity, stack: ItemStack) {
|
||||
player.giveOrDropItemStack(stack, false)
|
||||
this.stack = Items.AIR.defaultStack
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.text.Text
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class TMPartySlotWidget(
|
||||
pX: Number,
|
||||
pY: Number,
|
||||
val pokemon: Pokemon?,
|
||||
onPress: PressAction
|
||||
) : ButtonWidget(pX.toInt(), pY.toInt(), WIDTH, HEIGHT, Text.literal("PartyMember"), onPress, NarrationSupplier { "".text() }) {
|
||||
|
||||
companion object {
|
||||
const val WIDTH = 46
|
||||
const val HEIGHT = 27
|
||||
private const val PORTRAIT_DIAMETER = 25
|
||||
|
||||
private val slotResource = cobblemonResource("textures/gui/tm/party_slot.png")
|
||||
val genderIconMale = cobblemonResource("textures/gui/party/party_gender_male.png")
|
||||
val genderIconFemale = cobblemonResource("textures/gui/party/party_gender_female.png")
|
||||
}
|
||||
private fun getSlotVOffset(pokemon: Pokemon?, isHovered: Boolean, isSelected: Boolean): Int {
|
||||
if (isHovered || isSelected) {
|
||||
if (pokemon == null) {
|
||||
return 0
|
||||
}
|
||||
return height
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
hovered = mouseX >= x && mouseY >= y && mouseX < x + width && mouseY < y + height;
|
||||
val matrices = context.matrices
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = slotResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
vOffset = getSlotVOffset(pokemon, isHovered, isSelected),
|
||||
textureHeight = height * 2,
|
||||
)
|
||||
|
||||
if (pokemon != null) {
|
||||
val halfScale = 0.5F
|
||||
|
||||
// Render Pokémon
|
||||
matrices.push()
|
||||
matrices.translate(x + (PORTRAIT_DIAMETER / 2.0) + 10, y - 1.0, 0.0)
|
||||
matrices.scale(2.5F, 2.5F, 1F)
|
||||
drawProfilePokemon(
|
||||
species = pokemon.species.resourceIdentifier,
|
||||
aspects = pokemon.aspects.toSet(),
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = null,
|
||||
scale = 4.5F,
|
||||
partialTicks = delta
|
||||
)
|
||||
matrices.pop()
|
||||
|
||||
// Draw Name
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = pokemon.getDisplayName(),
|
||||
x = x + 16,
|
||||
y = y + 21.5,
|
||||
scale = halfScale
|
||||
)
|
||||
|
||||
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
|
||||
val ballHeight = 22
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = ballIcon,
|
||||
x = (x - 2) / halfScale,
|
||||
y = (y - 3) / halfScale,
|
||||
height = ballHeight,
|
||||
width = 18,
|
||||
textureHeight = ballHeight * 2,
|
||||
scale = halfScale
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class TMScrollingList(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val parent: TMMHandledScreen
|
||||
) : AlwaysSelectedEntryListWidget<TMScrollingList.TMScrollingListEntry> (
|
||||
MinecraftClient.getInstance(),
|
||||
WIDTH, // width
|
||||
HEIGHT, // height
|
||||
0, // top
|
||||
HEIGHT, // bottom
|
||||
SLOT_HEIGHT// + SLOT_SPACING
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val WIDTH = 140
|
||||
const val HEIGHT = 78
|
||||
const val SLOT_WIDTH = 140
|
||||
const val SLOT_HEIGHT = 20
|
||||
const val SLOT_SPACING = 3
|
||||
const val SCALE = 0.5f
|
||||
}
|
||||
|
||||
private var scrolling = false
|
||||
|
||||
override fun getRowWidth() = SLOT_WIDTH
|
||||
|
||||
init {
|
||||
correctSize()
|
||||
setRenderHorizontalShadows(false)
|
||||
setRenderBackground(false)
|
||||
setRenderSelection(false)
|
||||
|
||||
parent.tmList.subscribeIncludingCurrent { tmList ->
|
||||
val currentEntries = children()
|
||||
val newTMs = tmList.filter { tm -> currentEntries.none { it.tm.id() == tm.id() } }
|
||||
val removedTMs = currentEntries.filter { entry -> tmList.none { it.id() == entry.tm.id() } }
|
||||
|
||||
removedTMs.forEach(this::removeEntry)
|
||||
newTMs.forEach { tm -> addEntry(TMScrollingListEntry(tm, parent)) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getScrollbarPositionX() = left + width - 3
|
||||
|
||||
private fun correctSize() {
|
||||
updateSize(
|
||||
WIDTH,
|
||||
HEIGHT, y - 2, (y + 2) + (HEIGHT - 2))
|
||||
setLeftPos(x)
|
||||
}
|
||||
|
||||
public override fun addEntry(entry: TMScrollingListEntry) = super.addEntry(entry)
|
||||
public override fun removeEntry(entry: TMScrollingListEntry) = super.removeEntry(entry)
|
||||
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
correctSize()
|
||||
|
||||
context.enableScissor(
|
||||
left,
|
||||
top + 5,
|
||||
left + width,
|
||||
top + 1 + height
|
||||
)
|
||||
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
context.disableScissor()
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double): Boolean {
|
||||
println(mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT)))
|
||||
return mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
updateScrollingState(mouseX, mouseY)
|
||||
if (scrolling) {
|
||||
focused = getEntryAtPosition(mouseX, mouseY)
|
||||
isDragging = true
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (scrolling) {
|
||||
if (mouseY < top) {
|
||||
scrollAmount = 0.0
|
||||
} else if (mouseY > bottom) {
|
||||
scrollAmount = maxScroll.toDouble()
|
||||
} else {
|
||||
scrollAmount += deltaY
|
||||
}
|
||||
}
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
private fun updateScrollingState(mouseX: Double, mouseY: Double) {
|
||||
scrolling = mouseX >= this.scrollbarPositionX.toDouble()
|
||||
&& mouseX < (this.scrollbarPositionX + 3).toDouble()
|
||||
&& mouseY >= top
|
||||
&& mouseY < bottom
|
||||
}
|
||||
|
||||
class TMScrollingListEntry(val tm: TechnicalMachine, private val parent: TMMHandledScreen) : Entry<TMScrollingListEntry>() {
|
||||
|
||||
private val tmButton: TMListingButton = TMListingButton(
|
||||
pX = 0,
|
||||
pY = 0,
|
||||
onPress = {
|
||||
parent.selectedTM = tm
|
||||
parent.inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
|
||||
},
|
||||
tm = tm
|
||||
)
|
||||
override fun render(
|
||||
context: DrawContext,
|
||||
index: Int,
|
||||
rowTop: Int,
|
||||
rowLeft: Int,
|
||||
rowWidth: Int,
|
||||
rowHeight: Int,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
isHovered: Boolean,
|
||||
tickDelta: Float
|
||||
) {
|
||||
val x = rowLeft
|
||||
val y = rowTop
|
||||
val matrixStack = context.matrices
|
||||
|
||||
// Render just this TMListingButton
|
||||
tmButton.setPosition(x + 1, y)
|
||||
tmButton.render(context, mouseX, mouseY, tickDelta)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, delta: Int): Boolean {
|
||||
if (tmButton.isHovered(mouseX, mouseY)) {
|
||||
tmButton.onPress()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getNarration(): Text {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.cobblemon.mod.common.client.gui.tm
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.TypeReturnIcon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
|
||||
class TypeButton(
|
||||
pX: Int, pY: Int,
|
||||
onPress: PressAction,
|
||||
val type: ElementalType?
|
||||
): ButtonWidget(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), type?.displayName, onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 24F
|
||||
private const val HEIGHT = 24F
|
||||
val TYPE_BUTTON = cobblemonResource("textures/gui/tm/type_button.png")
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = TYPE_BUTTON,
|
||||
x = x,
|
||||
y = y,
|
||||
width = WIDTH,
|
||||
height = HEIGHT,
|
||||
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
|
||||
textureHeight = HEIGHT * 2
|
||||
)
|
||||
|
||||
context.matrices.push()
|
||||
context.matrices.scale(2f, 2f, 2f)
|
||||
|
||||
if (type != null) {
|
||||
TypeIcon(
|
||||
x = (x + 12.125) / 2,
|
||||
y = (y + 3) / 2,
|
||||
type = type,
|
||||
centeredX = true,
|
||||
small = true
|
||||
).render(context)
|
||||
}
|
||||
else {
|
||||
TypeReturnIcon(
|
||||
x = (x + 12.125) / 2,
|
||||
y = (y + 3) / 2,
|
||||
centeredX = true,
|
||||
small = true
|
||||
).render(context)
|
||||
}
|
||||
|
||||
|
||||
context.matrices.pop()
|
||||
}
|
||||
|
||||
override fun playDownSound(soundManager: SoundManager) {
|
||||
|
||||
}
|
||||
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
|
||||
|
||||
|
||||
}
|
||||
+2
-1
@@ -40,7 +40,8 @@ object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSno
|
||||
runtime = particleRuntime,
|
||||
sourceVelocity = { entity.velocity },
|
||||
sourceAlive = { !entity.isRemoved },
|
||||
sourceVisible = { !entity.isInvisible }
|
||||
sourceVisible = { !entity.isInvisible },
|
||||
entity = entity
|
||||
)
|
||||
|
||||
storm.spawn()
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.cobblemon.mod.common.client.net.gui
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.gui.TMMScreenHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
object CraftBlankTMPacketHandler : ServerNetworkPacketHandler<CraftBlankTMPacket> {
|
||||
override fun handle(packet: CraftBlankTMPacket, server: MinecraftServer, player: ServerPlayerEntity) {
|
||||
val screen = player.currentScreenHandler as TMMScreenHandler
|
||||
val ingredientSlot = screen.input.getStack(2)
|
||||
|
||||
if (ingredientSlot.isOf(Items.AMETHYST_SHARD) && ingredientSlot.count >= 1) {
|
||||
screen.input.removeStack(2, 1)
|
||||
screen.result.setStack(0, CobblemonItems.BLANK_TM.defaultStack)
|
||||
|
||||
screen.input.markDirty()
|
||||
screen.result.markDirty()
|
||||
player.currentScreenHandler.syncState()
|
||||
|
||||
player.world.playSoundServer(player.pos, CobblemonSounds.TMM_CRAFT_BLANK, SoundCategory.BLOCKS)
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.cobblemon.mod.common.client.net.gui
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.gui.TMMScreenHandler
|
||||
import com.cobblemon.mod.common.item.TechnicalMachineItem
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
object CraftTMPacketHandler : ServerNetworkPacketHandler<CraftTMPacket> {
|
||||
override fun handle(packet: CraftTMPacket, server: MinecraftServer, player: ServerPlayerEntity) {
|
||||
val screen = player.currentScreenHandler as TMMScreenHandler
|
||||
val discSlot = screen.inventory?.getStack(0)
|
||||
val gemSlot = screen.inventory?.getStack(1)
|
||||
val ingredientSlot = screen.inventory?.getStack(2)
|
||||
val outputSlot = screen.result.getStack(0)
|
||||
val typeGem = Registries.ITEM.get(ElementalTypes.get(packet.tm.type)?.typeGem)
|
||||
|
||||
print("test")
|
||||
|
||||
if (!outputSlot.isEmpty) return
|
||||
if (discSlot != null) {
|
||||
if (!discSlot.isOf(CobblemonItems.BLANK_TM)) return
|
||||
}
|
||||
if (gemSlot != null) {
|
||||
if (!gemSlot.isOf(typeGem)) return
|
||||
}
|
||||
if (packet.tm.recipe != null) {
|
||||
if (ingredientSlot != null) {
|
||||
if (!ingredientSlot.isOf(Registries.ITEM.get(packet.tm.recipe.item))) return
|
||||
}
|
||||
if (ingredientSlot != null) {
|
||||
if (ingredientSlot.count < packet.tm.recipe.count) return
|
||||
}
|
||||
}
|
||||
|
||||
val item = CobblemonItems.TECHNICAL_MACHINE.defaultStack
|
||||
item.getOrCreateNbt().putString(TechnicalMachineItem.STORED_MOVE_KEY, packet.tm.id().toString())
|
||||
|
||||
screen.result.setStack(0, item)
|
||||
screen.inventory?.removeStack(0, 1)
|
||||
screen.inventory?.removeStack(1, 1)
|
||||
if (packet.tm.recipe != null) {
|
||||
screen.inventory?.removeStack(2, packet.tm.recipe.count)
|
||||
}
|
||||
|
||||
screen.input.markDirty()
|
||||
screen.result.markDirty()
|
||||
player.currentScreenHandler.syncState()
|
||||
|
||||
player.world.playSoundServer(player.pos, CobblemonSounds.TMM_CRAFT, SoundCategory.BLOCKS)
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.cobblemon.mod.common.client.net.gui
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.OpenTMMPacket
|
||||
import net.minecraft.client.MinecraftClient
|
||||
|
||||
object OpenTMMPacketHandler : ClientNetworkPacketHandler<OpenTMMPacket>
|
||||
{
|
||||
override fun handle(packet: OpenTMMPacket, client: MinecraftClient) {
|
||||
//client.setScreen()
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.particle
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleEffect
|
||||
import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterAction
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
@@ -42,6 +43,11 @@ class ParticleStorm(
|
||||
): NoRenderParticle(world, matrixWrapper.getOrigin().x, matrixWrapper.getOrigin().y, matrixWrapper.getOrigin().z) {
|
||||
fun spawn() {
|
||||
if (entity != null) {
|
||||
runtime.environment.getQueryStruct()
|
||||
.addFunction("entity_width") { DoubleValue(entity.boundingBox.xLength) }
|
||||
.addFunction("entity_height") { DoubleValue(entity.boundingBox.yLength) }
|
||||
.addFunction("entity_size") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength }) }
|
||||
.addFunction("entity_radius") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength } / 2) }
|
||||
// TODO replace with a generified call to if (entity is MoLangEntity) entity.applyVariables(env) or w/e
|
||||
runtime.environment.setSimpleVariable("entity_width", DoubleValue(entity.boundingBox.xLength))
|
||||
runtime.environment.setSimpleVariable("entity_height", DoubleValue(entity.boundingBox.yLength))
|
||||
@@ -67,6 +73,8 @@ class ParticleStorm(
|
||||
// The idea is that some instantaneous particle effects could teeeechnically be over before they start.
|
||||
var hasPlayedOnce = false
|
||||
|
||||
var distanceTravelled = 0F
|
||||
|
||||
companion object {
|
||||
var contextStorm: ParticleStorm? = null
|
||||
}
|
||||
@@ -75,6 +83,7 @@ class ParticleStorm(
|
||||
|
||||
init {
|
||||
runtime.execute(effect.emitter.startExpressions)
|
||||
effect.emitter.creationEvents.forEach { it.trigger(this, null) }
|
||||
}
|
||||
|
||||
override fun getMaxAge(): Int {
|
||||
@@ -84,6 +93,7 @@ class ParticleStorm(
|
||||
override fun markDead() {
|
||||
super.markDead()
|
||||
if (!despawned) {
|
||||
effect.emitter.expirationEvents.forEach { it.trigger(this, null) }
|
||||
despawned = true
|
||||
onDespawn()
|
||||
}
|
||||
@@ -116,6 +126,12 @@ class ParticleStorm(
|
||||
y = pos.y
|
||||
z = pos.z
|
||||
|
||||
val oldDistanceTravelled = distanceTravelled
|
||||
distanceTravelled += Vec3d(x - prevPosX, y - prevPosY, z - prevPosZ).length().toFloat()
|
||||
|
||||
effect.emitter.travelDistanceEvents.check(this, null, oldDistanceTravelled.toDouble(), distanceTravelled.toDouble())
|
||||
effect.emitter.loopingTravelDistanceEvents.forEach { it.check(this, null, oldDistanceTravelled.toDouble(), distanceTravelled.toDouble()) }
|
||||
effect.emitter.eventTimeline.check(this, null, (age - 1) / 20.0, age / 20.0)
|
||||
|
||||
runtime.environment.setSimpleVariable("emitter_random_1", DoubleValue(Random.Default.nextDouble()))
|
||||
runtime.environment.setSimpleVariable("emitter_random_2", DoubleValue(Random.Default.nextDouble()))
|
||||
@@ -137,18 +153,27 @@ class ParticleStorm(
|
||||
}
|
||||
}
|
||||
|
||||
fun spawnParticle() {
|
||||
fun getNextParticleSpawnPosition(): Vec3d {
|
||||
runtime.environment.setSimpleVariable("particle_random_1", DoubleValue(Random.Default.nextDouble()))
|
||||
runtime.environment.setSimpleVariable("particle_random_2", DoubleValue(Random.Default.nextDouble()))
|
||||
runtime.environment.setSimpleVariable("particle_random_3", DoubleValue(Random.Default.nextDouble()))
|
||||
runtime.environment.setSimpleVariable("particle_random_4", DoubleValue(Random.Default.nextDouble()))
|
||||
|
||||
val center = transformPosition(effect.emitter.shape.getCenter(runtime, entity))
|
||||
val newPosition = transformPosition(effect.emitter.shape.getNewParticlePosition(runtime, entity))
|
||||
val initialVelocity = effect.particle.motion.getInitialVelocity(runtime, storm = this, particlePos = newPosition, emitterPos = center)
|
||||
val velocity = initialVelocity
|
||||
return newPosition
|
||||
}
|
||||
|
||||
fun getNextParticleVelocity(nextParticlePosition: Vec3d): Vec3d {
|
||||
val center = transformPosition(effect.emitter.shape.getCenter(runtime, entity))
|
||||
val initialVelocity = effect.particle.motion.getInitialVelocity(runtime, storm = this, particlePos = nextParticlePosition, emitterPos = center)
|
||||
return initialVelocity
|
||||
.multiply(1 / 20.0)
|
||||
.add(if (effect.space.localVelocity) sourceVelocity() else Vec3d.ZERO)
|
||||
}
|
||||
|
||||
fun spawnParticle() {
|
||||
val newPosition = getNextParticleSpawnPosition()
|
||||
val velocity = getNextParticleVelocity(newPosition)
|
||||
|
||||
contextStorm = this
|
||||
world.addParticle(particleEffect, newPosition.x, newPosition.y, newPosition.z, velocity.x, velocity.y, velocity.z)
|
||||
|
||||
@@ -89,6 +89,14 @@ class SnowstormParticle(
|
||||
var viewDirection = Vec3d.ZERO
|
||||
var originPos = Vec3d(storm.getX(), storm.getY(), storm.getZ())
|
||||
|
||||
fun getX() = x
|
||||
fun getY() = y
|
||||
fun getZ() = z
|
||||
|
||||
fun getVelocityX() = velocityX
|
||||
fun getVelocityY() = velocityY
|
||||
fun getVelocityZ() = velocityZ
|
||||
|
||||
fun getSpriteFromAtlas(): Sprite {
|
||||
val atlas = MinecraftClient.getInstance().particleManager.particleAtlasTexture
|
||||
|
||||
@@ -118,6 +126,7 @@ class SnowstormParticle(
|
||||
storm.particles.add(this)
|
||||
gravityStrength = 0F
|
||||
particleTextureSheet = if (invisible) NO_RENDER else PARTICLE_SHEET_TRANSLUCENT
|
||||
storm.effect.particle.creationEvents.forEach { it.trigger(storm, this) }
|
||||
// when (storm.effect.particle.material) {
|
||||
// ParticleMaterial.ALPHA -> ParticleMaterials.ALPHA
|
||||
// ParticleMaterial.OPAQUE -> ParticleMaterials.OPAQUE
|
||||
@@ -245,6 +254,7 @@ class SnowstormParticle(
|
||||
angularVelocity += storm.effect.particle.rotation.getAngularAcceleration(storm.runtime, angularVelocity) / 20
|
||||
|
||||
if (age > maxAge || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
|
||||
storm.effect.particle.expirationEvents.forEach { it.trigger(storm, this)}
|
||||
markDead()
|
||||
return
|
||||
} else {
|
||||
@@ -285,6 +295,8 @@ class SnowstormParticle(
|
||||
age++
|
||||
|
||||
this.move(velocityX, velocityY, velocityZ)
|
||||
|
||||
storm.effect.particle.timeline.check(storm, this, (age - 1) / 20.0, age / 20.0)
|
||||
}
|
||||
|
||||
override fun move(dx: Double, dy: Double, dz: Double) {
|
||||
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.item.PokemonItem
|
||||
import net.minecraft.block.HorizontalFacingBlock
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory
|
||||
import net.minecraft.client.render.model.json.ModelTransformationMode
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.item.BannerItem
|
||||
import net.minecraft.item.BedItem
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtInt
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
import net.minecraft.world.World
|
||||
|
||||
class DisplayCaseRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEntityRenderer<DisplayCaseBlockEntity> {
|
||||
val coinPouchStack: ItemStack by lazy { ItemStack(CobblemonItems.RELIC_COIN_POUCH).also { it.setSubNbt("CustomModelData", NbtInt.of(1)) } }
|
||||
override fun render(
|
||||
entity: DisplayCaseBlockEntity,
|
||||
tickDelta: Float,
|
||||
matrices: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val stack: ItemStack = if (entity.getStack().isOf(CobblemonItems.RELIC_COIN_POUCH)) {
|
||||
coinPouchStack
|
||||
} else {
|
||||
entity.getStack()
|
||||
}
|
||||
val world = entity.world ?: return
|
||||
val posType = getPositioningType(stack, world)
|
||||
val blockState = if (entity.world != null) entity.cachedState
|
||||
else (CobblemonBlocks.DISPLAY_CASE.defaultState.with(HorizontalFacingBlock.FACING, Direction.NORTH))
|
||||
val yRot = if (posType == PositioningType.ITEM_MODEL) blockState.get(HorizontalFacingBlock.FACING).opposite.asRotation()
|
||||
else blockState.get(HorizontalFacingBlock.FACING).asRotation()
|
||||
|
||||
if (stack.item is PokemonItem) {
|
||||
renderPokemon(
|
||||
matrices,
|
||||
vertexConsumers,
|
||||
light,
|
||||
stack,
|
||||
yRot
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
matrices.push()
|
||||
matrices.translate(0.5f, 0.4f, 0.5f)
|
||||
|
||||
matrices.scale(posType.scaleX, posType.scaleY, posType.scaleZ)
|
||||
matrices.translate(posType.transX, posType.transY, posType.transZ)
|
||||
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-yRot))
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(posType.rotY))
|
||||
|
||||
MinecraftClient.getInstance().itemRenderer.renderItem(
|
||||
stack,
|
||||
ModelTransformationMode.GROUND,
|
||||
light,
|
||||
overlay,
|
||||
matrices,
|
||||
vertexConsumers,
|
||||
entity.world,
|
||||
0
|
||||
)
|
||||
|
||||
matrices.pop()
|
||||
|
||||
}
|
||||
|
||||
private fun renderPokemon(
|
||||
matrices: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
light: Int,
|
||||
stack: ItemStack,
|
||||
yRot: Float
|
||||
) {
|
||||
val item = stack.item as? PokemonItem ?: return
|
||||
val pokemon = item.asPokemon(stack) ?: return
|
||||
val model = PokemonModelRepository.getPoser(pokemon.species.resourceIdentifier, pokemon.aspects)
|
||||
val renderLayer = model.getLayer(PokemonModelRepository.getTexture(pokemon.species.resourceIdentifier, pokemon.aspects, 0F))
|
||||
val tint = item.tint(stack)
|
||||
val vertexConsumer: VertexConsumer = vertexConsumers.getBuffer(renderLayer)
|
||||
val scale = 0.25f
|
||||
|
||||
matrices.push()
|
||||
matrices.scale(1f, -1f, -1f)
|
||||
matrices.translate(0.5f, -0.69f, -0.5f)
|
||||
matrices.scale(scale, scale, scale)
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yRot))
|
||||
|
||||
model.setupAnimStateless(PoseType.PROFILE)
|
||||
|
||||
model.withLayerContext(vertexConsumers, null, PokemonModelRepository.getLayers(pokemon.species.resourceIdentifier, pokemon.aspects)) {
|
||||
model.render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV, tint.x, tint.y, tint.z, tint.w)
|
||||
}
|
||||
|
||||
matrices.pop()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val mobHeads = listOf<Item>(
|
||||
Items.SKELETON_SKULL,
|
||||
Items.WITHER_SKELETON_SKULL,
|
||||
Items.ZOMBIE_HEAD,
|
||||
Items.PIGLIN_HEAD,
|
||||
Items.PLAYER_HEAD,
|
||||
Items.DRAGON_HEAD,
|
||||
Items.CREEPER_HEAD
|
||||
)
|
||||
|
||||
private fun getPositioningType(stack: ItemStack, world: World) = when {
|
||||
mobHeads.contains(stack.item) -> PositioningType.MOB_HEAD
|
||||
stack.item is BedItem -> PositioningType.BED
|
||||
stack.item is BannerItem -> PositioningType.BANNER
|
||||
stack.item is PokeBallItem -> PositioningType.POKE_BALL
|
||||
stack.item == CobblemonItems.RELIC_COIN_POUCH -> PositioningType.COIN_POUCH
|
||||
stack.item == CobblemonItems.PASTURE -> PositioningType.PASTURE
|
||||
stack.item == CobblemonItems.POKEMON_MODEL -> PositioningType.ITEM_MODEL
|
||||
stack.item == Items.SHIELD -> PositioningType.SHIELD
|
||||
stack.item == Items.DECORATED_POT -> PositioningType.MOB_HEAD
|
||||
MinecraftClient.getInstance().itemRenderer.getModel(stack, world, null, 0).hasDepth() -> PositioningType.BLOCK_MODEL
|
||||
else -> PositioningType.ITEM_MODEL
|
||||
}
|
||||
}
|
||||
|
||||
private enum class PositioningType(
|
||||
val scaleX: Float, val scaleY: Float, val scaleZ: Float,
|
||||
val transX: Float, val transY: Float, val transZ: Float,
|
||||
val rotY: Float = 0f
|
||||
) {
|
||||
POKE_BALL(1f, 1f, 1f, 0f, 0.04f, 0f),
|
||||
BLOCK_MODEL(1f, 1f, 1f, 0f, -0.15f, 0f),
|
||||
ITEM_MODEL(1f, 1f, 1f, 0f, 0.04f, 0f),
|
||||
BED(1f, 1f, 1f, 0f, -0.02f, 0f),
|
||||
BANNER(1f, 1f, 1f, 0f, -0.02f, 0f, 180f),
|
||||
MOB_HEAD(1f, 1f, 1f, 0f, -0.025f, 0f, 180f),
|
||||
SHIELD(1f, 1f, 1f, 0f, -0.045f, 0f, 180f),
|
||||
PASTURE(1f, 1f, 1f, 0f, 0.0375f, 0f),
|
||||
COIN_POUCH(1f, 1f, 1f, 0f, 0.415f, 0f)
|
||||
}
|
||||
}
|
||||
+16
-6
@@ -19,8 +19,10 @@ import net.minecraft.client.render.block.entity.BlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory
|
||||
import net.minecraft.client.render.model.json.ModelTransformationMode
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.state.property.DirectionProperty
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
class FossilAnalyzerRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEntityRenderer<FossilAnalyzerBlockEntity> {
|
||||
|
||||
@@ -34,21 +36,29 @@ class FossilAnalyzerRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEnt
|
||||
) {
|
||||
val blockState = if (entity.world != null) entity.cachedState
|
||||
else (CobblemonBlocks.FOSSIL_ANALYZER.defaultState.with(HorizontalFacingBlock.FACING, Direction.SOUTH) as BlockState)
|
||||
val yRot = blockState.get(HorizontalFacingBlock.FACING).asRotation()
|
||||
//We shouldnt have to do any complex rendering when the block isn't a multiblock
|
||||
// We shouldn't have to do any complex rendering when the block isn't a multiblock
|
||||
if (entity.multiblockStructure == null) {
|
||||
return
|
||||
}
|
||||
val direction = blockState.get(HorizontalFacingBlock.FACING)
|
||||
val yRot = direction.asRotation() + if(direction == Direction.WEST || direction == Direction.EAST) 180F else 0F
|
||||
val struct = entity.multiblockStructure as FossilMultiblockStructure
|
||||
|
||||
struct.fossilInventory.forEachIndexed { index, fossilStack ->
|
||||
matrices.push()
|
||||
|
||||
matrices.translate(0.5, 0.4 + (index * 0.1F), 0.55)
|
||||
matrices.scale(0.7F, 0.7F, 0.7F)
|
||||
|
||||
val dirOffset = when (direction) {
|
||||
Direction.NORTH -> Vec3d(0.0, 0.0, 0.05)
|
||||
Direction.SOUTH -> Vec3d(0.0, 0.0, -0.05)
|
||||
Direction.EAST -> Vec3d(-0.05, 0.0, 0.0)
|
||||
Direction.WEST -> Vec3d(0.05, 0.0, 0.0)
|
||||
else -> Vec3d.ZERO
|
||||
}
|
||||
matrices.translate(0.5 + dirOffset.x,0.4 + (index * 0.1) + dirOffset.y, 0.5 + dirOffset.z)
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yRot))
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180F))
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-yRot))
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90F))
|
||||
matrices.scale(0.7F, 0.7F, 0.7F)
|
||||
|
||||
MinecraftClient.getInstance().itemRenderer.renderItem(fossilStack, ModelTransformationMode.NONE, light, overlay, matrices, vertexConsumers, entity.world, 0)
|
||||
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.api.text.blue
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.block.TMBlock
|
||||
import com.cobblemon.mod.common.block.entity.TMBlockEntity
|
||||
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
|
||||
import com.cobblemon.mod.common.client.render.layer.CobblemonRenderLayers
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.setRotation
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.Axis
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.LightmapTextureManager
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
import java.awt.Color
|
||||
import kotlin.math.sin
|
||||
|
||||
class TMBlockRenderer(context: BlockEntityRendererFactory.Context) : BlockEntityRenderer<TMBlockEntity> {
|
||||
//TODO Do the render optimizations that we do for berries and fossils
|
||||
override fun render(
|
||||
entity: TMBlockEntity,
|
||||
tickDelta: Float,
|
||||
matrices: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
val diskModel = MiscModelRepository.modelOf(MODEL_ID)
|
||||
matrices.push()
|
||||
val tm = entity.tmmInventory.filterTM?.let {
|
||||
TechnicalMachines.getTechnicalMachineFromStack(
|
||||
it
|
||||
)
|
||||
}
|
||||
entity.partialTicks += tickDelta
|
||||
val color = tm?.let { Color(ElementalTypes.get(it.type)!!.hue) } ?: Color.WHITE
|
||||
when (entity.cachedState.get(TMBlock.FACING)) {
|
||||
Direction.SOUTH -> matrices.translate(15F / 16F, 5.5F / 16F, 1F / 16F)
|
||||
Direction.WEST -> matrices.translate(14F / 16F, 5.5F / 16F, 0F)
|
||||
Direction.EAST -> matrices.translate(1F, 5.5F / 16F, 0F)
|
||||
else -> matrices.translate(15F / 16F, 5.5F / 16F, -1F / 16F)
|
||||
}
|
||||
|
||||
matrices.translate(-7.0/16f, 0.0, 8.0/16f)
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees( entity.partialTicks / 0.5f))
|
||||
matrices.translate(7.0/16f, 0.0, -8.0/16f)
|
||||
|
||||
|
||||
val renderLayer = RenderLayer.getEntityCutout(cobblemonResource("textures/block/tm_machine.png"))
|
||||
diskModel?.render(
|
||||
matrices,
|
||||
vertexConsumers.getBuffer(renderLayer),
|
||||
light,
|
||||
overlay,
|
||||
color.red.toFloat() / 255F,
|
||||
color.green.toFloat() / 255F,
|
||||
color.blue.toFloat() / 255F,
|
||||
1.0F
|
||||
)
|
||||
matrices.pop()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val MODEL_ID = cobblemonResource("tm_disk.geo")
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -40,6 +40,27 @@ object CobblemonRenderLayers {
|
||||
multiPhaseParameters
|
||||
)
|
||||
}
|
||||
val TM_BLOCK_LAYER = run {
|
||||
val multiPhaseParameters = RenderLayer.MultiPhaseParameters.builder()
|
||||
.lightmap(ENABLE_LIGHTMAP)
|
||||
.program(CUTOUT_PROGRAM)
|
||||
.texture(Texture(
|
||||
cobblemonResource("textures/misc/tm_machine"),
|
||||
false,
|
||||
true
|
||||
))
|
||||
.cull(DISABLE_CULLING)
|
||||
.build(true)
|
||||
RenderLayer.of(
|
||||
"tm_block",
|
||||
VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL,
|
||||
VertexFormat.DrawMode.QUADS,
|
||||
512,
|
||||
true,
|
||||
false,
|
||||
multiPhaseParameters
|
||||
)
|
||||
}
|
||||
|
||||
val ENTITY_TRANSLUCENT: BiFunction<Identifier, Boolean, RenderLayer> = Util.memoize { texture: Identifier, affectsOutline: kotlin.Boolean ->
|
||||
var multiPhaseParameters: RenderLayer.MultiPhaseParameters =
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ abstract class PoseableEntityState<T : Entity> : Schedulable {
|
||||
val pose = currentPose?.let(model::getPose)
|
||||
allStatefulAnimations.forEach { it.applyEffects(entity, this, previousSeconds, currentSeconds) }
|
||||
primaryAnimation?.animation?.applyEffects(entity, this, previousSeconds, currentSeconds)
|
||||
pose?.idleAnimations?.filter { shouldIdleRun(it, 0.5F) }
|
||||
pose?.idleAnimations?.filter { shouldIdleRun(it, 0.5F) }?.forEach { it.applyEffects(entity, this, previousSeconds, currentSeconds) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.command
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
|
||||
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.util.alias
|
||||
import com.cobblemon.mod.common.util.distanceTo
|
||||
@@ -17,10 +18,12 @@ import com.cobblemon.mod.common.util.permission
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import com.mojang.brigadier.Command
|
||||
import com.mojang.brigadier.CommandDispatcher
|
||||
import com.mojang.brigadier.arguments.StringArgumentType
|
||||
import net.minecraft.command.argument.DimensionArgumentType
|
||||
import net.minecraft.command.argument.EntityArgumentType
|
||||
import net.minecraft.command.argument.IdentifierArgumentType
|
||||
import net.minecraft.command.argument.Vec3ArgumentType
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.server.command.CommandManager
|
||||
import net.minecraft.server.command.ServerCommandSource
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
@@ -40,6 +43,16 @@ object BedrockParticleCommand {
|
||||
val entities = EntityArgumentType.getEntities(it, "target")
|
||||
return@executes entities.sumOf { entity -> execute(it.source, effectId, entity.world as ServerWorld, entity.pos) }
|
||||
}
|
||||
.then(
|
||||
CommandManager.argument("locator", StringArgumentType.word())
|
||||
.executes {
|
||||
val effectId = IdentifierArgumentType.getIdentifier(it, "effect")
|
||||
val entities = EntityArgumentType.getEntities(it, "target")
|
||||
val locator = StringArgumentType.getString(it, "locator")
|
||||
|
||||
return@executes entities.sumOf { entity -> execute(it.source, effectId, entity.world as ServerWorld, entity, locator) }
|
||||
}
|
||||
)
|
||||
)
|
||||
.then(
|
||||
CommandManager.argument("world", DimensionArgumentType.dimension())
|
||||
@@ -60,9 +73,16 @@ object BedrockParticleCommand {
|
||||
|
||||
private fun execute(source: ServerCommandSource, effectId: Identifier, world: ServerWorld, target: Vec3d): Int {
|
||||
val pos = target.toBlockPos()
|
||||
val nearbyPlayers = world.getPlayers { it.distanceTo(pos) < 200 }
|
||||
val nearbyPlayers = world.getPlayers { it.distanceTo(pos) < 1000 }
|
||||
nearbyPlayers.forEach { player -> player.sendPacket(SpawnSnowstormParticlePacket(effectId, target)) }
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
|
||||
private fun execute(source: ServerCommandSource, effectId: Identifier, world: ServerWorld, target: Entity, locator: String): Int {
|
||||
val pos = target.blockPos
|
||||
val nearbyPlayers = world.getPlayers { it.distanceTo(pos) < 1000 }
|
||||
nearbyPlayers.forEach { player -> player.sendPacket(SpawnSnowstormEntityParticlePacket(effectId, target.id, locator)) }
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,21 +11,27 @@ package com.cobblemon.mod.common.command
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonEntities
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.taskBuilder
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.particle.SnowstormParticleReader
|
||||
import com.cobblemon.mod.common.trade.ActiveTrade
|
||||
import com.cobblemon.mod.common.trade.DummyTradeParticipant
|
||||
import com.cobblemon.mod.common.trade.PlayerTradeParticipant
|
||||
import com.cobblemon.mod.common.util.fromJson
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.toPokemon
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -57,7 +63,12 @@ object TestCommand {
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
|
||||
for (id in Cobblemon.playerData.get(context.source.playerOrThrow).tmSet) {
|
||||
context.source.player?.sendMessage(TechnicalMachines.tmMap[id]?.translatedMoveName())
|
||||
}
|
||||
|
||||
try {
|
||||
filter(context)
|
||||
// readBerryDataFromCSV()
|
||||
|
||||
// this.testClosestBattle(context)
|
||||
@@ -105,6 +116,12 @@ object TestCommand {
|
||||
var trade: ActiveTrade? = null
|
||||
var lastDebugId = 0
|
||||
|
||||
private fun filter(context: CommandContext<ServerCommandSource>) {
|
||||
val tms = TechnicalMachine.filterTms("fire", null, null)
|
||||
println(context.source.playerOrThrow.party().get(1)?.getDisplayName())
|
||||
tms.forEach { context.source.playerOrThrow.sendMessage(it.moveName.text()) }
|
||||
}
|
||||
|
||||
private fun testClosestBattle(context: CommandContext<ServerCommandSource>) {
|
||||
val player = context.source.playerOrThrow
|
||||
val cloneTeam = player.party().toBattleTeam(true)
|
||||
@@ -292,4 +309,82 @@ object TestCommand {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun testAbilitiesBetweenEvolution(context: CommandContext<ServerCommandSource>) {
|
||||
val results = Text.literal("Ability test results (Assumed default assets)")
|
||||
.append(Text.literal("\n"))
|
||||
.append(this.testHiddenAbilityThroughoutEvolutions())
|
||||
.append(Text.literal("\n"))
|
||||
.append(this.testMiddleStageSingleAbility())
|
||||
.append(Text.literal("\n"))
|
||||
.append(this.testForcedAbility())
|
||||
.append(Text.literal("\n"))
|
||||
.append(this.testIllegalAbilityNonForced())
|
||||
.append(Text.literal("\n"))
|
||||
.append(this.testAbilityCapsule())
|
||||
.append(Text.literal("\n"))
|
||||
.append(this.testAbilityPatch())
|
||||
context.source.sendMessage(results)
|
||||
}
|
||||
|
||||
private fun testHiddenAbilityThroughoutEvolutions(): Text {
|
||||
// Hidden ability test, Dragonite HA differs from Dratini/Dragonair we need to ensure he keeps that ability until the end
|
||||
// Skip Dratini cause same HA irrelevant for this test
|
||||
val pokemon = PokemonProperties.parse("dragonair level=${Cobblemon.config.maxPokemonLevel} hiddenability=true").create()
|
||||
val dragonite = pokemon.evolutions.firstOrNull() ?: return Text.literal("✖ Failed to find Dragonair » Dragonite evolution").red()
|
||||
dragonite.evolutionMethod(pokemon)
|
||||
val failed = pokemon.ability.index != 0 || pokemon.ability.priority != Priority.LOW || pokemon.ability.forced
|
||||
val symbol = if (failed) "✖" else "✔"
|
||||
val result = Text.literal(" $symbol Dratini line final Ability(name=${pokemon.ability.name}, priority=${pokemon.ability.priority}, index=${pokemon.ability.index}, forced=${pokemon.ability.forced})")
|
||||
return if (failed) result.red() else result.green()
|
||||
}
|
||||
|
||||
private fun testMiddleStageSingleAbility(): Text {
|
||||
val pokemon = PokemonProperties.parse("scatterbug level=${Cobblemon.config.maxPokemonLevel} ability=compoundeyes").create()
|
||||
val spewpa = pokemon.evolutions.firstOrNull() ?: return Text.literal("✖ Failed to find Scatterbug » Spewpa evolution").red()
|
||||
spewpa.evolutionMethod(pokemon)
|
||||
val vivillon = pokemon.evolutions.firstOrNull() ?: return Text.literal("✖ Failed to find Spewpa » Vivillon evolution").red()
|
||||
vivillon.evolutionMethod(pokemon)
|
||||
val failed = pokemon.ability.index != 1 || pokemon.ability.priority != Priority.LOWEST || pokemon.ability.forced
|
||||
val symbol = if (failed) "✖" else "✔"
|
||||
val result = Text.literal(" $symbol Scatterbug line final Ability(name=${pokemon.ability.name}, priority=${pokemon.ability.priority}, index=${pokemon.ability.index}, forced=${pokemon.ability.forced})")
|
||||
return if (failed) result.red() else result.green()
|
||||
}
|
||||
|
||||
private fun testForcedAbility(): Text {
|
||||
val pokemon = PokemonProperties.parse("magikarp level=${Cobblemon.config.maxPokemonLevel} ability=adaptability").create()
|
||||
val gyarados = pokemon.evolutions.firstOrNull() ?: return Text.literal("✖ Failed to find Magikarp » Gyarados evolution").red()
|
||||
gyarados.evolutionMethod(pokemon)
|
||||
val failed = !pokemon.ability.forced || pokemon.ability.template.name != "adaptability"
|
||||
val symbol = if (failed) "✖" else "✔"
|
||||
val result = Text.literal(" $symbol Magikarp line forced Ability(name=${pokemon.ability.name}, priority=${pokemon.ability.priority}, index=${pokemon.ability.index}, forced=${pokemon.ability.forced})")
|
||||
return if (failed) result.red() else result.green()
|
||||
}
|
||||
|
||||
private fun testIllegalAbilityNonForced(): Text {
|
||||
val pokemon = PokemonProperties.parse("rattata").create()
|
||||
pokemon.updateAbility(Abilities.getOrException("adaptability").create(false))
|
||||
val failed = !pokemon.ability.forced
|
||||
val symbol = if (failed) "✖" else "✔"
|
||||
val result = Text.literal(" $symbol Rattata illegal non-forced (name=${pokemon.ability.name}, priority=${pokemon.ability.priority}, index=${pokemon.ability.index}, forced=${pokemon.ability.forced})")
|
||||
return if (failed) result.red() else result.green()
|
||||
}
|
||||
|
||||
private fun testAbilityCapsule(): Text {
|
||||
val pokemon = PokemonProperties.parse("rattata").create()
|
||||
val failed = !AbilityChanger.COMMON_ABILITY.performChange(pokemon)
|
||||
val symbol = if (failed) "✖" else "✔"
|
||||
val result = Text.literal(" $symbol Rattata capsule Ability(name=${pokemon.ability.name}, priority=${pokemon.ability.priority}, index=${pokemon.ability.index}, forced=${pokemon.ability.forced})")
|
||||
return if (failed) result.red() else result.green()
|
||||
}
|
||||
|
||||
private fun testAbilityPatch(): Text {
|
||||
val pokemon = PokemonProperties.parse("magikarp ha=true").create()
|
||||
// It shouldn't change
|
||||
val failed = AbilityChanger.HIDDEN_ABILITY.performChange(pokemon)
|
||||
val symbol = if (failed) "✖" else "✔"
|
||||
val result = Text.literal(" $symbol Magikarp patch Ability(name=${pokemon.ability.name}, priority=${pokemon.ability.priority}, index=${pokemon.ability.index}, forced=${pokemon.ability.forced})")
|
||||
return if (failed) result.red() else result.green()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,9 +86,6 @@ class CobblemonConfig {
|
||||
@NodeCategory(Category.Spawning)
|
||||
var maxNearbyBlocksVerticalRange = 2
|
||||
|
||||
@NodeCategory(Category.Spawning)
|
||||
var maxHorizontalSpace = 6
|
||||
|
||||
@NodeCategory(Category.Spawning)
|
||||
var maxVerticalSpace = 8
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
@@ -75,6 +76,7 @@ object CobblemonDataProvider : DataProvider {
|
||||
|
||||
CobblemonSpawnPools.load()
|
||||
this.register(Berries)
|
||||
this.register(TechnicalMachines)
|
||||
|
||||
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
|
||||
synchronizedPlayerIds.remove(it.player.uuid)
|
||||
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.entity.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.*
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToServer
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.entity.Despawner
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
@@ -31,9 +30,9 @@ import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes.FIRE
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.Poseable
|
||||
@@ -46,7 +45,6 @@ import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimat
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_DURATION
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
@@ -1046,13 +1044,13 @@ open class PokemonEntity(
|
||||
* @param player The player to attempt a battle with.
|
||||
* @return Whether the battle was successfully started.
|
||||
*/
|
||||
fun forceBattle(player: PlayerEntity): Boolean {
|
||||
if (!canBattle(player)) return false
|
||||
if (CobblemonClient.storage.myParty.isEmpty()) return false
|
||||
val selectedPokemon = CobblemonClient.storage.myParty.get(CobblemonClient.storage.selectedSlot) ?: return false
|
||||
if (selectedPokemon.isFainted()) return false
|
||||
sendPacketToServer(BattleChallengePacket(this.id, selectedPokemon.uuid))
|
||||
return true
|
||||
fun forceBattle(player: ServerPlayerEntity): Boolean {
|
||||
if (!canBattle(player)) {
|
||||
return false
|
||||
}
|
||||
var isSuccessful = false
|
||||
BattleBuilder.pve(player, this).ifSuccessful { isSuccessful = true }
|
||||
return isSuccessful
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.events
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.*
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
@@ -17,6 +18,9 @@ import com.cobblemon.mod.common.api.events.pokemon.LevelUpEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.PokemonCapturedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.TradeCompletedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
|
||||
import com.cobblemon.mod.common.block.TumblestoneBlock
|
||||
import com.cobblemon.mod.common.item.TumblestoneItem
|
||||
import com.cobblemon.mod.common.platform.events.ServerPlayerEvent
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import java.util.*
|
||||
|
||||
@@ -138,4 +142,20 @@ object AdvancementHandler {
|
||||
Cobblemon.playerData.saveSingle(playerData)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the advancement for placing a tumblestone
|
||||
*
|
||||
* Checks if the item in the player's hand is a tumblestone.
|
||||
* Then gets the block as a [TumblestoneBlock] so we can call the canGrow method.
|
||||
* Finally, triggers the advancement.
|
||||
*
|
||||
* @param event the event to trigger the advancement from
|
||||
*/
|
||||
fun onTumbleStonePlaced(event: ServerPlayerEvent.RightClickBlock) {
|
||||
if (event.player.getStackInHand(event.hand).item == CobblemonItems.TUMBLESTONE.asItem()) {
|
||||
val block = ((event.player.getStackInHand(event.hand).item as TumblestoneItem).block as TumblestoneBlock)
|
||||
CobblemonCriteria.PLANT_TUMBLESTONE.trigger(event.player, PlantTumblestoneContext(event.pos, block))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@
|
||||
package com.cobblemon.mod.common.events
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.item.TechnicalMachineItem
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.MinecraftServer
|
||||
|
||||
@@ -25,9 +27,21 @@ object ServerTickHandler {
|
||||
if (secondsTick == 20) {
|
||||
secondsTick = 0
|
||||
|
||||
// Party tick
|
||||
// Code to run every in-game second
|
||||
for (player in server.playerManager.playerList) {
|
||||
player.party().onSecondPassed(player)
|
||||
TechnicalMachines.checkPassives(player)
|
||||
|
||||
// Give player tm unlocks for tms they don't already have
|
||||
repeat(player.inventory.size()) {
|
||||
val stack = player.inventory.getStack(it)
|
||||
if (stack.item is TechnicalMachineItem) {
|
||||
val tm = TechnicalMachineItem.getMoveNbt(stack) ?: return@repeat
|
||||
if (!Cobblemon.playerData.get(player).tmSet.contains(tm.id())) {
|
||||
tm.unlock(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.cobblemon.mod.common.gui
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.mixin.invoker.ScreenHandlerTypeInvoker
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlags
|
||||
import net.minecraft.screen.ScreenHandlerType.Factory
|
||||
import net.minecraft.resource.featuretoggle.FeatureSet
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.ScreenHandlerType
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object CobblemonScreenHandlers {
|
||||
val TMM_SCREEN = register(cobblemonResource("tmm_screen"), ::TMMScreenHandler)
|
||||
val MOD_STATION_SCREEN = register(cobblemonResource("mod_station_screen"), ::ModStationScreenHandler)
|
||||
|
||||
fun <T : ScreenHandler> register(identifier: Identifier, factory: Factory<T>): ScreenHandlerType<T> {
|
||||
|
||||
val result = ScreenHandlerTypeInvoker.`cobblemon$create`(factory, FeatureFlags.VANILLA_FEATURES)
|
||||
Cobblemon.implementation.registerScreenHandlerType(identifier, result)
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.cobblemon.mod.common.gui
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.*
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.slot.CraftingResultSlot
|
||||
import net.minecraft.screen.slot.Slot
|
||||
|
||||
class ModStationScreenHandler(syncId: Int) : ScreenHandler(CobblemonScreenHandlers.MOD_STATION_SCREEN, syncId) {
|
||||
var playerInventory: PlayerInventory? = null
|
||||
var input: RecipeInputInventory = CraftingInventory(this, 2, 1)
|
||||
val result: CraftingResultInventory = CraftingResultInventory()
|
||||
var inventory: Inventory? = null
|
||||
|
||||
constructor(syncId: Int, playerInventory: PlayerInventory) : this(syncId, playerInventory, SimpleInventory(3))
|
||||
|
||||
constructor(syncId: Int, playerInventory: PlayerInventory, inventory: Inventory) : this(syncId) {
|
||||
val startX = 0 - 9
|
||||
val startY = 112
|
||||
val xLen = 18 // 18 is standard
|
||||
val yLen = 18 // 18 is standard
|
||||
this.playerInventory = playerInventory
|
||||
this.inventory = inventory
|
||||
|
||||
for (row in 0..2) {
|
||||
for (column in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, 9 + (row * 9) + column, startX + (xLen * column), startY + (yLen * row)))
|
||||
}
|
||||
}
|
||||
for (column in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, column, startX + (xLen * column), startY + 58))
|
||||
}
|
||||
|
||||
addSlot(CraftingResultSlot(
|
||||
playerInventory.player,
|
||||
input,
|
||||
result,
|
||||
0,
|
||||
startX,
|
||||
startY
|
||||
))
|
||||
|
||||
this.addSlot(Slot(this.inventory, 0, startX, startY - 20))
|
||||
this.addSlot(Slot(this.inventory, 1, startX, startY - 40))
|
||||
}
|
||||
|
||||
override fun quickMove(player: PlayerEntity?, slot: Int): ItemStack {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun canUse(player: PlayerEntity?): Boolean {
|
||||
return inventory?.canPlayerUse(player) ?: false
|
||||
}
|
||||
|
||||
override fun syncState() {
|
||||
super.syncState()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.cobblemon.mod.common.gui
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.entity.TMBlockEntity
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.*
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.slot.CraftingResultSlot
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.screen.slot.SlotActionType
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
class TMMScreenHandler(syncId: Int) : ScreenHandler(CobblemonScreenHandlers.TMM_SCREEN, syncId) {
|
||||
var playerInventory: PlayerInventory? = null
|
||||
|
||||
var input: RecipeInputInventory = CraftingInventory(this, 3, 1)
|
||||
val result: CraftingResultInventory = CraftingResultInventory()
|
||||
var tmmEntity: TMBlockEntity? = null
|
||||
var inventory: Inventory? = null
|
||||
|
||||
constructor(syncId: Int, playerInventory: PlayerInventory) : this(syncId, playerInventory, SimpleInventory(4), null) {
|
||||
}
|
||||
constructor(syncId: Int, playerInventory: PlayerInventory, inventory: Inventory, blockEntity: TMBlockEntity?) : this(syncId) {
|
||||
val startX = 0 - 9
|
||||
val startY = 112
|
||||
val xLen = 18 // 18 is standard
|
||||
val yLen = 18 // 18 is standard
|
||||
this.playerInventory = playerInventory
|
||||
this.inventory = inventory // as TMBlockEntity.TMBlockInventory) // this does grab the right inventory slots
|
||||
tmmEntity = blockEntity
|
||||
|
||||
if (inventory is TMBlockEntity.TMBlockInventory) {
|
||||
//inventory.tmBlockEntity
|
||||
|
||||
this.syncState()
|
||||
|
||||
/*this.inventory = inventory
|
||||
|
||||
this.tmmEntity = (inventory as TMBlockEntity.TMBlockInventory).tmBlockEntity
|
||||
|
||||
// sync the input slots with the TMM slots
|
||||
input.setStack(0, inventory.items!!.get(0))
|
||||
input.setStack(1, inventory.items!!.get(1))
|
||||
input.setStack(2, inventory.items!!.get(2))
|
||||
//inventory.items!![3] = result.getStack(0) // sync output of TMM with the crafted TM
|
||||
result.setStack(0, inventory.items!!.get(3))*/
|
||||
}
|
||||
|
||||
|
||||
for (row in 0..2) {
|
||||
for (column in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, 9 + (row * 9) + column, startX + (xLen * column), startY + (yLen * row)))
|
||||
}
|
||||
}
|
||||
for (column in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, column, startX + (xLen * column), startY + 58))
|
||||
}
|
||||
|
||||
this.addSlot(CraftingResultSlot(
|
||||
playerInventory.player,
|
||||
input,
|
||||
result,
|
||||
0,
|
||||
startX + 123,
|
||||
startY - 22
|
||||
))
|
||||
|
||||
this.addSlot(Slot(this.inventory, 0, startX + 167, startY + 9)) // material input slot 1
|
||||
this.addSlot(Slot(this.inventory, 1, startX + 185, startY + 9)) // material input slot 2
|
||||
this.addSlot(Slot(this.inventory, 2, startX + 203, startY + 9)) // material input slot 3
|
||||
|
||||
}
|
||||
override fun quickMove(player: PlayerEntity, slot: Int): ItemStack {
|
||||
return if (insertItem(getSlot(slot).stack, 0, SLOT_COUNT, true)) getSlot(slot).stack else ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun canUse(player: PlayerEntity?): Boolean {
|
||||
return inventory?.canPlayerUse(player) ?: false
|
||||
}
|
||||
|
||||
// override fun onClosed(player: PlayerEntity) {
|
||||
//// for (i in 0..2) {
|
||||
//// if (!slots[i].stack.isEmpty) player.giveOrDropItemStack(slots[i].stack, false)
|
||||
//// }
|
||||
// super.onClosed(player)
|
||||
// }
|
||||
|
||||
override fun onSlotClick(slotIndex: Int, button: Int, actionType: SlotActionType?, player: PlayerEntity) {
|
||||
val type = if (actionType == SlotActionType.THROW) SlotActionType.PICKUP else actionType // todo issue might be inventory is not being clicked, but it is index 36 still
|
||||
|
||||
// if slot is the output slot then clear the TMM output slot
|
||||
if (slotIndex == 36) {
|
||||
this.inventory?.setStack(3, ItemStack.EMPTY)
|
||||
}
|
||||
|
||||
super.onSlotClick(slotIndex, button, type, player)
|
||||
}
|
||||
|
||||
override fun onClosed(player: PlayerEntity?) {
|
||||
super.onClosed(player)
|
||||
|
||||
tmmEntity?.stateManager?.closeContainer(player, tmmEntity!!.world, tmmEntity!!.blockPos, tmmEntity!!.blockState)
|
||||
|
||||
}
|
||||
|
||||
override fun syncState() {
|
||||
if (inventory is TMBlockEntity.TMBlockInventory) {
|
||||
//inventory.tmBlockEntity
|
||||
|
||||
// sync the input slots with the TMM slots
|
||||
input.setStack(0, (inventory as TMBlockEntity.TMBlockInventory).items!!.get(0))
|
||||
input.setStack(1, (inventory as TMBlockEntity.TMBlockInventory).items!!.get(1))
|
||||
input.setStack(2, (inventory as TMBlockEntity.TMBlockInventory).items!!.get(2))
|
||||
//inventory.items!![3] = result.getStack(0) // sync output of TMM with the crafted TM
|
||||
|
||||
// if there is something in the output slot of this.inventory then sync that with result
|
||||
if ((inventory as TMBlockEntity.TMBlockInventory).items?.get(3) != ItemStack.EMPTY) {
|
||||
if (ItemStack.areItemsEqual((inventory as TMBlockEntity.TMBlockInventory).items?.get(3), ItemStack(CobblemonItems.TECHNICAL_MACHINE, 1))
|
||||
|| ItemStack.areItemsEqual((inventory as TMBlockEntity.TMBlockInventory).items?.get(3), ItemStack(CobblemonItems.BLANK_TM, 1)))
|
||||
result.setStack(0, (inventory as TMBlockEntity.TMBlockInventory).items!!.get(3))
|
||||
}
|
||||
// if inventory is empty in the output slot then sync it to result
|
||||
else
|
||||
(inventory as TMBlockEntity.TMBlockInventory).items?.set(3, result.getStack(0))
|
||||
|
||||
}
|
||||
|
||||
super.syncState()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SLOT_COUNT = 3
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.text.gray
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.block.entity.TMBlockEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.item.TooltipContext
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.ItemUsageContext
|
||||
import net.minecraft.nbt.NbtString
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.world.World
|
||||
|
||||
class TechnicalMachineItem(settings: Settings): CobblemonItem(settings) {
|
||||
|
||||
companion object {
|
||||
val STORED_MOVE_KEY = "StoredMove"
|
||||
|
||||
fun getMoveNbt(stack: ItemStack): TechnicalMachine? {
|
||||
val nbtCompound = stack.nbt ?: return null
|
||||
|
||||
return TechnicalMachines.tmMap[Identifier.tryParse(nbtCompound.getString(STORED_MOVE_KEY))]
|
||||
}
|
||||
|
||||
fun getItemColor(stack: ItemStack, tint: Int): Int {
|
||||
val nbt = getMoveNbt(stack) ?: return ElementalTypes.NORMAL.primaryColor
|
||||
|
||||
if (nbt.primaryColor != null && tint == 0) return nbt.primaryColor
|
||||
if (nbt.secondaryColor != null && tint != 0) return nbt.secondaryColor
|
||||
|
||||
val type = ElementalTypes.getOrException(nbt.type)
|
||||
if (tint == 0) return type.primaryColor
|
||||
return type.secondaryColor
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasGlint(stack: ItemStack?) = false
|
||||
|
||||
fun setNbt(stack: ItemStack, id: String): ItemStack {
|
||||
stack.getOrCreateNbt().put(STORED_MOVE_KEY, NbtString.of(id))
|
||||
return stack
|
||||
}
|
||||
|
||||
override fun appendTooltip(
|
||||
stack: ItemStack,
|
||||
world: World?,
|
||||
tooltip: MutableList<Text>,
|
||||
context: TooltipContext?
|
||||
) {
|
||||
val nbt = getMoveNbt(stack)
|
||||
val text: MutableText = if (nbt != null) {
|
||||
lang("move." + nbt.moveName)
|
||||
} else {
|
||||
lang("tms.unknown_move")
|
||||
}
|
||||
tooltip.add(text.gray())
|
||||
}
|
||||
|
||||
override fun useOnEntity(stack: ItemStack, user: PlayerEntity, entity: LivingEntity, hand: Hand): ActionResult {
|
||||
if (user.world.isClient) return ActionResult.FAIL
|
||||
if (entity !is PokemonEntity) return ActionResult.FAIL
|
||||
|
||||
val tm = getMoveNbt(stack) ?: return ActionResult.FAIL
|
||||
val move = Moves.getByName(tm.moveName) ?: return ActionResult.FAIL
|
||||
val pokemon = entity.pokemon
|
||||
|
||||
val tmLearnableMoves = pokemon.species.moves.tmLearnableMoves()
|
||||
|
||||
if (!tmLearnableMoves.contains(move)) {
|
||||
user.sendMessage(lang("tms.cannot_learn", pokemon.getDisplayName(), tm.translatedMoveName()))
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
if (pokemon.allAccessibleMoves.contains(move)) {
|
||||
user.sendMessage(lang("tms.already_known", pokemon.getDisplayName(), tm.translatedMoveName()))
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
if (!user.isCreative) stack.decrement(1)
|
||||
if (pokemon.moveSet.hasSpace()) { pokemon.moveSet.add(move.create()) }
|
||||
else { pokemon.benchedMoves.add(BenchedMove(move, 0)) }
|
||||
|
||||
user.sendMessage(lang("tms.teach_move", pokemon.getDisplayName(), tm.translatedMoveName()).green())
|
||||
user.playSound(CobblemonSounds.TM_USE, SoundCategory.PLAYERS, 1.0f, 1.0f)
|
||||
entity.cry()
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
|
||||
override fun useOnBlock(context: ItemUsageContext): ActionResult {
|
||||
if (context.world.isClient) {
|
||||
context.player?.swingHand(context.hand)
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
//val TMM = context.world.getBlockState(context.hitPos.toBlockPos()).block
|
||||
val TMM = context.world.getBlockEntity(context.hitPos.toBlockPos())
|
||||
|
||||
if (TMM is TMBlockEntity
|
||||
&& (TMM.tmmInventory.filterTM == null
|
||||
|| TMM.tmmInventory.filterTM!!.nbt?.get("StoredMove") != context.stack.nbt?.get("StoredMove"))) {
|
||||
context.player?.playSound(CobblemonSounds.TMM_ON, SoundCategory.BLOCKS, 1.0f, 1.0f)
|
||||
context.player?.swingHand(context.hand)
|
||||
if (TMM.tmmInventory.filterTM != null) {
|
||||
if (!context.player?.isCreative!!) {
|
||||
context.player!!.giveItemStack(TMM.tmmInventory.filterTM!!)
|
||||
}
|
||||
}
|
||||
|
||||
// set filterTM equal to the item it corresponds to
|
||||
TMM.tmmInventory.filterTM = TechnicalMachines.getTechnicalMachineFromStack(context.stack)?.let { TechnicalMachines.getStackFromTechnicalMachine(it) }
|
||||
|
||||
|
||||
|
||||
/*// if materials are in the machine already
|
||||
if (TMM.tmmInventory.items?.isNotEmpty() == true) {
|
||||
TMM.tmmInventory.items?.forEach {
|
||||
// Get the direction the block is facing
|
||||
val facingDirection = TMM.blockState.get(Properties.FACING).opposite
|
||||
|
||||
// Calculate the center position of the block
|
||||
val frontOffset = 0.5 // Half block offset to the front
|
||||
val spawnX = TMM.blockPos.x + 0.5 + facingDirection.offsetX * frontOffset
|
||||
val spawnY = TMM.blockPos.y + 0.3 + facingDirection.offsetY * frontOffset
|
||||
val spawnZ = TMM.blockPos.z + 0.5 + facingDirection.offsetZ * frontOffset
|
||||
|
||||
// Create the ItemEntity at the center of the block
|
||||
val itemEntity = ItemEntity( context.world, spawnX, spawnY, spawnZ, it)
|
||||
|
||||
// Create the ItemEntity
|
||||
itemEntity.setVelocity(0.0, 0.0, 0.0)
|
||||
|
||||
// Add the ItemEntity to the world
|
||||
context.world.spawnEntity(itemEntity)
|
||||
}
|
||||
TMM.tmmInventory.items?.clear()
|
||||
}*/
|
||||
|
||||
// todo change the color of the disk in the TMM
|
||||
// todo remove 1 from the stack in the player's hand if not in creative
|
||||
if (!context.player?.isCreative!!) {
|
||||
context.player?.getStackInHand(context.hand)?.decrement(1)
|
||||
}
|
||||
|
||||
TMM.markDirty()
|
||||
TMM.world?.updateListeners(TMM.blockPos, TMM.cachedState, TMM.cachedState, Block.NOTIFY_LISTENERS)
|
||||
}
|
||||
|
||||
/*val type = ElementalTypes.getOrException(getMoveNbt(context.stack)!!.type)
|
||||
context.player!!.giveItemStack(Registries.ITEM.get(type.typeGem).defaultStack)*/
|
||||
return super.useOnBlock(context)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.item.group
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.item.ItemConvertible
|
||||
import net.minecraft.item.ItemGroup
|
||||
@@ -35,6 +36,7 @@ object CobblemonItemGroups {
|
||||
@JvmStatic val CONSUMABLES_KEY = this.create("consumables", this::consumableEntries) { ItemStack(CobblemonItems.ROASTED_LEEK) }
|
||||
@JvmStatic val HELD_ITEMS_KEY = this.create("held_item", this::heldItemEntries) { ItemStack(CobblemonItems.EXP_SHARE) }
|
||||
@JvmStatic val EVOLUTION_ITEMS_KEY = this.create("evolution_item", this::evolutionItemEntries) { ItemStack(CobblemonItems.BLACK_AUGURITE) }
|
||||
@JvmStatic val TECHNICAL_MACHINES_KEY = this.create("technical_machines", this::technicalMachineEntries) { ItemStack(CobblemonItems.BLANK_TM) }
|
||||
|
||||
@JvmStatic val BLOCKS get() = Registries.ITEM_GROUP.get(BLOCKS_KEY)
|
||||
@JvmStatic val POKEBALLS get() = Registries.ITEM_GROUP.get(POKEBALLS_KEY)
|
||||
@@ -43,6 +45,7 @@ object CobblemonItemGroups {
|
||||
@JvmStatic val CONSUMABLES get() = Registries.ITEM_GROUP.get(CONSUMABLES_KEY)
|
||||
@JvmStatic val HELD_ITEMS get() = Registries.ITEM_GROUP.get(HELD_ITEMS_KEY)
|
||||
@JvmStatic val EVOLUTION_ITEMS get() = Registries.ITEM_GROUP.get(EVOLUTION_ITEMS_KEY)
|
||||
@JvmStatic val TECHNICAL_MACHINES get() = Registries.ITEM_GROUP.get(TECHNICAL_MACHINES_KEY)
|
||||
|
||||
@JvmStatic val FOOD_INJECTIONS = this.inject(RegistryKey.of(Registries.ITEM_GROUP.key, Identifier("food_and_drinks")), this::foodInjections)
|
||||
@JvmStatic val TOOLS_AND_UTILITIES_INJECTIONS = this.inject(RegistryKey.of(Registries.ITEM_GROUP.key, Identifier("tools_and_utilities")), this::toolsAndUtilitiesInjections)
|
||||
@@ -76,6 +79,16 @@ object CobblemonItemGroups {
|
||||
return consumer
|
||||
}
|
||||
|
||||
private fun technicalMachineEntries(displayContext: DisplayContext, entries: Entries) {
|
||||
entries.add(CobblemonItems.BLANK_TM)
|
||||
for (tm in TechnicalMachines.tmMap) {
|
||||
val item = CobblemonItems.TECHNICAL_MACHINE.setNbt(
|
||||
CobblemonItems.TECHNICAL_MACHINE.defaultStack,
|
||||
tm.key.toString())
|
||||
entries.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun agricultureEntries(displayContext: DisplayContext, entries: Entries) {
|
||||
entries.add(CobblemonItems.MEDICINAL_LEEK)
|
||||
entries.add(CobblemonItems.BIG_ROOT)
|
||||
@@ -217,8 +230,10 @@ object CobblemonItemGroups {
|
||||
entries.add(CobblemonItems.FOSSIL_ANALYZER)
|
||||
entries.add(CobblemonItems.MONITOR)
|
||||
entries.add(CobblemonItems.PC)
|
||||
entries.add(CobblemonItems.TM_MACHINE)
|
||||
entries.add(CobblemonItems.HEALING_MACHINE)
|
||||
entries.add(CobblemonItems.PASTURE)
|
||||
entries.add(CobblemonItems.MOD_STATION)
|
||||
|
||||
entries.add(CobblemonItems.GILDED_CHEST)
|
||||
entries.add(CobblemonItems.YELLOW_GILDED_CHEST)
|
||||
@@ -231,6 +246,7 @@ object CobblemonItemGroups {
|
||||
entries.add(CobblemonItems.RELIC_COIN_POUCH)
|
||||
entries.add(CobblemonItems.RELIC_COIN_SACK)
|
||||
|
||||
entries.add(CobblemonItems.DISPLAY_CASE)
|
||||
entries.add(CobblemonItems.APRICORN_LOG)
|
||||
entries.add(CobblemonItems.APRICORN_WOOD)
|
||||
entries.add(CobblemonItems.STRIPPED_APRICORN_LOG)
|
||||
@@ -363,6 +379,9 @@ object CobblemonItemGroups {
|
||||
entries.add(CobblemonItems.JOLLY_MINT)
|
||||
entries.add(CobblemonItems.NAIVE_MINT)
|
||||
entries.add(CobblemonItems.SERIOUS_MINT)
|
||||
|
||||
entries.add(CobblemonItems.ABILITY_CAPSULE)
|
||||
entries.add(CobblemonItems.ABILITY_PATCH)
|
||||
}
|
||||
|
||||
private fun evolutionItemEntries(displayContext: DisplayContext, entries: Entries) {
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.item.interactive.ability
|
||||
|
||||
import com.cobblemon.mod.common.api.abilities.PotentialAbility
|
||||
import com.cobblemon.mod.common.api.interaction.PokemonEntityInteraction
|
||||
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.CobblemonItem
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
open class AbilityChangeItem<T : PotentialAbility>(val changer: AbilityChanger<T>) : CobblemonItem(Settings()), PokemonEntityInteraction {
|
||||
|
||||
override val accepted: Set<PokemonEntityInteraction.Ownership> = setOf(PokemonEntityInteraction.Ownership.OWNER)
|
||||
|
||||
override fun processInteraction(player: ServerPlayerEntity, entity: PokemonEntity, stack: ItemStack): Boolean {
|
||||
if (this.changer.performChange(entity.pokemon)) {
|
||||
this.consumeItem(player, stack)
|
||||
val feedback = lang(
|
||||
"ability_changer.changed",
|
||||
entity.pokemon.getDisplayName(),
|
||||
entity.pokemon.ability.displayName.asTranslated()
|
||||
)
|
||||
player.sendMessage(feedback)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.item.interactive.ability
|
||||
|
||||
import com.cobblemon.mod.common.api.abilities.*
|
||||
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
|
||||
// TODO Polish me down the line when we actually release the item in the mod, we need a way to select the ability
|
||||
open class AbilityTypeChanger<T : PotentialAbility>(
|
||||
override val type: PotentialAbilityType<T>,
|
||||
private val supportsChangingFrom: (other: PotentialAbilityType<*>?) -> Boolean
|
||||
) : AbilityChanger<T> {
|
||||
|
||||
override fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate> {
|
||||
val ofType = pokemon.form.abilities.filter { it.type == this.type }
|
||||
return ofType.filter { it.template != pokemon.ability.template }
|
||||
.map { it.template }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun performChange(pokemon: Pokemon): Boolean {
|
||||
val currentType = this.findCurrent(pokemon)
|
||||
if (!this.canChangeFrom(currentType)) {
|
||||
return false
|
||||
}
|
||||
val possible = this.queryPossible(pokemon)
|
||||
val picked = possible.randomOrNull() ?: return false
|
||||
val old = pokemon.ability.template
|
||||
pokemon.updateAbility(picked.create(forced = false))
|
||||
return pokemon.ability.template != old
|
||||
}
|
||||
|
||||
override fun canChangeFrom(type: PotentialAbilityType<*>?): Boolean = this.supportsChangingFrom(type)
|
||||
|
||||
private fun findCurrent(pokemon: Pokemon): PotentialAbilityType<*>? {
|
||||
if (pokemon.ability.forced) {
|
||||
return null
|
||||
}
|
||||
return pokemon.form.abilities.firstOrNull { it.template == pokemon.ability.template }?.type
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.ui
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Tells the server to attempt crafting a Blank TM using the [TMBlock]
|
||||
*
|
||||
* Handled by [CraftBlankTMPacketHandler]
|
||||
*
|
||||
* @author whatsy
|
||||
*/
|
||||
class CraftBlankTMPacket(
|
||||
val ingredient: ItemStack
|
||||
): NetworkPacket<CraftBlankTMPacket> {
|
||||
override val id = ID
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeItemStack(ingredient)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("craft_blank_tm")
|
||||
|
||||
fun decode(buffer: PacketByteBuf) = CraftBlankTMPacket(
|
||||
buffer.readItemStack()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.ui
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachine
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Tells the server to attempt crafting a [TechnicalMachineItem] using the [TMBlock]
|
||||
*
|
||||
* Handled by [CraftTMPacketHandler]
|
||||
*
|
||||
* @author whatsy
|
||||
*/
|
||||
class CraftTMPacket(
|
||||
val tm: TechnicalMachine,
|
||||
val disc: ItemStack,
|
||||
val gem: ItemStack,
|
||||
val ingredient: ItemStack
|
||||
): NetworkPacket<CraftTMPacket> {
|
||||
override val id = ID
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeIdentifier(tm.id())
|
||||
buffer.writeItemStack(disc)
|
||||
buffer.writeItemStack(gem)
|
||||
buffer.writeItemStack(ingredient)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("craft_tm")
|
||||
|
||||
fun decode(buffer: PacketByteBuf) = CraftTMPacket(
|
||||
TechnicalMachines.tmMap[buffer.readIdentifier()]!!,
|
||||
buffer.readItemStack(), buffer.readItemStack(), buffer.readItemStack()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.ui
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class OpenTMMPacket: NetworkPacket<OpenTMMPacket> {
|
||||
override val id = cobblemonResource("open_tm")
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+63
-3
@@ -12,7 +12,10 @@ import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.ast.NumberExpression
|
||||
import com.cobblemon.mod.common.api.snowstorm.*
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.normalizeToArray
|
||||
import com.cobblemon.mod.common.util.singularToPluralList
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonPrimitive
|
||||
@@ -38,6 +41,7 @@ object SnowstormParticleReader {
|
||||
val emitterShapeDiscJson = componentsJson.get("minecraft:emitter_shape_disc")?.asJsonObject
|
||||
val emitterShapeBoxJson = componentsJson.get("minecraft:emitter_shape_box")?.asJsonObject
|
||||
val emitterShapeEntityBoundingBoxJson = componentsJson.get("minecraft:emitter_shape_entity_aabb")?.asJsonObject
|
||||
val emitterLifetimeEventsJson = componentsJson.get("minecraft:emitter_lifetime_events")?.asJsonObject
|
||||
val dynamicMotionJson = componentsJson.get("minecraft:particle_motion_dynamic")?.asJsonObject
|
||||
val particleAppearanceJson = componentsJson.get("minecraft:particle_appearance_billboard").asJsonObject
|
||||
val sizeJson = particleAppearanceJson.get("size")?.asJsonArray
|
||||
@@ -50,6 +54,7 @@ object SnowstormParticleReader {
|
||||
val colourJson = tintingJson?.get("color")
|
||||
val collisionJson = componentsJson.get("minecraft:particle_motion_collision")?.asJsonObject
|
||||
val spaceJson = componentsJson.get("minecraft:emitter_local_space")?.asJsonObject
|
||||
val particleLifetimeEventsJson = componentsJson.get("minecraft:particle_lifetime_events")?.asJsonObject
|
||||
|
||||
val id = Identifier(descJson.get("identifier").asString)
|
||||
val maxAge = particleLifetimeJson?.get("max_lifetime")?.asString?.asExpression() ?: 0.0.asExpression()
|
||||
@@ -107,6 +112,24 @@ object SnowstormParticleReader {
|
||||
else -> TODO("Unrecognized curve type was used")
|
||||
}
|
||||
}
|
||||
val events = mutableMapOf<String, ParticleEvent>()
|
||||
val eventJson = effectJson.get("events")?.asJsonObject
|
||||
eventJson?.entrySet()?.forEach { (name, event) ->
|
||||
val eventObj = event.asJsonObject
|
||||
val particleEffect = eventObj.get("particle_effect")?.asJsonObject?.let {
|
||||
val effect = it.get("effect").asString.asIdentifierDefaultingNamespace()
|
||||
val type = it.get("type").asString
|
||||
val preEffectExpression = it.get("pre_effect_expression")?.asString?.asExpressionLike()
|
||||
val typeEnum = EventParticleEffect.EventParticleType.valueOf(type.uppercase())
|
||||
EventParticleEffect(effect, typeEnum, preEffectExpression)
|
||||
}
|
||||
val soundEffect = eventObj.get("sound_effect")?.asJsonObject?.let {
|
||||
val eventName = it.get("event_name").asString.asIdentifierDefaultingNamespace()
|
||||
EventSoundEffect(eventName)
|
||||
}
|
||||
val expression = eventObj.get("expression")?.asString?.asExpressionLike()
|
||||
events[name] = ParticleEvent(particleEffect, soundEffect, expression)
|
||||
}
|
||||
val emitterStartExpressions = (emitterInitializationJson["creation_expression"]?.asString ?: "").split(";").filter { it.isNotEmpty() }.map { it.asExpression() }
|
||||
val emitterUpdateExpressions = (emitterInitializationJson["per_update_expression"]?.asString ?: "").split(";").filter { it.isNotEmpty() }.map { it.asExpression() }
|
||||
val particleUpdateExpressions = (particleInitializationJson["per_update_expression"]?.asString ?: "").split(";").filter { it.isNotEmpty() }.map { it.asExpression() }
|
||||
@@ -128,7 +151,7 @@ object SnowstormParticleReader {
|
||||
} else if (emitterLifetimeLoopingJson != null) {
|
||||
LoopingEmitterLifetime(
|
||||
activeTime = (emitterLifetimeLoopingJson.get("active_time")?.asString ?: "").asExpression(),
|
||||
sleepTime = (emitterLifetimeLoopingJson.get("sleep_time")?.asString ?: "").asExpression()
|
||||
sleepTime = (emitterLifetimeLoopingJson.get("sleep_time")?.asString ?: "0.0").asExpression()
|
||||
)
|
||||
} else if (emitterLifetimeExpressionJson != null) {
|
||||
ExpressionEmitterLifetime(
|
||||
@@ -324,14 +347,48 @@ object SnowstormParticleReader {
|
||||
)
|
||||
} ?: ParticleSpace()
|
||||
|
||||
val particleEventSet = particleLifetimeEventsJson?.let {
|
||||
val creationEvents = it.get("creation_event")?.normalizeToArray()?.map { SimpleEventTrigger(it.asString) }?.toMutableList() ?: mutableListOf()
|
||||
val expirationEvents = it.get("expiration_event")?.normalizeToArray()?.map { SimpleEventTrigger(it.asString) }?.toMutableList() ?: mutableListOf()
|
||||
val timeline = it.get("timeline")?.asJsonObject?.entrySet()?.map { (key, value) ->
|
||||
key.toDouble() to value.normalizeToArray().map { it.asString }.toMutableList()
|
||||
}?.toMap()?.toMutableMap() ?: mutableMapOf()
|
||||
BedrockParticle.EventSet(
|
||||
creationEvents = creationEvents,
|
||||
expirationEvents = expirationEvents,
|
||||
timeline = EventTriggerTimeline(timeline)
|
||||
)
|
||||
} ?: BedrockParticle.EventSet(mutableListOf(), mutableListOf(), EventTriggerTimeline(mutableMapOf()))
|
||||
|
||||
val emitterCreationEvents = emitterLifetimeEventsJson?.get("creation_event")?.normalizeToArray()?.map { SimpleEventTrigger(it.asString) }?.toMutableList() ?: mutableListOf()
|
||||
val emitterExpirationEvents = emitterLifetimeEventsJson?.get("expiration_event")?.normalizeToArray()?.map { SimpleEventTrigger(it.asString) }?.toMutableList() ?: mutableListOf()
|
||||
val emitterTravelDistanceEvents = emitterLifetimeEventsJson?.get("travel_distance_events")?.asJsonObject?.entrySet()?.map { (key, value) ->
|
||||
key.toDouble() to value.normalizeToArray().map { it.asString }.toMutableList()
|
||||
}?.toMap()?.toMutableMap() ?: mutableMapOf()
|
||||
val emitterLoopingTravelDistanceEvents = emitterLifetimeEventsJson?.get("looping_travel_distance_events")?.asJsonArray?.map {
|
||||
val obj = it.asJsonObject
|
||||
val distance = obj.get("distance").asDouble
|
||||
val events = obj.get("events").normalizeToArray().map { it.asString }.toMutableList()
|
||||
LoopingTravelDistanceEventTrigger(distance, events)
|
||||
}?.toMutableList() ?: mutableListOf()
|
||||
val emitterEventTimeline = EventTriggerTimeline(emitterLifetimeEventsJson?.get("timeline")?.asJsonObject?.entrySet()?.map { (key, value) ->
|
||||
key.toDouble() to value.normalizeToArray().map { it.asString }.toMutableList()
|
||||
}?.toMap()?.toMutableMap() ?: mutableMapOf())
|
||||
|
||||
return BedrockParticleEffect(
|
||||
id = id,
|
||||
events = events,
|
||||
emitter = BedrockParticleEmitter(
|
||||
startExpressions = emitterStartExpressions.toMutableList(),
|
||||
updateExpressions = emitterUpdateExpressions.toMutableList(),
|
||||
rate = rate,
|
||||
shape = shape,
|
||||
lifetime = lifetime
|
||||
lifetime = lifetime,
|
||||
eventTimeline = emitterEventTimeline,
|
||||
creationEvents = emitterCreationEvents,
|
||||
expirationEvents = emitterExpirationEvents,
|
||||
travelDistanceEvents = EventTriggerTimeline(emitterTravelDistanceEvents),
|
||||
loopingTravelDistanceEvents = emitterLoopingTravelDistanceEvents
|
||||
),
|
||||
curves = curves.toMutableList(),
|
||||
particle = BedrockParticle(
|
||||
@@ -350,7 +407,10 @@ object SnowstormParticleReader {
|
||||
cameraMode = cameraMode,
|
||||
collision = collision,
|
||||
environmentLighting = environmentLighting,
|
||||
tinting = tinting
|
||||
tinting = tinting,
|
||||
creationEvents = particleEventSet.creationEvents,
|
||||
expirationEvents = particleEventSet.expirationEvents,
|
||||
timeline = particleEventSet.timeline
|
||||
),
|
||||
space = space
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.particle.ParticleType
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
class SnowstormParticleType : ParticleType<SnowstormParticleEffect>(false, SnowstormParticleEffect.PARAMETERS_FACTORY) {
|
||||
class SnowstormParticleType : ParticleType<SnowstormParticleEffect>(true, SnowstormParticleEffect.PARAMETERS_FACTORY) {
|
||||
companion object {
|
||||
val CODEC: Codec<SnowstormParticleEffect> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.abilities.Ability
|
||||
import com.cobblemon.mod.common.api.abilities.AbilityPool
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.FRIENDSHIP_UPDATED
|
||||
@@ -122,8 +123,8 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
updateAspects()
|
||||
updateForm()
|
||||
checkGender()
|
||||
checkAbility()
|
||||
updateHP(quotient)
|
||||
this.attemptAbilityUpdate()
|
||||
_species.emit(value)
|
||||
}
|
||||
|
||||
@@ -138,9 +139,9 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
// Evo proxy is already cleared on species update but the form may be changed by itself, this is fine and no unnecessary packets will be sent out
|
||||
this.evolutionProxy.current().clear()
|
||||
findAndLearnFormChangeMoves()
|
||||
updateHP(quotient)
|
||||
checkGender()
|
||||
checkAbility()
|
||||
updateHP(quotient)
|
||||
this.attemptAbilityUpdate()
|
||||
_form.emit(value)
|
||||
}
|
||||
|
||||
@@ -375,8 +376,9 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
*/
|
||||
val benchedMoves = BenchedMoves()
|
||||
|
||||
var ability: Ability = Abilities.DUMMY.create()
|
||||
set(value) {
|
||||
var ability: Ability = Abilities.DUMMY.create(false)
|
||||
// Keep internal for DTO & sync packet
|
||||
internal set(value) {
|
||||
if (field != value) {
|
||||
_ability.emit(value)
|
||||
}
|
||||
@@ -777,10 +779,8 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
evs.loadFromNBT(nbt.getCompound(DataKeys.POKEMON_EVS))
|
||||
moveSet.loadFromNBT(nbt)
|
||||
scaleModifier = nbt.getFloat(DataKeys.POKEMON_SCALE_MODIFIER)
|
||||
val abilityNBT = nbt.getCompound(DataKeys.POKEMON_ABILITY) ?: NbtCompound()
|
||||
val abilityName = abilityNBT.getString(DataKeys.POKEMON_ABILITY_NAME).takeIf { it.isNotEmpty() } ?: "runaway"
|
||||
if (abilityName != "dummy") {
|
||||
ability = Abilities.getOrException(abilityName).create(abilityNBT)
|
||||
if (nbt.contains(DataKeys.POKEMON_ABILITY, COMPOUND_TYPE.toInt())) {
|
||||
this.ability.loadFromNBT(nbt.getCompound(DataKeys.POKEMON_ABILITY))
|
||||
}
|
||||
shiny = nbt.getBoolean(DataKeys.POKEMON_SHINY)
|
||||
if (nbt.contains(DataKeys.POKEMON_STATE)) {
|
||||
@@ -813,7 +813,6 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
}
|
||||
updateAspects()
|
||||
updateForm() // If saved with an incorrect form, readjust on load
|
||||
checkAbility()
|
||||
nbt.get(DataKeys.POKEMON_EVOLUTIONS)?.let { tag -> this.evolutionProxy.loadFromNBT(tag) }
|
||||
if (nbt.contains(DataKeys.HELD_ITEM)) {
|
||||
this.heldItem = ItemStack.fromNbt(nbt.getCompound(DataKeys.HELD_ITEM))
|
||||
@@ -906,10 +905,8 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
evs.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_EVS))
|
||||
moveSet.loadFromJSON(json.get(DataKeys.POKEMON_MOVESET).asJsonObject)
|
||||
scaleModifier = json.get(DataKeys.POKEMON_SCALE_MODIFIER).asFloat
|
||||
val abilityJSON = json.get(DataKeys.POKEMON_ABILITY)?.asJsonObject ?: JsonObject()
|
||||
val abilityName = abilityJSON.get(DataKeys.POKEMON_ABILITY_NAME)?.asString
|
||||
if (abilityName != "dummy" && abilityName != null) {
|
||||
ability = Abilities.getOrException(abilityName).create(abilityJSON)
|
||||
if (json.has(DataKeys.POKEMON_ABILITY) && json.get(DataKeys.POKEMON_ABILITY).isJsonObject) {
|
||||
this.ability.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_ABILITY))
|
||||
}
|
||||
shiny = json.get(DataKeys.POKEMON_SHINY).asBoolean
|
||||
if (json.has(DataKeys.POKEMON_STATE)) {
|
||||
@@ -942,7 +939,6 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
}
|
||||
updateAspects()
|
||||
updateForm() // If saved with an incorrect form, readjust on load
|
||||
checkAbility()
|
||||
json.get(DataKeys.POKEMON_EVOLUTIONS)?.let { this.evolutionProxy.loadFromJson(it) }
|
||||
if (json.has(DataKeys.HELD_ITEM)) {
|
||||
ItemStack.CODEC.decode(JsonOps.INSTANCE, json.get(DataKeys.HELD_ITEM)).result().ifPresent {
|
||||
@@ -1154,7 +1150,6 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
// Force the setter to initialize it
|
||||
species = species
|
||||
checkGender()
|
||||
checkAbility()
|
||||
// This should only be a thing once we have moveset control in properties until then a creation should require a moveset init.
|
||||
/*
|
||||
if (pokemon.moveSet.none { it != null }) {
|
||||
@@ -1191,63 +1186,104 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
}
|
||||
}
|
||||
|
||||
fun checkAbility() {
|
||||
if (isClient) {
|
||||
/**
|
||||
* Rolls an ability for this [Pokemon] using [AbilityPool.select]. This will override any current [Ability.forced].
|
||||
*
|
||||
* This operation does nothing on the client side.
|
||||
*
|
||||
* @return The newly assigned [Ability] or the existing one if called on the client.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open fun rollAbility(): Ability {
|
||||
if (this.isClient) {
|
||||
return this.ability
|
||||
}
|
||||
val (ability, _) = this.form.abilities.select(this.species, this.aspects)
|
||||
return this.updateAbility(ability.template.create(false))
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the given [ability] to this Pokémon.
|
||||
* It will also handle all the coordinate storing for proper synchronization across evolutions or other [species]/[form] updates.
|
||||
* This coordinate storage is skipped for [Ability.forced].
|
||||
*
|
||||
* This operation does nothing on the client side.
|
||||
*
|
||||
* @param ability The ability getting assigned
|
||||
* @return The resulting [Ability].
|
||||
*/
|
||||
open fun updateAbility(ability: Ability): Ability {
|
||||
if (this.isClient) {
|
||||
return this.ability
|
||||
}
|
||||
this.ability = if (ability.forced) ability else this.attachAbilityCoordinate(ability)
|
||||
return this.ability
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to update the [ability] on [species]/[form] change.
|
||||
*
|
||||
* This operation does nothing on the client side or [Ability.forced] is true.
|
||||
*
|
||||
* If the ability is [Abilities.DUMMY] it invokes [rollAbility] instead.
|
||||
*/
|
||||
protected open fun attemptAbilityUpdate() {
|
||||
if (this.isClient || this.ability.forced) {
|
||||
return
|
||||
}
|
||||
val hasForcedAbility = this.ability.forced
|
||||
val hasLegalAbility = this.form.abilities.mapping.values.any { list ->
|
||||
list.any { potential ->
|
||||
potential.template == this.ability.template
|
||||
if (this.ability.template == Abilities.DUMMY) {
|
||||
this.rollAbility()
|
||||
return
|
||||
}
|
||||
val potentials = this.form.abilities.mapping[this.ability.priority]
|
||||
// Step 1 try to get the same exact index and priority
|
||||
var potential = potentials?.getOrNull(this.ability.index)
|
||||
// Step 2 Disclaimer I don't really know the best course of action here
|
||||
// For default assets this just means "pick the only other regular ability" and hidden abilities are always just 1 so...
|
||||
// Sorry 3rd party please don't make really weird stats :(
|
||||
if (potential == null && potentials != null) {
|
||||
for (i in this.ability.index.coerceAtLeast(0) downTo 0) {
|
||||
val indexed = potentials.getOrNull(i)
|
||||
if (indexed != null) {
|
||||
potential = indexed
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
val isDummy = this.ability.template == Abilities.DUMMY
|
||||
if (potential != null) {
|
||||
// Keep our known index and priority, this was the original valid state after all
|
||||
this.ability = potential.template.create(false).apply {
|
||||
this.index = this@Pokemon.ability.index
|
||||
this.priority = this@Pokemon.ability.priority
|
||||
}
|
||||
return
|
||||
}
|
||||
// End of the road kiddo it didn't have to go down like this...
|
||||
this.rollAbility()
|
||||
}
|
||||
|
||||
// EXPLANATION
|
||||
// This is used to keep the same intended ability between evolution stages
|
||||
// Between species updates if an original indexed data is attached it will be honored next time that it's possible
|
||||
// This is still not a perfect system but it will now only break if players are constantly adding/removing data edits which at that point it's on them
|
||||
if (isDummy || (!hasLegalAbility && !hasForcedAbility)) {
|
||||
var needsSelection = true
|
||||
var needsUpdate = true
|
||||
if (this.ability.index == -1 && !isDummy) {
|
||||
base@ for ((priority, list) in this.form.abilities.mapping) {
|
||||
for ((index, potential) in list.withIndex()) {
|
||||
if (potential.template == this.ability.template) {
|
||||
this.ability.priority = priority
|
||||
this.ability.index = index
|
||||
needsUpdate = false
|
||||
needsSelection = false
|
||||
break@base
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.ability.index >= 0) {
|
||||
needsUpdate = false
|
||||
val potentialAbility = this.form.abilities.mapping[this.ability.priority]?.getOrNull(this.ability.index)
|
||||
if (potentialAbility != null) {
|
||||
// Don't update index nor priority
|
||||
val newAbility = potentialAbility.template.create()
|
||||
newAbility.index = this.ability.index
|
||||
newAbility.priority = this.ability.priority
|
||||
this.ability = newAbility
|
||||
needsSelection = false
|
||||
}
|
||||
}
|
||||
if (needsSelection) {
|
||||
val (ability, priority) = this.form.abilities.select(this.species, this.aspects)
|
||||
ability.index = this.ability.index
|
||||
ability.priority = this.ability.priority
|
||||
this.ability = ability
|
||||
if (needsUpdate) {
|
||||
// This may sometimes happen when both species and form update as well as if AbilityPool#select throws a graceful exception, we return to prevent a crash.
|
||||
val mapped = this.form.abilities.mapping[priority] ?: return
|
||||
this.ability.index = mapped.indexOfFirst { potential -> potential.template == this.ability.template }
|
||||
this.ability.priority = priority
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Finds the position of the current ability in the Pokémon data and attaches the coordinates.
|
||||
* This is done in an attempt to synchronize correctly between [species]/[form] changes.
|
||||
*
|
||||
* This operation does nothing on the client side, if [Ability.forced] is true or if the ability is [Abilities.DUMMY].
|
||||
*
|
||||
* @param ability The [Ability] being queried.
|
||||
* @return It can return [ability] with the [Ability.priority] & [Ability.index] mapped, the [Ability.forced] set to true if illegal or itself if no operation is performed.
|
||||
*/
|
||||
protected open fun attachAbilityCoordinate(ability: Ability): Ability {
|
||||
if (this.isClient || ability.forced || ability.template == Abilities.DUMMY) {
|
||||
return ability
|
||||
}
|
||||
val found = this.form.abilities.firstOrNull { potential -> potential.template == ability.template }
|
||||
?: return ability.apply { this.forced = true }
|
||||
val index = this.form.abilities.mapping[found.priority]
|
||||
?.indexOf(found)
|
||||
?.takeIf { it != -1 }
|
||||
?: return ability.apply { this.forced = true }
|
||||
ability.priority = found.priority
|
||||
ability.index = index
|
||||
return ability
|
||||
}
|
||||
|
||||
fun initializeMoveset(preferLatest: Boolean = true) {
|
||||
@@ -1362,11 +1398,18 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
|
||||
val newLevelUpMoves = form.moves.getLevelUpMovesUpTo(newLevel)
|
||||
val differences = (newLevelUpMoves - previousLevelUpMoves).filter { this.moveSet.none { move -> move.template == it } }.toMutableSet()
|
||||
differences.forEach {
|
||||
if (moveSet.hasSpace()) {
|
||||
moveSet.add(it.create())
|
||||
}
|
||||
}
|
||||
// differences.forEach {
|
||||
// TechnicalMachines.tmsToCheckOnLevelUp.entries.filter { entry ->
|
||||
// entry.value.moveName == it.name
|
||||
// }.forEach { entry ->
|
||||
// val player = this.getOwnerPlayer() as PlayerEntity
|
||||
// Cobblemon.playerData.get(player).tmSet.add(entry.key)
|
||||
// player.sendMessage(lang("tms.unlock_tm", lang("move." + entry.value.moveName)))
|
||||
// }
|
||||
// if (moveSet.hasSpace()) {
|
||||
// moveSet.add(it.create())
|
||||
// }
|
||||
// }
|
||||
|
||||
CobblemonEvents.EXPERIENCE_GAINED_EVENT_POST.post(
|
||||
ExperienceGainedPostEvent(this, source, appliedXP, oldLevel, newLevel, differences),
|
||||
|
||||
+4
-4
@@ -30,10 +30,10 @@ object HiddenAbilityPropertyType : CustomPokemonPropertyType<HiddenAbilityProper
|
||||
class HiddenAbilityProperty : CustomPokemonProperty {
|
||||
override fun asString() = "hiddenability"
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
val hiddenAbilities = pokemon.form.abilities.mapping.flatMap { it.value }.filter { it.type == HiddenAbilityType }
|
||||
if (hiddenAbilities.isNotEmpty()) {
|
||||
pokemon.ability = hiddenAbilities.random().template.create()
|
||||
}
|
||||
val possible = pokemon.form.abilities.mapping.flatMap { it.value }
|
||||
.filter { it.type == HiddenAbilityType }
|
||||
val picked = possible.randomOrNull() ?: return
|
||||
pokemon.updateAbility(picked.template.create(false))
|
||||
}
|
||||
|
||||
override fun matches(pokemon: Pokemon) = pokemon.form.abilities.mapping
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.cobblemon.mod.common.tms.obtain
|
||||
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethod
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
* An [ObtainMethod] that never triggers.
|
||||
* Useful since the obtainMethods field in TM JSONs cannot be blank.
|
||||
*
|
||||
* @author whatsy
|
||||
*/
|
||||
class ImpossibleObtainMethod : ObtainMethod {
|
||||
|
||||
override val passive = false
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("impossible")
|
||||
}
|
||||
|
||||
override fun matches(player: ServerPlayerEntity) = false
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.cobblemon.mod.common.tms.obtain
|
||||
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethod
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
* An [ObtainMethod] that unlocks immediately.
|
||||
* Used for TMs that aren't unlocked and are available immediately.
|
||||
* TMs with this [ObtainMethod] will not display an unlock text.
|
||||
*
|
||||
* @author whatsy
|
||||
*/
|
||||
class NoneObtainMethod : ObtainMethod {
|
||||
|
||||
override val passive = true
|
||||
|
||||
override fun matches(player: ServerPlayerEntity) = true
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("none")
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.cobblemon.mod.common.tms.obtain
|
||||
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethod
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* An [ObtainMethod] that triggers when the player has an advancement.
|
||||
*/
|
||||
class PlayerHasAdvancementObtainMethod : ObtainMethod {
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("advancement")
|
||||
}
|
||||
|
||||
override val passive = true
|
||||
val advancement: Identifier? = null
|
||||
|
||||
override fun matches(player: ServerPlayerEntity): Boolean {
|
||||
if (advancement == null) return false
|
||||
|
||||
for (entry in player.advancementTracker.progress) {
|
||||
if (entry.key.id == advancement && entry.value.isDone) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.cobblemon.mod.common.tms.obtain
|
||||
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethod
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
* An [ObtainMethod] that checks the player's Y level.
|
||||
*
|
||||
* @param yLevel The Y level to check for.
|
||||
* @param operator The operator to use. Can be "equals", "less", or "greater".
|
||||
* @author whatsy
|
||||
*/
|
||||
class PlayerYObtainMethod(val yLevel: Int, val operator: String) : ObtainMethod {
|
||||
|
||||
override val passive = true
|
||||
override fun matches(player: ServerPlayerEntity): Boolean {
|
||||
when (operator) {
|
||||
"equals" -> return (player.y.toInt() == yLevel)
|
||||
"less" -> return (yLevel < player.y)
|
||||
"greater" -> return (yLevel > player.y)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("y_level")
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.tms.obtain
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethod
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
* An [ObtainMethod] that triggers when a [Pokemon] in the player's party knows a certain [Move]
|
||||
*
|
||||
* @author whatsy
|
||||
*/
|
||||
class PokemonHasMoveObtainMethod : ObtainMethod {
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("pokemon_knows")
|
||||
}
|
||||
|
||||
@SerializedName("move")
|
||||
val moveId: String = "splash"
|
||||
override val passive = true
|
||||
|
||||
override fun matches(player: ServerPlayerEntity): Boolean {
|
||||
player.party().forEach {
|
||||
it.allAccessibleMoves.forEach {
|
||||
if (it.equals(Moves.getByName(moveId))) return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,14 @@ fun JsonObject.singularToPluralList(rootName: String, pluralName: String = "${ro
|
||||
get(pluralName).asJsonArray.add(get(rootName))
|
||||
remove(rootName)
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonElement.normalizeToArray(): JsonArray {
|
||||
if (this is JsonArray) {
|
||||
return this
|
||||
} else {
|
||||
val array = JsonArray()
|
||||
array.add(this)
|
||||
return array
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.cobblemon.mod.common.util.adapters
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethod
|
||||
import com.cobblemon.mod.common.api.tms.ObtainMethodAdapter
|
||||
import com.cobblemon.mod.common.tms.obtain.*
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import net.minecraft.util.Identifier
|
||||
import java.lang.reflect.Type
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
object CobblemonObtainMethodAdapter : ObtainMethodAdapter {
|
||||
private const val VARIANT = "variant"
|
||||
private val types = hashMapOf<String, KClass<out ObtainMethod>>()
|
||||
|
||||
init {
|
||||
this.register(PokemonHasMoveObtainMethod::class, PokemonHasMoveObtainMethod.ID)
|
||||
this.register(ImpossibleObtainMethod::class, ImpossibleObtainMethod.ID)
|
||||
this.register(PlayerHasAdvancementObtainMethod::class, PlayerHasAdvancementObtainMethod.ID)
|
||||
this.register(NoneObtainMethod::class, NoneObtainMethod.ID)
|
||||
this.register(PlayerYObtainMethod::class, PlayerYObtainMethod.ID)
|
||||
}
|
||||
|
||||
override fun register(type: KClass<out ObtainMethod>, identifier: Identifier) {
|
||||
val existing = this.types.put(identifier.toString(), type)
|
||||
if (existing != null) {
|
||||
Cobblemon.LOGGER.debug("Replaced {} under ID {} with {} in the {}", existing::class.qualifiedName, identifier.toString(), type.qualifiedName, this::class.qualifiedName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun deserialize(jElement: JsonElement, type: Type, context: JsonDeserializationContext): ObtainMethod {
|
||||
val json = jElement.asJsonObject
|
||||
val variant = json.get(VARIANT).asString.lowercase()
|
||||
val registeredType = this.types[variant] ?: throw IllegalArgumentException("Cannot resolve type for variant $variant")
|
||||
return context.deserialize(json, registeredType.java)
|
||||
}
|
||||
|
||||
override fun serialize(method: ObtainMethod, type: Type, context: JsonSerializationContext): JsonElement {
|
||||
val json = context.serialize(method).asJsonObject
|
||||
val variant = this.types.entries.find { it.value == method::class }?.key ?: throw IllegalArgumentException("Cannot resolve variant for type ${method::class.qualifiedName}")
|
||||
json.addProperty(VARIANT, variant)
|
||||
return json
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"format_version": "1.12.0",
|
||||
"minecraft:geometry": [
|
||||
{
|
||||
"description": {
|
||||
"identifier": "geometry.unknown",
|
||||
"texture_width": 64,
|
||||
"texture_height": 64,
|
||||
"visible_bounds_width": 3,
|
||||
"visible_bounds_height": 1.5,
|
||||
"visible_bounds_offset": [0, 0.25, 0]
|
||||
},
|
||||
"bones": [
|
||||
{
|
||||
"name": "bb_main",
|
||||
"pivot": [0, 0, 0],
|
||||
"cubes": [
|
||||
{"origin": [-11, 0, 4], "size": [8, 0, 8], "uv": [37, 55]}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 1
|
||||
},
|
||||
"minecraft:emitter_lifetime_looping": {
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 1
|
||||
},
|
||||
"minecraft:emitter_shape_point": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user