forked from mirrors/cobblemon
Compare commits
244
Commits
dynamax
...
mod_station
| 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 | ||
|
|
a270371632 | ||
|
|
a9caf92d2f | ||
|
|
ff4d9ae208 | ||
|
|
a87d522f6d | ||
|
|
c24ab2049d | ||
|
|
b7b1850a4e | ||
|
|
8995c107f7 | ||
|
|
dca6b5273d | ||
|
|
2e8483cdc7 | ||
|
|
6b1c2e2074 | ||
|
|
8af5b5e07a | ||
|
|
7ae19ca774 | ||
|
|
830f00553e | ||
|
|
c87f56c83f | ||
|
|
4478532bcb | ||
|
|
184348d6fa | ||
|
|
04d288ed7e | ||
|
|
0d5cce730e | ||
|
|
3e01535203 | ||
|
|
f0d99a9d83 | ||
|
|
b4eb46d632 | ||
|
|
7e52336255 | ||
|
|
2b40b5b689 | ||
|
|
da889f220b | ||
|
|
ac65910380 | ||
|
|
ba6847be9a | ||
|
|
5ddbcd1830 | ||
|
|
47605646d0 | ||
|
|
d9fdf089c2 | ||
|
|
41db2a53ce | ||
|
|
85d8779818 | ||
|
|
e9a405694c | ||
|
|
d5af099120 | ||
|
|
d817ce1471 | ||
|
|
ce09b66db5 | ||
|
|
d0c666a20d | ||
|
|
04cef915e4 | ||
|
|
e50412443e | ||
|
|
3fe1f87f18 | ||
|
|
5a0fc199ff | ||
|
|
beb769c2a5 | ||
|
|
a7b6fae4ed | ||
|
|
af2ff7bbfa | ||
|
|
4f3575b2c6 | ||
|
|
98c248ef13 | ||
|
|
4a2c69d47c | ||
|
|
c23cb644d9 | ||
|
|
a8b12cfca3 | ||
|
|
825e1ee110 | ||
|
|
b489e747bb | ||
|
|
08f957e33b | ||
|
|
e17d38c53a | ||
|
|
ee0dc24207 | ||
|
|
e5d702bb5a | ||
|
|
0397e39735 | ||
|
|
6ca1d23f45 | ||
|
|
44def2be76 | ||
|
|
507f70c2d5 | ||
|
|
d91118d05f | ||
|
|
07d8e6ed09 | ||
|
|
fb34d4bd23 | ||
|
|
6ca1337ff9 | ||
|
|
0d344337dd | ||
|
|
c6e514216c | ||
|
|
64d160020b | ||
|
|
fb4c80f68c | ||
|
|
3d27681aaa | ||
|
|
2b5433f9da | ||
|
|
352202203a | ||
|
|
62cfbd7087 | ||
|
|
5a64e8ea1b | ||
|
|
8dc9863a5d | ||
|
|
9bad141c3f | ||
|
|
9280258413 | ||
|
|
51a11163dd | ||
|
|
fe8e4dcaff | ||
|
|
e0bd6766fc | ||
|
|
4da800d396 | ||
|
|
f3246ae14a | ||
|
|
d5ad84a495 | ||
|
|
47f87b1be3 | ||
|
|
b01086270a | ||
|
|
da6a09c942 | ||
|
|
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 |
+9
-1
@@ -33,4 +33,12 @@ common/src/main/resources/assets/cobblemon/showdown
|
||||
# Generated files
|
||||
*/generated/resources/*
|
||||
/fabric/src/generated/resources/cobblemon-common.accesswidener
|
||||
/.ignored/
|
||||
/.ignored/
|
||||
|
||||
# Logs
|
||||
common/logs
|
||||
fabric/logs
|
||||
forge/logs
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
+28
-1
@@ -14,12 +14,23 @@
|
||||
- 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.
|
||||
- Added effects for many Poké Balls when sending out Pokémon or capturing.
|
||||
- Added a new send-out ball toss animation that showcases the ball used for the Pokémon.
|
||||
- Added simple move animations for Pokémon in battle with a proof of concept for more complicated animations.
|
||||
- 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.
|
||||
@@ -32,6 +43,7 @@
|
||||
- Updated movesets to add Gen 9 DLC moves.
|
||||
- Apricorn Sprouts can now be planted directly on Apricorn leaves to create a new Apricorn of the same color.
|
||||
- Added tooltips to interaction GUIs to make it clearer what they do.
|
||||
- Poké Ball recipes now use tags for their center ingredient, rather than specificied Copper Ingot, Iron Ingot, Gold Ingot, and Diamond, allowing for further datapack customisation.
|
||||
|
||||
### Fixes
|
||||
- Re-added the recipe for the Iron Vitamin.
|
||||
@@ -46,6 +58,14 @@
|
||||
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
|
||||
- Adds a translation string name to the Empty Pokeball entity.
|
||||
- Fixed very many issues with Bedrock particle effects.
|
||||
- Fixed Arbok patterns not being as specific in spawning as they were intended.
|
||||
- Fixed Pokémon not avoiding danger at all when pathfinding.
|
||||
- Fixed Sobble line not being able to swim or breathe underwater.
|
||||
- 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).
|
||||
@@ -57,6 +77,12 @@
|
||||
- Made PokemonProperties.parse more interoperable with Java.
|
||||
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
|
||||
- API to support dynamic lighting in any mod that implements this feature has been added to all Species & Forms, see LightingData class for details.
|
||||
- Added a Mixin for the BrewingStandScreenHandler to fix the incorrect handling of quickMove in Fabric.
|
||||
- Corrections to the ability implementation have been made that make ``Ability.forced`` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
|
||||
- ``Pokemon.ability`` no longer has a public setter, please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
|
||||
- ``Ability.forced``, ``Ability.index`` and ``Ability.priority`` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
|
||||
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 to 2 regular abilities and 1 hidden.
|
||||
- ``Pokemon#rollAbility`` has been added, this rerolls a legal ability for the Pokémon.
|
||||
|
||||
### Datapack & Resourcepack Creators
|
||||
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
|
||||
@@ -91,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)
|
||||
|
||||
@@ -1026,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
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import net.minecraft.entity.mob.PiglinBrain;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
|
||||
@Mixin(PiglinBrain.class)
|
||||
public class PiglinBarterMixin {
|
||||
|
||||
@Inject(method = "acceptsForBarter", at = @At(value = "RETURN"), cancellable = true)
|
||||
private static void cobblemon$acceptsForBarter(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
|
||||
if(!ci.getReturnValue()) {
|
||||
//TODO: Make a tag?
|
||||
if(stack.isOf(CobblemonItems.RELIC_COIN_POUCH))
|
||||
ci.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.mob.PiglinEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PiglinEntity.class)
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.HELD_ITEM_UPDATED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
|
||||
@@ -34,9 +35,7 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.StandardExperienceCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.*
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Generation8EvCalculator
|
||||
@@ -84,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
|
||||
@@ -94,6 +94,7 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
import com.cobblemon.mod.common.pokemon.misc.GimmighoulStashHandler
|
||||
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
@@ -130,6 +131,7 @@ import net.minecraft.entity.data.TrackedDataHandlerRegistry
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.item.NameTagItem
|
||||
import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.util.WorldSavePath
|
||||
import net.minecraft.world.World
|
||||
import org.apache.logging.log4j.LogManager
|
||||
@@ -230,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() }
|
||||
}
|
||||
@@ -253,6 +258,7 @@ object Cobblemon {
|
||||
|
||||
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["integer"] = IntSpeciesFeatureProvider::class.java
|
||||
|
||||
SpeciesFeatures.register(
|
||||
DataKeys.CAN_BE_MILKED,
|
||||
|
||||
@@ -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,20 @@ 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,
|
||||
CobblemonBlocks.GILDED_CHEST,
|
||||
CobblemonBlocks.BLUE_GILDED_CHEST,
|
||||
CobblemonBlocks.YELLOW_GILDED_CHEST,
|
||||
CobblemonBlocks.PINK_GILDED_CHEST,
|
||||
CobblemonBlocks.BLACK_GILDED_CHEST,
|
||||
CobblemonBlocks.WHITE_GILDED_CHEST,
|
||||
CobblemonBlocks.GREEN_GILDED_CHEST,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST
|
||||
).build(null))
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_MULTIBLOCK: BlockEntityType<FossilMultiblockEntity> = this.create("fossil_multiblock",
|
||||
@@ -65,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.mixin.invoker.*
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
|
||||
@@ -151,11 +152,11 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val YELLOW_APRICORN_SAPLING = this.create("yellow_apricorn_sapling", ApricornSaplingBlock(PLANT_PROPERTIES, Apricorn.YELLOW))
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
|
||||
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
|
||||
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.BIG_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
|
||||
@@ -191,6 +192,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
@@ -198,6 +200,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
@@ -205,6 +208,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
|
||||
@@ -223,6 +227,99 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
@JvmField
|
||||
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
|
||||
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = create(
|
||||
"relic_coin_pouch",
|
||||
CoinPouchBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
|
||||
.pistonBehavior(PistonBehavior.DESTROY)
|
||||
.strength(0.4f)
|
||||
.nonOpaque(), true
|
||||
)
|
||||
)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = create(
|
||||
"relic_coin_sack",
|
||||
CoinPouchBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
|
||||
.pistonBehavior(PistonBehavior.DESTROY)
|
||||
.strength(0.4f), false
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST = create(
|
||||
"gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.RED
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BLUE_GILDED_CHEST = create(
|
||||
"blue_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.BLUE
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BLACK_GILDED_CHEST = create(
|
||||
"black_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.BLACK
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val YELLOW_GILDED_CHEST = create(
|
||||
"yellow_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.YELLOW
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val WHITE_GILDED_CHEST = create(
|
||||
"white_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.WHITE
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GREEN_GILDED_CHEST = create(
|
||||
"green_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.GREEN
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val PINK_GILDED_CHEST = create(
|
||||
"pink_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.PINK
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GIMMIGHOUL_CHEST = create(
|
||||
"gimmighoul_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.FAKE
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MONITOR = create(
|
||||
"monitor",
|
||||
@@ -255,7 +352,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
RestorationTankBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.IRON_GRAY)
|
||||
.sounds(BlockSoundGroup.METAL)
|
||||
.sounds(BlockSoundGroup.GLASS)
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.requiresTool()
|
||||
.strength(5.0F, 6.0F)
|
||||
@@ -288,19 +385,55 @@ 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 RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
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 BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
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 CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create(
|
||||
@@ -323,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.
|
||||
*
|
||||
@@ -333,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 {
|
||||
@@ -441,7 +633,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
|
||||
private fun berryBlock(name: String): BerryBlock {
|
||||
val identifier = cobblemonResource("${name}_berry")
|
||||
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP).strength(0.2F)))
|
||||
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(CobblemonSounds.BERRY_BUSH_SOUNDS).strength(0.2F)))
|
||||
this.berries[identifier] = block
|
||||
return 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
|
||||
@@ -229,6 +231,22 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val APRICORN_SIGN = this.create("apricorn_sign", SignItem(Item.Settings().maxCount(16), CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN))
|
||||
@JvmField
|
||||
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", HangingSignItem(CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
|
||||
@JvmField
|
||||
val GILDED_CHEST = this.create("gilded_chest", BlockItem(CobblemonBlocks.GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val BLUE_GILDED_CHEST = this.create("blue_gilded_chest", BlockItem(CobblemonBlocks.BLUE_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val YELLOW_GILDED_CHEST = this.create("yellow_gilded_chest", BlockItem(CobblemonBlocks.YELLOW_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val PINK_GILDED_CHEST = this.create("pink_gilded_chest", BlockItem(CobblemonBlocks.PINK_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val BLACK_GILDED_CHEST = this.create("black_gilded_chest", BlockItem(CobblemonBlocks.BLACK_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val WHITE_GILDED_CHEST = this.create("white_gilded_chest", BlockItem(CobblemonBlocks.WHITE_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val GREEN_GILDED_CHEST = this.create("green_gilded_chest", BlockItem(CobblemonBlocks.GREEN_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val GIMMIGHOUL_CHEST = this.create("gimmighoul_chest", BlockItem(CobblemonBlocks.GIMMIGHOUL_CHEST, Item.Settings()))
|
||||
|
||||
@JvmField
|
||||
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
|
||||
@@ -240,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())
|
||||
@@ -380,6 +403,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField
|
||||
val GENIUS_FEATHER = create("genius_feather", FeatherItem(Stats.SPECIAL_ATTACK))
|
||||
@JvmField
|
||||
val SWIFT_FEATHER = create("swift_feather", FeatherItem(Stats.SPEED))
|
||||
@JvmField
|
||||
val HEALTH_FEATHER = create("health_feather", FeatherItem(Stats.HP))
|
||||
@JvmField
|
||||
val RESIST_FEATHER = create("resist_feather", FeatherItem(Stats.DEFENCE))
|
||||
@JvmField
|
||||
val MUSCLE_FEATHER = create("muscle_feather", FeatherItem(Stats.ATTACK))
|
||||
@JvmField
|
||||
val CLEVER_FEATHER = create("clever_feather", FeatherItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = create("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Settings().food(FoodComponent.Builder().snack().hunger(1).saturationModifier(0.2f).build())))
|
||||
@JvmField
|
||||
val ROASTED_LEEK = create("roasted_leek", Item(Item.Settings().food(FoodComponent.Builder().snack().hunger(3).saturationModifier(0.3f).build())))
|
||||
@@ -530,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
|
||||
*/
|
||||
@@ -733,19 +773,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val ARMOR_FOSSIL = noSettingsItem("armor_fossil")
|
||||
@JvmField
|
||||
val BIRD_FOSSIL = noSettingsItem("bird_fossil")
|
||||
val FOSSILIZED_BIRD = noSettingsItem("fossilized_bird")
|
||||
@JvmField
|
||||
val CLAW_FOSSIL = noSettingsItem("claw_fossil")
|
||||
@JvmField
|
||||
val COVER_FOSSIL = noSettingsItem("cover_fossil")
|
||||
@JvmField
|
||||
val DINO_FOSSIL = noSettingsItem("dino_fossil")
|
||||
val FOSSILIZED_DINO = noSettingsItem("fossilized_dino")
|
||||
@JvmField
|
||||
val DOME_FOSSIL = noSettingsItem("dome_fossil")
|
||||
@JvmField
|
||||
val DRAKE_FOSSIL = noSettingsItem("drake_fossil")
|
||||
val FOSSILIZED_DRAKE = noSettingsItem("fossilized_drake")
|
||||
@JvmField
|
||||
val FISH_FOSSIL = noSettingsItem("fish_fossil")
|
||||
val FOSSILIZED_FISH = noSettingsItem("fossilized_fish")
|
||||
@JvmField
|
||||
val HELIX_FOSSIL = noSettingsItem("helix_fossil")
|
||||
@JvmField
|
||||
@@ -824,6 +864,54 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
// Misc
|
||||
@JvmField
|
||||
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
|
||||
@JvmField
|
||||
val RELIC_COIN = noSettingsItem("relic_coin")
|
||||
@JvmField
|
||||
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
|
||||
val NORMAL_GEM = noSettingsItem("normal_gem")
|
||||
@JvmField
|
||||
val FIRE_GEM = noSettingsItem("fire_gem")
|
||||
@JvmField
|
||||
val WATER_GEM = noSettingsItem("water_gem")
|
||||
@JvmField
|
||||
val GRASS_GEM = noSettingsItem("grass_gem")
|
||||
@JvmField
|
||||
val ELECTRIC_GEM = noSettingsItem("electric_gem")
|
||||
@JvmField
|
||||
val ICE_GEM = noSettingsItem("ice_gem")
|
||||
@JvmField
|
||||
val FIGHTING_GEM = noSettingsItem("fighting_gem")
|
||||
@JvmField
|
||||
val POISON_GEM = noSettingsItem("poison_gem")
|
||||
@JvmField
|
||||
val GROUND_GEM = noSettingsItem("ground_gem")
|
||||
@JvmField
|
||||
val FLYING_GEM = noSettingsItem("flying_gem")
|
||||
@JvmField
|
||||
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
|
||||
@JvmField
|
||||
val BUG_GEM = noSettingsItem("bug_gem")
|
||||
@JvmField
|
||||
val ROCK_GEM = noSettingsItem("rock_gem")
|
||||
@JvmField
|
||||
val GHOST_GEM = noSettingsItem("ghost_gem")
|
||||
@JvmField
|
||||
val DRAGON_GEM = noSettingsItem("dragon_gem")
|
||||
@JvmField
|
||||
val DARK_GEM = noSettingsItem("dark_gem")
|
||||
@JvmField
|
||||
val STEEL_GEM = noSettingsItem("steel_gem")
|
||||
@JvmField
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
|
||||
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -82,9 +84,12 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPa
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
@@ -128,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.*
|
||||
@@ -248,6 +255,7 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(PokemonStateUpdatePacket.ID, PokemonStateUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(TetheringUpdatePacket.ID, TetheringUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
|
||||
// Evolution start
|
||||
@@ -314,6 +322,9 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
|
||||
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
|
||||
@@ -425,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)
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.sound.BlockSoundGroup
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.sound.SoundEvents
|
||||
|
||||
object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Registry<SoundEvent>>, SoundEvent>() {
|
||||
|
||||
@@ -84,6 +85,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
val MEDICINE_PILLS_USE = this.create("medicine_pills.use")
|
||||
@JvmField
|
||||
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
|
||||
@JvmField
|
||||
val MEDICINE_FEATHER_USE = this.create("medicine_feather.use")
|
||||
|
||||
@JvmField
|
||||
val BERRY_HARVEST = this.create("berry.harvest")
|
||||
@@ -118,6 +121,14 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossilmachine.unprotected")
|
||||
|
||||
@JvmField
|
||||
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
|
||||
@JvmField
|
||||
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
|
||||
@JvmField
|
||||
val COIN_POUCH_STEP = this.create("coin_pouch.step")
|
||||
@JvmField
|
||||
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
|
||||
@JvmField
|
||||
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
|
||||
@JvmField
|
||||
@@ -129,6 +140,52 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
@JvmField
|
||||
val TUMBLESTONE_STEP = this.create("tumblestone.step")
|
||||
|
||||
@JvmField
|
||||
val GIMMIGHOUL_GIVE_ITEM_SMALL = this.create("gimmighoul.give_item_small")
|
||||
@JvmField
|
||||
val GIMMIGHOUL_REVEAL = this.create("gimmighoul.reveal")
|
||||
|
||||
@JvmField
|
||||
val BERRY_BUSH_BREAK = this.create("berry_bush.break")
|
||||
@JvmField
|
||||
val BERRY_BUSH_PLACE = this.create("berry_bush.place")
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_BREAK = this.create("big_root.break")
|
||||
@JvmField
|
||||
val ENERGY_ROOT_PLACE = this.create("energy_root.place")
|
||||
|
||||
@JvmField
|
||||
val MINT_BREAK = this.create("mint.break")
|
||||
@JvmField
|
||||
val MINT_PLACE = this.create("mint.place")
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK_BREAK = this.create("medicinal_leek.break")
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK_PLACE = this.create("medicinal_leek.plant")
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST_OPEN = this.create("gilded_chest.open")
|
||||
@JvmField
|
||||
val GILDED_CHEST_CLOSE = this.create("gilded_chest.close")
|
||||
@JvmField
|
||||
val GILDED_CHEST_STEP = this.create("gilded_chest.step")
|
||||
@JvmField
|
||||
val GILDED_CHEST_HIT = this.create("gilded_chest.hit")
|
||||
@JvmField
|
||||
val GILDED_CHEST_BREAK = this.create("gilded_chest.break")
|
||||
@JvmField
|
||||
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
|
||||
|
||||
@JvmField
|
||||
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
COIN_POUCH_BREAK,
|
||||
COIN_POUCH_STEP,
|
||||
COIN_POUCH_PLACE,
|
||||
COIN_POUCH_HIT,
|
||||
COIN_POUCH_STEP
|
||||
)
|
||||
@JvmField
|
||||
val IMPACT_NORMAL = this.create("impact.normal")
|
||||
@JvmField
|
||||
@@ -183,6 +240,95 @@ 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,
|
||||
BERRY_BUSH_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
BERRY_BUSH_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
BIG_ROOT_BREAK,
|
||||
SoundEvents.BLOCK_ROOTS_STEP,
|
||||
SoundEvents.BLOCK_ROOTS_PLACE,
|
||||
SoundEvents.BLOCK_ROOTS_HIT,
|
||||
SoundEvents.BLOCK_ROOTS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ENERGY_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
SoundEvents.BLOCK_ROOTS_BREAK,
|
||||
SoundEvents.BLOCK_ROOTS_STEP,
|
||||
ENERGY_ROOT_PLACE,
|
||||
SoundEvents.BLOCK_ROOTS_HIT,
|
||||
SoundEvents.BLOCK_ROOTS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
MEDICINAL_LEEK_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
MEDICINAL_LEEK_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MINT_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
MINT_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
MINT_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
GILDED_CHEST_BREAK,
|
||||
GILDED_CHEST_STEP,
|
||||
GILDED_CHEST_PLACE,
|
||||
GILDED_CHEST_HIT,
|
||||
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)
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.drop
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
@@ -88,6 +89,8 @@ class DropTable {
|
||||
amount: IntRange = this.amount
|
||||
) {
|
||||
val drops = getDrops(amount).toMutableList()
|
||||
val heldItem = (entity as PokemonEntity).pokemon.heldItemNoCopy()
|
||||
if (!heldItem.isEmpty) entity.dropItem(heldItem.item)
|
||||
LOOT_DROPPED.postThen(
|
||||
event = LootDroppedEvent(this, player, entity, drops),
|
||||
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEv
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.HeldItemUpdatedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
|
||||
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
|
||||
@@ -66,6 +67,8 @@ object CobblemonEvents {
|
||||
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
|
||||
@JvmField
|
||||
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
|
||||
@JvmField
|
||||
val HELD_ITEM_UPDATED = CancelableObservable<HeldItemUpdatedEvent>()
|
||||
|
||||
@JvmField
|
||||
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.interaction
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
/**
|
||||
* Event published when a held item is being updated on a [Pokemon].
|
||||
* Cancelling the event will prevent any action from being taken on
|
||||
* the Pokémon.
|
||||
*
|
||||
* [newItem] differs from [originalStack] in that the former is a
|
||||
* one-sized version of the original (Pokémon cannot hold stacks) and
|
||||
* changing it in any way will not affect the original item held by
|
||||
* the player.
|
||||
*
|
||||
* @author Whatsy, Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
data class HeldItemUpdatedEvent(
|
||||
val cause: LivingEntity?,
|
||||
val pokemon: Pokemon,
|
||||
val originalStack: ItemStack,
|
||||
val decrement: Boolean,
|
||||
val oldItem: ItemStack,
|
||||
val newItem: ItemStack
|
||||
) : Cancelable()
|
||||
@@ -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 }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
@@ -77,6 +78,11 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
|
||||
return BlockRenderType.MODEL
|
||||
}
|
||||
|
||||
override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState): ItemStack {
|
||||
val blockEntity = world.getBlockEntity(pos) as? MultiblockEntity ?: return ItemStack.EMPTY
|
||||
return if (blockEntity.multiblockStructure == null) super.getPickStack(world, pos, state) else ItemStack.EMPTY
|
||||
}
|
||||
|
||||
abstract fun createMultiBlockEntity(pos: BlockPos, state: BlockState): FossilMultiblockEntity
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -34,4 +34,10 @@ interface PassiveEvolution : Evolution {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* If the evolution will disappear once the conditions are no longer met.
|
||||
*/
|
||||
val permanent: Boolean
|
||||
|
||||
|
||||
}
|
||||
+39
-6
@@ -11,10 +11,12 @@ package com.cobblemon.mod.common.api.pokemon.feature
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.substitute
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A [SpeciesFeatureProvider] which is a string value selected from a fixed list of choices. Parameters exist
|
||||
@@ -24,15 +26,46 @@ import net.minecraft.nbt.NbtCompound
|
||||
* @since November 30th, 2022
|
||||
*/
|
||||
open class ChoiceSpeciesFeatureProvider(
|
||||
override val keys: List<String>,
|
||||
override var keys: List<String>,
|
||||
var default: String? = null,
|
||||
var choices: List<String> = listOf(),
|
||||
var isAspect: Boolean = true,
|
||||
var aspectFormat: String = "{{choice}}"
|
||||
) : SpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
|
||||
override val needsKey = true
|
||||
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
|
||||
override var needsKey = true
|
||||
override var visible = false
|
||||
fun getAspect(feature: StringSpeciesFeature) = aspectFormat.substitute("choice", feature.value)
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeCollection(choices) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeBoolean(isAspect)
|
||||
buffer.writeString(aspectFormat)
|
||||
buffer.writeBoolean(needsKey)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
keys = buffer.readList { buffer.readString() }
|
||||
default = buffer.readNullable { buffer.readString() }
|
||||
choices = buffer.readList { buffer.readString() }
|
||||
isAspect = buffer.readBoolean()
|
||||
aspectFormat = buffer.readString()
|
||||
needsKey = buffer.readBoolean()
|
||||
}
|
||||
|
||||
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<StringSpeciesFeature>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun invoke(buffer: PacketByteBuf, name: String): StringSpeciesFeature? {
|
||||
return if (name in keys) {
|
||||
StringSpeciesFeature(name, "").also { it.decode(buffer) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllAspects(): MutableList<String> {
|
||||
val aspects = choices.toMutableList()
|
||||
choices.forEach {
|
||||
@@ -45,10 +78,10 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
|
||||
internal constructor(): this(emptyList())
|
||||
|
||||
fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
override fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): StringSpeciesFeature? {
|
||||
val existing = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
val existing = get(pokemon)
|
||||
return if (existing != null && existing.value in choices) {
|
||||
existing
|
||||
} else {
|
||||
@@ -88,7 +121,7 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return if (isAspect) {
|
||||
pokemon.getFeature<StringSpeciesFeature>(keys.first())?.let { setOf(getAspect(it)) } ?: emptySet()
|
||||
get(pokemon)?.let { setOf(getAspect(it)) } ?: emptySet()
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
+43
-5
@@ -12,10 +12,12 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A simple [SpeciesFeature] that is a true/false flag value. It implements [CustomPokemonProperty] for use in
|
||||
@@ -26,7 +28,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
* @author Hiroku
|
||||
* @since May 13th, 2022
|
||||
*/
|
||||
open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, CustomPokemonProperty {
|
||||
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
constructor(name: String, enabled: Boolean): this(name) {
|
||||
this.enabled = enabled
|
||||
}
|
||||
@@ -53,6 +55,14 @@ open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, Custo
|
||||
return this
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeBoolean(enabled)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
enabled = buffer.readBoolean()
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$enabled"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
@@ -71,11 +81,37 @@ open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, Custo
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == enabled
|
||||
}
|
||||
|
||||
class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
|
||||
override val keys: List<String>
|
||||
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
|
||||
override var keys: List<String> = emptyList()
|
||||
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
|
||||
override val needsKey get() = true
|
||||
var default: String? = null
|
||||
val isAspect = true
|
||||
var isAspect = true
|
||||
override var visible: Boolean = false
|
||||
|
||||
override fun invoke(buffer: PacketByteBuf, name: String): FlagSpeciesFeature? {
|
||||
return if (name in keys) {
|
||||
FlagSpeciesFeature(name).also { it.decode(buffer) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeBoolean(isAspect)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
keys = buffer.readList { it.readString() }
|
||||
default = buffer.readNullable { it.readString() }
|
||||
isAspect = buffer.readBoolean()
|
||||
}
|
||||
|
||||
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<FlagSpeciesFeature>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun examples() = setOf("true", "false")
|
||||
|
||||
@@ -94,8 +130,10 @@ class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, C
|
||||
|
||||
constructor(vararg keys: String) : this(keys.toList())
|
||||
|
||||
override fun get(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(keys.first())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): FlagSpeciesFeature? {
|
||||
return pokemon.getFeature(keys.first())
|
||||
return get(pokemon)
|
||||
?: when (default) {
|
||||
"random" -> FlagSpeciesFeature(keys.first(), Random.Default.nextBoolean())
|
||||
in setOf("true", "false") -> FlagSpeciesFeature(keys.first(), default.toBoolean())
|
||||
|
||||
+14
-1
@@ -8,13 +8,17 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -24,6 +28,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* A registry very similar to [SpeciesFeatures] but the [SpeciesFeatureProvider] contained within it
|
||||
@@ -42,11 +47,15 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
|
||||
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
|
||||
override val resourcePath: String = "global_species_features"
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
player.sendPacket(GlobalSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
|
||||
}
|
||||
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
|
||||
resourceFeatures.keys.toList().forEach(this::unregister)
|
||||
@@ -59,6 +68,10 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
|
||||
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
|
||||
|
||||
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
|
||||
codeFeatures.putAll(entries.map { it.toPair() })
|
||||
}
|
||||
|
||||
private fun register(name: String, provider: SpeciesFeatureProvider<*>, isCoded: Boolean) {
|
||||
val mapping = if (isCoded) codeFeatures else resourceFeatures
|
||||
if (provider is AspectProvider) {
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* A species feature value that's just an integer. Complex stuff.
|
||||
* @author Hiroku
|
||||
*/
|
||||
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
var value = 0
|
||||
|
||||
constructor(): this("")
|
||||
constructor(name: String, value: Int): this(name) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
|
||||
pokemonNBT.putInt(name, value)
|
||||
return pokemonNBT
|
||||
}
|
||||
|
||||
override fun loadFromNBT(pokemonNBT: NbtCompound): SynchronizedSpeciesFeature {
|
||||
value = pokemonNBT.getInt(name)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
|
||||
pokemonJSON.addProperty(name, value)
|
||||
return pokemonJSON
|
||||
}
|
||||
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
|
||||
value = pokemonJSON.get(name).asInt
|
||||
return this
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeInt(value)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
value = buffer.readInt()
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$value"
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
|
||||
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
|
||||
val existingFeature = pokemon.getFeature<IntSpeciesFeature>(name)
|
||||
if (existingFeature != null) {
|
||||
existingFeature.value = value
|
||||
} else {
|
||||
pokemon.features.add(IntSpeciesFeature(name, value))
|
||||
}
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<IntSpeciesFeature>(name)?.value == value
|
||||
}
|
||||
|
||||
class IntSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<IntSpeciesFeature>, CustomPokemonPropertyType<IntSpeciesFeature> {
|
||||
class DisplayData : Encodable, Decodable {
|
||||
var name: String = ""
|
||||
@SerializedName(value = "colour" /* fuck you we use real english */, alternate = ["color"])
|
||||
var colour = Vec3d(255.0, 255.0, 255.0)
|
||||
var underlay: Identifier? = null
|
||||
var overlay: Identifier? = null
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
name = buffer.readString()
|
||||
colour = Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble())
|
||||
underlay = buffer.readNullable { buffer.readIdentifier() }
|
||||
overlay = buffer.readNullable { buffer.readIdentifier() }
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(name)
|
||||
buffer.writeDouble(colour.x)
|
||||
buffer.writeDouble(colour.y)
|
||||
buffer.writeDouble(colour.z)
|
||||
buffer.writeNullable(underlay) { _, value -> buffer.writeIdentifier(value) }
|
||||
buffer.writeNullable(overlay) { _, value -> buffer.writeIdentifier(value) }
|
||||
}
|
||||
}
|
||||
|
||||
override var keys = listOf<String>()
|
||||
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
|
||||
override val needsKey get() = true
|
||||
override var visible = false
|
||||
var default: Int? = null
|
||||
var min = 0
|
||||
var max = 100
|
||||
var display: DisplayData? = null
|
||||
|
||||
override fun fromString(value: String?) = value?.toIntOrNull()?.takeIf { it in min..max }?.let { IntSpeciesFeature(keys.first(), it) }
|
||||
|
||||
override fun examples() = emptyList<String>()
|
||||
override fun invoke(buffer: PacketByteBuf, name: String): IntSpeciesFeature? {
|
||||
return if (name in keys) {
|
||||
IntSpeciesFeature(name, buffer.readInt())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(pokemon: Pokemon): IntSpeciesFeature? {
|
||||
return get(pokemon) ?: default?.let { IntSpeciesFeature(keys.first(), it) }
|
||||
}
|
||||
|
||||
override fun invoke(nbt: NbtCompound): IntSpeciesFeature? {
|
||||
return if (nbt.contains(keys.first())) {
|
||||
IntSpeciesFeature(keys.first(), nbt.getInt(keys.first()))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(json: JsonObject): IntSpeciesFeature? {
|
||||
return if (json.has(keys.first())) {
|
||||
IntSpeciesFeature(keys.first(), json.get(keys.first()).asInt)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(pokemon: Pokemon) = pokemon.features.filterIsInstance<IntSpeciesFeature>().find { it.name in keys }
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeNullable(default) { _, value -> buffer.writeInt(value) }
|
||||
buffer.writeInt(min)
|
||||
buffer.writeInt(max)
|
||||
buffer.writeNullable(display) { _, value -> value.encode(buffer) }
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
keys = buffer.readList { buffer.readString() }
|
||||
default = buffer.readNullable { buffer.readInt() }
|
||||
min = buffer.readInt()
|
||||
max = buffer.readInt()
|
||||
display = buffer.readNullable { DisplayData().also { it.decode(buffer) } }
|
||||
}
|
||||
|
||||
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<IntSpeciesFeature>? {
|
||||
return display?.let {
|
||||
BarSummarySpeciesFeatureRenderer(
|
||||
name = keys.first(),
|
||||
displayName = it.name.asTranslated(),
|
||||
min = min,
|
||||
max = max,
|
||||
colour = it.colour,
|
||||
underlay = it.underlay ?: cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
|
||||
overlay = it.overlay ?: cobblemonResource("textures/gui/summary/summary_stats_generic_overlay.png"),
|
||||
pokemon = pokemon
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-4
@@ -8,10 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -38,17 +41,24 @@ object SpeciesFeatureAssignments : JsonDataRegistry<SpeciesFeatureAssignment> {
|
||||
override val typeToken = TypeToken.get(SpeciesFeatureAssignment::class.java)
|
||||
override val resourcePath = "species_feature_assignments"
|
||||
|
||||
private val assignments = mutableMapOf<String, MutableSet<String>>()
|
||||
private val assignments = mutableMapOf<Identifier, MutableSet<String>>()
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
player.sendPacket(SpeciesFeatureAssignmentSyncPacket(assignments))
|
||||
}
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureAssignment>) {
|
||||
data.values.forEach {
|
||||
it.pokemon.forEach { pokemon ->
|
||||
assignments.getOrPut(pokemon) { mutableSetOf() }.addAll(it.features)
|
||||
assignments.getOrPut(pokemon.asIdentifierDefaultingNamespace()) { mutableSetOf() }.addAll(it.features)
|
||||
}
|
||||
}
|
||||
this.observable.emit(this)
|
||||
}
|
||||
|
||||
fun getFeatures(species: Species) = assignments[species.showdownId()] ?: emptySet()
|
||||
fun loadOnClient(data: Map<Identifier, MutableSet<String>>) {
|
||||
this.assignments.clear()
|
||||
this.assignments.putAll(data)
|
||||
}
|
||||
|
||||
fun getFeatures(species: Species) = assignments[species.resourceIdentifier] ?: emptySet()
|
||||
}
|
||||
+1
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokemon.feature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A provider of [SpeciesFeature]s. These must be registered via [SpeciesFeatures] either programmatically or
|
||||
|
||||
+13
-1
@@ -8,13 +8,17 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -22,6 +26,7 @@ import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* A registry for [SpeciesFeatureProvider]s. This is the backbone of species-specific data such as
|
||||
@@ -48,11 +53,15 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
|
||||
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
|
||||
override val resourcePath: String = "species_features"
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
player.sendPacket(StandardSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
|
||||
}
|
||||
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
|
||||
resourceFeatures.keys.toList().forEach(this::unregister)
|
||||
@@ -62,6 +71,9 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
fun getCodeFeature(name: String) = resourceFeatures[name]
|
||||
fun getResourceFeature(name: String) = codeFeatures[name]
|
||||
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
|
||||
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
|
||||
codeFeatures.putAll(entries.map { it.toPair() })
|
||||
}
|
||||
|
||||
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
|
||||
fun getFeaturesFor(species: Species): List<SpeciesFeatureProvider<*>> {
|
||||
|
||||
+10
-1
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A species feature value that is a string value.
|
||||
@@ -22,7 +23,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
class StringSpeciesFeature(
|
||||
override val name: String,
|
||||
var value: String
|
||||
) : SpeciesFeature, CustomPokemonProperty {
|
||||
) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
|
||||
pokemonNBT.putString(name, value)
|
||||
return pokemonNBT
|
||||
@@ -43,6 +44,14 @@ class StringSpeciesFeature(
|
||||
return this
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(value)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
value = buffer.readString()
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$value"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
|
||||
/**
|
||||
* A species feature that can be synchronized to the client. These must be managed by a [SynchronizedSpeciesFeatureProvider].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SynchronizedSpeciesFeature : SpeciesFeature, Encodable, Decodable
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A species feature provider that will be synchronized to the client. For it to be renderable in the summary
|
||||
* screen of a Pokémon it must also provide a [SummarySpeciesFeatureRenderer] in [getRenderer]. Note that you
|
||||
* really don't want to initialize a renderer outside of that function call because if the server ever initializes
|
||||
* it then you're going to get crashes on dedicated servers (this is a bad thing).
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature> : SpeciesFeatureProvider<T>, Encodable, Decodable {
|
||||
var visible: Boolean
|
||||
operator fun invoke(buffer: PacketByteBuf, name: String): T?
|
||||
/** Gets the feature from this Pokémon, if it has been created yet. */
|
||||
fun get(pokemon: Pokemon): T?
|
||||
/** Only run this from the client. */
|
||||
fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<T>?
|
||||
}
|
||||
@@ -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.
|
||||
@@ -29,7 +28,7 @@ import net.minecraft.util.math.Vec3d
|
||||
* @since January 4th, 2023
|
||||
*/
|
||||
class BedrockParticle(
|
||||
var texture: Identifier = Identifier("minecraft:textures/particle/bubble.png"),
|
||||
var texture: Identifier = Identifier("minecraft:textures/particles/bubble.png"),
|
||||
var material: ParticleMaterial = ParticleMaterial.ALPHA,
|
||||
var uvMode: ParticleUVMode = StaticParticleUVMode(),
|
||||
var sizeX: Expression = NumberExpression(0.15),
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import net.minecraft.registry.tag.BlockTags
|
||||
|
||||
/**
|
||||
* Implements a [SpawningInfluence] that prevents Pokémon spawning on certain blocks.
|
||||
* This ensures that Pokémon don't spawn in areas that could disrupt player-built structures or mechanisms.
|
||||
* The influence checks the block directly above the potential spawn location; if the block is in the restricted list,
|
||||
* the spawn is disallowed.
|
||||
*
|
||||
* @author Aethen
|
||||
* @since February 20th, 2024
|
||||
*/
|
||||
open class RestrictedSpawnBlocksInfluence
|
||||
: SpawningInfluence {
|
||||
|
||||
// The list of blocks that Pokémon are not allowed to spawn on.
|
||||
private val restrictedBlocks = BlockTags.RAILS
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Affects the spawnable status of a Pokémon spawn detail.
|
||||
* If the block directly above the spawn location is in the restricted list, the spawn is disallowed.
|
||||
*
|
||||
* @param detail The Pokémon spawn detail to be affected.
|
||||
* @param ctx The context of the spawning event.
|
||||
* @return `true` if the spawn is allowed, `false` if it is disallowed.
|
||||
*/
|
||||
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
|
||||
// If the spawn detail is not a Pokémon, it is not affected by this influence.
|
||||
if (detail.type != "pokemon") return true
|
||||
|
||||
// Get the position of the potential spawn location.
|
||||
val pokemonSpawnPos = ctx.position
|
||||
|
||||
// Examine the block immediately above the spawn block to check for a rail
|
||||
// Potentially need a refactor if we ever want to check the spawn block itself.
|
||||
return ctx.world.getBlockState(pokemonSpawnPos.withY(pokemonSpawnPos.y + 1)).isIn(restrictedBlocks).not()
|
||||
}
|
||||
}
|
||||
+22
-3
@@ -13,8 +13,10 @@ import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnerManager
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.RestrictedSpawnBlocksInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
|
||||
import com.cobblemon.mod.common.util.mutableLazy
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
@@ -26,14 +28,31 @@ import net.minecraft.server.network.ServerPlayerEntity
|
||||
* @since February 14th, 2022
|
||||
*/
|
||||
object PlayerSpawnerFactory {
|
||||
var spawns: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) })
|
||||
var spawns: SpawnPool by mutableLazy {
|
||||
CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of influences for a new [PlayerSpawner].
|
||||
* The influences will be added to the spawner in the order they are added to this list.
|
||||
*
|
||||
* [PlayerLevelRangeInfluence] adjusts the level range of Pokémon that can spawn based on the player's level.
|
||||
* [RestrictedSpawnBlocksInfluence] prevents Pokémon from spawning on certain blocks.
|
||||
*/
|
||||
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({
|
||||
PlayerLevelRangeInfluence(
|
||||
it,
|
||||
variation = 5
|
||||
)
|
||||
}, { RestrictedSpawnBlocksInfluence() })
|
||||
|
||||
fun create(spawnerManager: SpawnerManager, player: ServerPlayerEntity): PlayerSpawner {
|
||||
val influences = influenceBuilders.mapNotNull { it(player) }
|
||||
return PlayerSpawner(player, spawns, spawnerManager).also {
|
||||
it.influences.addAll(influences)
|
||||
it.influences.addAll(CobblemonSpawnRules.rules.values.filter(SpawnRule::enabled).flatMap(SpawnRule::components))
|
||||
it.influences.addAll(
|
||||
CobblemonSpawnRules.rules.values.filter(SpawnRule::enabled).flatMap(SpawnRule::components)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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")
|
||||
|
||||
@@ -11,12 +11,14 @@ package com.cobblemon.mod.common.api.storage.party
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.PartyCheckContext
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
@@ -123,6 +125,12 @@ open class PlayerPartyStore(
|
||||
|
||||
// Passive evolutions
|
||||
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
|
||||
val removeList = mutableListOf<Evolution>()
|
||||
pokemon.evolutionProxy.server().forEach {
|
||||
if (!it.test(pokemon) && it is LevelUpEvolution && !it.permanent)
|
||||
removeList.add(it)
|
||||
}
|
||||
removeList.forEach { pokemon.evolutionProxy.server().remove(it) }
|
||||
}
|
||||
// Friendship
|
||||
// ToDo expand this down the line just a very basic implementation for the first releases
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -6,15 +6,14 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.summary.widgets
|
||||
package com.cobblemon.mod.common.api.tms
|
||||
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
class TypeDisplayWidget {
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
companion object {
|
||||
|
||||
private val TYPES_RESOURCE = cobblemonResource("textures/gui/types.png")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
+20
-11
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
@@ -70,18 +71,26 @@ class DamageInstruction(
|
||||
}
|
||||
|
||||
battle.dispatch {
|
||||
if (!causedFaint) {
|
||||
val pokemonEntity = battlePokemon.entity
|
||||
if (!causedFaint && pokemonEntity != null) {
|
||||
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
|
||||
pkt.sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
y = pokemonEntity.y,
|
||||
z = pokemonEntity.z,
|
||||
worldKey = pokemonEntity.world.registryKey,
|
||||
distance = 50.0
|
||||
)
|
||||
}
|
||||
|
||||
battlePokemon.entity?.let { pokemonEntity ->
|
||||
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
|
||||
pkt.sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
y = pokemonEntity.y,
|
||||
z = pokemonEntity.z,
|
||||
worldKey = pokemonEntity.world.registryKey,
|
||||
distance = 50.0
|
||||
)
|
||||
}
|
||||
if (pokemonEntity != null) {
|
||||
RunPosableMoLangPacket(pokemonEntity.id, setOf("q.particle('cobblemon:hit', 'target')")).sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
y = pokemonEntity.y,
|
||||
z = pokemonEntity.z,
|
||||
worldKey = pokemonEntity.world.registryKey,
|
||||
distance = 50.0
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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.block
|
||||
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
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.WorldAccess
|
||||
|
||||
class CoinPouchBlock(settings: Settings, val small: Boolean) : HorizontalFacingBlock(settings) {
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(NATURAL, false)
|
||||
.with(FACING, Direction.NORTH)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(
|
||||
state: BlockState?,
|
||||
world: BlockView?,
|
||||
pos: BlockPos?,
|
||||
context: ShapeContext?
|
||||
) = if (!small) super.getOutlineShape(state, world, pos, context) else {
|
||||
VoxelShapes.cuboid(0.3125, 0.0, 0.3125, 0.6875, 0.3125, 0.6875)
|
||||
}
|
||||
|
||||
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.opposite) as BlockState
|
||||
if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
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 appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, NATURAL)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val NATURAL: BooleanProperty = BooleanProperty.of("natural")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
@@ -216,6 +217,11 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
|
||||
override fun asString() = label
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(state: BlockState?, world: BlockView?, pos: BlockPos?, type: NavigationType?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PART = EnumProperty.of("part", TankPart::class.java)
|
||||
val TRIGGERED = Properties.TRIGGERED
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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.chest
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.block.entity.GildedChestBlockEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.BlockWithEntity
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.block.HorizontalFacingBlock
|
||||
import net.minecraft.block.ShapeContext
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.mob.PiglinBrain
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.BlockMirror
|
||||
import net.minecraft.util.BlockRotation
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.ItemScatterer
|
||||
import net.minecraft.util.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
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWithEntity(settings) {
|
||||
|
||||
init {
|
||||
defaultState = defaultState.with(Properties.HORIZONTAL_FACING, Direction.SOUTH)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val POKEMON_ARGS = "gimmighoul"
|
||||
val LEVEL_RANGE = 5..30
|
||||
|
||||
val SOUTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.25, 1.0, 1.0, 0.9375)
|
||||
)
|
||||
val NORTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0625, 1.0, 1.0, 0.75)
|
||||
)
|
||||
val WEST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0625, 0.0, 0.0, 0.75, 1.0, 1.0)
|
||||
)
|
||||
val EAST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.25, 0.0, 0.0, 0.9375, 1.0, 1.0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState) = GildedChestBlockEntity(pos, state, type)
|
||||
|
||||
override fun getOutlineShape(
|
||||
state: BlockState,
|
||||
world: BlockView,
|
||||
pos: BlockPos,
|
||||
context: ShapeContext
|
||||
): VoxelShape {
|
||||
return when (state.get(HorizontalFacingBlock.FACING)) {
|
||||
Direction.NORTH -> NORTH_OUTLINE
|
||||
Direction.SOUTH -> SOUTH_OUTLINE
|
||||
Direction.WEST -> WEST_OUTLINE
|
||||
else -> EAST_OUTLINE
|
||||
}
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
super.appendProperties(builder)
|
||||
builder.add(Properties.HORIZONTAL_FACING)
|
||||
}
|
||||
|
||||
private val facingToYaw: HashMap<Direction, Float> = hashMapOf(
|
||||
Direction.NORTH to -179.0F,
|
||||
Direction.WEST to 90.0F,
|
||||
Direction.SOUTH to 0.0F,
|
||||
Direction.EAST to -90.0F
|
||||
)
|
||||
|
||||
override fun getName(): MutableText {
|
||||
return if (isFake()) Text.translatable("block.cobblemon.gilded_chest") else super.getName()
|
||||
}
|
||||
|
||||
fun isFake() = (type == Type.FAKE)
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
|
||||
if (isFake()) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
spawnPokemon(world, pos, state, player)
|
||||
}
|
||||
world.setBlockState(pos, Blocks.AIR.defaultState)
|
||||
} else super.onBreak(world, pos, state, player)
|
||||
}
|
||||
|
||||
private fun spawnPokemon(world: World, pos: BlockPos, state: BlockState, player: ServerPlayerEntity) : ActionResult {
|
||||
val properties = "$POKEMON_ARGS lvl=${LEVEL_RANGE.random()}"
|
||||
val pokemon = PokemonProperties.parse(properties)
|
||||
val entity = pokemon.createEntity(world)
|
||||
|
||||
// The yaw based on the block's facing direction
|
||||
val yaw = facingToYaw[state[HorizontalFacingBlock.FACING]] ?: 0.0F
|
||||
|
||||
entity.dataTracker.set(PokemonEntity.SPAWN_DIRECTION, facingToYaw[state[HorizontalFacingBlock.FACING]])
|
||||
val offsetDir = state[HorizontalFacingBlock.FACING]
|
||||
val vec = pos.toVec3d().add(offsetDir.offsetX * 0.1 + 0.5, 0.0, offsetDir.offsetZ * 0.1 + 0.5)
|
||||
entity.refreshPositionAndAngles(vec.x, vec.y, vec.z, yaw, entity.pitch)
|
||||
world.spawnEntity(entity)
|
||||
|
||||
world.removeBlock(pos, false)
|
||||
afterOnServer(ticks = 2) {
|
||||
if (player !in player.world.players) {
|
||||
return@afterOnServer
|
||||
}
|
||||
val party = player.party()
|
||||
if (!player.isCreative) {
|
||||
entity.forceBattle(player)
|
||||
} else {
|
||||
world.playSound(null, pos, CobblemonSounds.GIMMIGHOUL_REVEAL, SoundCategory.NEUTRAL)
|
||||
}
|
||||
}
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun onUse(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
hand: Hand,
|
||||
hit: BlockHitResult
|
||||
): ActionResult {
|
||||
if (isFake()) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
return spawnPokemon(world, pos, state, player)
|
||||
} else {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
val entity = world.getBlockEntity(pos) as? GildedChestBlockEntity ?: return ActionResult.FAIL
|
||||
if (world.getBlockState(pos.up()).isSolidBlock(world, pos.up())) return ActionResult.FAIL
|
||||
player.openHandledScreen(entity)
|
||||
if (!player.world.isClient) {
|
||||
PiglinBrain.onGuardedBlockInteracted(player, true)
|
||||
}
|
||||
val state = entity.poseableState
|
||||
state.currentModel?.let {
|
||||
it.moveToPose(null, state, it.getPose("OPEN")!!)
|
||||
}
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun onStateReplaced(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
newState: BlockState,
|
||||
moved: Boolean
|
||||
) {
|
||||
if (!state.isOf(newState.block)) {
|
||||
val chest = world.getBlockEntity(pos) as GildedChestBlockEntity
|
||||
ItemScatterer.spawn(world, pos, chest.inventoryContents)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRenderType(state: BlockState?) = BlockRenderType.ENTITYBLOCK_ANIMATED
|
||||
|
||||
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.opposite)
|
||||
// }
|
||||
// return null
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun rotate(state: BlockState, rotation: BlockRotation): BlockState {
|
||||
return state.with(
|
||||
Properties.HORIZONTAL_FACING, rotation.rotate(
|
||||
state.get(Properties.HORIZONTAL_FACING) as Direction
|
||||
)
|
||||
) as BlockState
|
||||
}
|
||||
|
||||
override fun mirror(state: BlockState, mirror: BlockMirror): BlockState {
|
||||
return state.rotate(mirror.getRotation(state.get(Properties.HORIZONTAL_FACING) as Direction))
|
||||
}
|
||||
|
||||
enum class Type(val poserId: Identifier) {
|
||||
RED(cobblemonResource("gilded_chest")),
|
||||
BLUE(cobblemonResource("blue_gilded_chest")),
|
||||
GREEN(cobblemonResource("green_gilded_chest")),
|
||||
PINK(cobblemonResource("pink_gilded_chest")),
|
||||
WHITE(cobblemonResource("white_gilded_chest")),
|
||||
BLACK(cobblemonResource("black_gilded_chest")),
|
||||
YELLOW(cobblemonResource("yellow_gilded_chest")),
|
||||
FAKE(cobblemonResource("gilded_chest"))
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(state: BlockState?, world: BlockView?, pos: BlockPos?, type: NavigationType?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.chest
|
||||
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
class GildedState : PoseableEntityState<Entity>() {
|
||||
override fun getEntity() = null
|
||||
|
||||
init {
|
||||
setPose("CLOSED")
|
||||
}
|
||||
|
||||
override fun updatePartialTicks(partialTicks: Float) {
|
||||
this.currentPartialTicks += partialTicks
|
||||
}
|
||||
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
}
|
||||
+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)
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 com.cobblemon.mod.common.block.chest.GildedChestBlock.Type
|
||||
import com.cobblemon.mod.common.block.chest.GildedState
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.block.entity.LootableContainerBlockEntity
|
||||
import net.minecraft.block.entity.ViewerCountManager
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.DoubleInventory
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.screen.GenericContainerScreenHandler
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.collection.DefaultedList
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type = Type.RED) : LootableContainerBlockEntity(CobblemonBlockEntities.GILDED_CHEST, pos, state) {
|
||||
var inventoryContents: DefaultedList<ItemStack> = DefaultedList.ofSize(NUM_SLOTS, ItemStack.EMPTY)
|
||||
val poseableState: GildedState = GildedState()
|
||||
|
||||
private val stateManager: ViewerCountManager = object : ViewerCountManager() {
|
||||
override fun onContainerOpen(world: World, pos: BlockPos, state: BlockState) {
|
||||
playSound(world, pos, state, CobblemonSounds.GILDED_CHEST_OPEN)
|
||||
}
|
||||
|
||||
override fun onContainerClose(world: World, pos: BlockPos, state: BlockState) {
|
||||
playSound(world, pos, state, CobblemonSounds.GILDED_CHEST_CLOSE)
|
||||
}
|
||||
|
||||
override fun onViewerCountUpdate(
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
oldViewerCount: Int,
|
||||
newViewerCount: Int
|
||||
) {
|
||||
this@GildedChestBlockEntity.onViewerCountUpdate(world, pos, state, oldViewerCount, newViewerCount)
|
||||
}
|
||||
|
||||
override fun isPlayerViewing(player: PlayerEntity): Boolean {
|
||||
if (player.currentScreenHandler is GenericContainerScreenHandler) {
|
||||
val inventory = (player.currentScreenHandler as GenericContainerScreenHandler).inventory
|
||||
return inventory === this@GildedChestBlockEntity || inventory is DoubleInventory && inventory.isPart(
|
||||
this@GildedChestBlockEntity
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getType(): BlockEntityType<*> = CobblemonBlockEntities.GILDED_CHEST
|
||||
override fun size() = NUM_SLOTS
|
||||
|
||||
override fun getContainerName() = Text.translatable("block.cobblemon.gilded_chest")
|
||||
override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory?) =
|
||||
GenericContainerScreenHandler.createGeneric9x3(syncId, playerInventory, this);
|
||||
|
||||
override fun onOpen(player: PlayerEntity) {
|
||||
if (!this.removed && !player.isSpectator && type != Type.FAKE) {
|
||||
stateManager.openContainer(player, this.getWorld(), this.getPos(), this.cachedState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClose(player: PlayerEntity) {
|
||||
if (!this.removed && !player.isSpectator) {
|
||||
stateManager.closeContainer(player, this.getWorld(), this.getPos(), this.cachedState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getInvStackList(): DefaultedList<ItemStack> = inventoryContents
|
||||
|
||||
override fun setInvStackList(list: DefaultedList<ItemStack>) {
|
||||
inventoryContents = list
|
||||
}
|
||||
|
||||
companion object {
|
||||
val NUM_SLOTS = 27
|
||||
fun playSound(world: World, pos: BlockPos, state: BlockState, sound: net.minecraft.sound.SoundEvent) {
|
||||
var d = pos.x.toDouble() + 0.5
|
||||
val e = pos.y.toDouble() + 0.5
|
||||
var f = pos.z.toDouble() + 0.5
|
||||
val direction = state.get(Properties.HORIZONTAL_FACING)
|
||||
d += direction.offsetX.toDouble() * 0.5
|
||||
f += direction.offsetZ.toDouble() * 0.5
|
||||
world.playSound(
|
||||
null,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
sound,
|
||||
SoundCategory.BLOCKS,
|
||||
0.5f,
|
||||
world.random.nextFloat() * 0.1f + 0.9f
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSyncedBlockEvent(type: Int, data: Int): Boolean {
|
||||
if (type == 1) {
|
||||
val isNowOpen = data > 0
|
||||
val wasOpen = poseableState.currentPose == "OPEN"
|
||||
val model = poseableState.currentModel ?: return true
|
||||
if (isNowOpen && !wasOpen) {
|
||||
model.moveToPose(null, poseableState, model.getPose("OPEN")!!)
|
||||
} else if (!isNowOpen && wasOpen) {
|
||||
model.moveToPose(null, poseableState, model.getPose("CLOSED")!!)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return super.onSyncedBlockEvent(type, data)
|
||||
}
|
||||
|
||||
fun onViewerCountUpdate(world: World, pos: BlockPos, state: BlockState, oldViewerCount: Int, newViewerCount: Int) {
|
||||
val block = state.block
|
||||
world.addSyncedBlockEvent(pos, block, 1, newViewerCount)
|
||||
}
|
||||
override fun writeNbt(nbt: NbtCompound?) {
|
||||
super.writeNbt(nbt)
|
||||
if (!serializeLootTable(nbt)) {
|
||||
Inventories.writeNbt(nbt, inventoryContents)
|
||||
}
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound?) {
|
||||
super.readNbt(nbt)
|
||||
inventoryContents= DefaultedList.ofSize(
|
||||
size(), ItemStack.EMPTY
|
||||
)
|
||||
if (!deserializeLootTable(nbt)) {
|
||||
Inventories.readNbt(nbt, inventoryContents)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -128,7 +128,7 @@ class RestorationTankBlockEntity(
|
||||
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
|
||||
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
|
||||
&& structure.createdPokemon == null
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return false
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
|
||||
if(canUtilize) {
|
||||
// See if there's room
|
||||
for (i in items.indices ) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -328,8 +328,8 @@ class FossilMultiblockStructure (
|
||||
tankBaseEntity.masterBlockPos = null
|
||||
tankTopEntity.masterBlockPos = null
|
||||
|
||||
// Drop fossils from machine as long as the machine is not running
|
||||
if (this.timeRemaining == TIME_TO_TAKE || this.timeRemaining == -1) {
|
||||
// Drop fossils from machine as long as the machine is not started or near completion
|
||||
if (this.timeRemaining == -1 || this.timeRemaining >= 20) {
|
||||
this.fossilInventory.forEach {
|
||||
val stack = ItemStack(it.item, 1)
|
||||
ItemScatterer.spawn(world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), stack)
|
||||
|
||||
@@ -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,20 +18,28 @@ 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
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
|
||||
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
|
||||
@@ -42,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
|
||||
@@ -102,9 +116,10 @@ object CobblemonClient {
|
||||
|
||||
this.registerBlockEntityRenderers()
|
||||
registerBlockRenderTypes()
|
||||
//registerColors()
|
||||
registerColors()
|
||||
registerFlywheelRenderers()
|
||||
this.registerEntityRenderers()
|
||||
registerHandledScreens()
|
||||
Berries.observable.subscribe {
|
||||
BerryModelRepository.patchModels()
|
||||
}
|
||||
@@ -115,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) {
|
||||
@@ -123,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}"
|
||||
}
|
||||
}
|
||||
@@ -145,23 +159,27 @@ 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,
|
||||
CobblemonBlocks.FOSSIL_ANALYZER,
|
||||
CobblemonBlocks.APRICORN_DOOR,
|
||||
CobblemonBlocks.APRICORN_TRAPDOOR,
|
||||
@@ -215,6 +233,8 @@ object CobblemonClient {
|
||||
CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST,
|
||||
CobblemonBlocks.DISPLAY_CASE
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
@@ -244,6 +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() {
|
||||
@@ -255,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) {
|
||||
@@ -262,12 +287,14 @@ object CobblemonClient {
|
||||
BedrockParticleEffectRepository.loadEffects(resourceManager)
|
||||
BedrockAnimationRepository.loadAnimations(
|
||||
resourceManager = resourceManager,
|
||||
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.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")
|
||||
}
|
||||
@@ -293,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
|
||||
)
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -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(
|
||||
@@ -61,9 +62,8 @@ class BattleGeneralActionSelection(
|
||||
}
|
||||
|
||||
addOption(rank++, battleLang("ui.run"), BattleGUI.runResource) {
|
||||
CobblemonClient.battle?.minimised = true
|
||||
MinecraftClient.getInstance().player?.sendMessage(battleLang("run_prompt"), false)
|
||||
playDownSound(MinecraftClient.getInstance().soundManager)
|
||||
|
||||
MinecraftClient.getInstance().player?.sendMessage(battleLang("flee"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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) {
|
||||
|
||||
}
|
||||
}
|
||||
+12
-20
@@ -46,7 +46,7 @@ import net.minecraft.text.Text
|
||||
* @author Qu
|
||||
* @since 2022-06-18
|
||||
*/
|
||||
class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter.title".asTranslated()) {
|
||||
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
|
||||
|
||||
companion object {
|
||||
// Size of UI at scale 1
|
||||
@@ -63,22 +63,14 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
private val doubleTypeBackground = cobblemonResource("textures/gui/starterselection/starterselection_type_slot2.png")
|
||||
}
|
||||
|
||||
lateinit var categories: List<RenderableStarterCategory>
|
||||
lateinit var currentCategory: RenderableStarterCategory
|
||||
lateinit var modelWidget: ModelWidget
|
||||
lateinit var currentPokemon: RenderablePokemon
|
||||
var currentSelection = 0
|
||||
lateinit var rightButton: ArrowButton
|
||||
lateinit var leftButton: ArrowButton
|
||||
lateinit var typeWidget: TypeWidget
|
||||
lateinit var selectionButton: SelectionButton
|
||||
lateinit var starterRoundaboutCenter: StarterRoundabout
|
||||
lateinit var starterRoundaboutLeft: StarterRoundabout
|
||||
lateinit var starterRoundaboutRight: StarterRoundabout
|
||||
|
||||
constructor(categories: List<RenderableStarterCategory>) : this() {
|
||||
this.categories = categories
|
||||
}
|
||||
private var currentSelection = 0
|
||||
private lateinit var currentCategory: RenderableStarterCategory
|
||||
private lateinit var modelWidget: ModelWidget
|
||||
private lateinit var currentPokemon: RenderablePokemon
|
||||
private lateinit var typeWidget: TypeWidget
|
||||
private lateinit var starterRoundaboutCenter: StarterRoundabout
|
||||
private lateinit var starterRoundaboutLeft: StarterRoundabout
|
||||
private lateinit var starterRoundaboutRight: StarterRoundabout
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
@@ -102,7 +94,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
)
|
||||
)
|
||||
|
||||
rightButton = ArrowButton(
|
||||
val rightButton = ArrowButton(
|
||||
pX = x + 183, pY = y + 151,
|
||||
pWidth = 9, pHeight = 14,
|
||||
right = true
|
||||
@@ -110,7 +102,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
right()
|
||||
}
|
||||
|
||||
leftButton = ArrowButton(
|
||||
val leftButton = ArrowButton(
|
||||
pX = x + 72, pY = y + 151,
|
||||
pWidth = 9, pHeight = 14,
|
||||
right = false
|
||||
@@ -137,7 +129,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
|
||||
addDrawableChild(modelWidget)
|
||||
|
||||
selectionButton = SelectionButton(
|
||||
val selectionButton = SelectionButton(
|
||||
pX = x + 106, pY = y + 124,
|
||||
pWidth = SelectionButton.BUTTON_WIDTH, pHeight = SelectionButton.BUTTON_HEIGHT
|
||||
) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.summary
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
@@ -59,7 +60,7 @@ import net.minecraft.text.Text
|
||||
*
|
||||
* @param selection The index the [party] will have as the base [selectedPokemon].
|
||||
*/
|
||||
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, selection: Int): Screen(Text.translatable("cobblemon.ui.summary.title")), Schedulable {
|
||||
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, private val selection: Int): Screen(Text.translatable("cobblemon.ui.summary.title")), Schedulable {
|
||||
|
||||
companion object {
|
||||
const val BASE_WIDTH = 331
|
||||
@@ -106,24 +107,27 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
|
||||
internal var selectedPokemon: Pokemon
|
||||
internal lateinit var selectedPokemon: Pokemon
|
||||
private lateinit var mainScreen: ClickableWidget
|
||||
lateinit var sideScreen: Element
|
||||
lateinit var modelWidget: ModelWidget
|
||||
lateinit var nicknameEntryWidget: NicknameEntryWidget
|
||||
private lateinit var modelWidget: ModelWidget
|
||||
private lateinit var nicknameEntryWidget: NicknameEntryWidget
|
||||
private val summaryTabs = mutableListOf<SummaryTab>()
|
||||
private var mainScreenIndex = INFO
|
||||
var sideScreenIndex = PARTY
|
||||
private val party = ArrayList(party)
|
||||
|
||||
init {
|
||||
/**
|
||||
* Initializes the Summary Screen
|
||||
*/
|
||||
override fun init() {
|
||||
super.init()
|
||||
if (this.party.isEmpty()) {
|
||||
throw IllegalArgumentException("Summary UI cannot display zero Pokemon")
|
||||
}
|
||||
if (this.party.size > 6) {
|
||||
throw IllegalArgumentException("Summary UI cannot display more than six Pokemon")
|
||||
}
|
||||
|
||||
val idealSelected = this.party[selection]
|
||||
if (idealSelected == null) {
|
||||
this.selectedPokemon = this.party.first { it != null }!!
|
||||
@@ -131,14 +135,6 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
this.selectedPokemon = idealSelected
|
||||
}
|
||||
this.listenToMoveSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Summary Screen
|
||||
*/
|
||||
public override fun init() {
|
||||
clearChildren()
|
||||
super.init()
|
||||
|
||||
val x = (width - BASE_WIDTH) / 2
|
||||
val y = (height - BASE_HEIGHT) / 2
|
||||
@@ -245,6 +241,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
rotationY = 325F,
|
||||
offsetY = -10.0
|
||||
)
|
||||
addDrawable(this.modelWidget)
|
||||
}
|
||||
|
||||
fun swapPartySlot(sourceIndex: Int, targetIndex: Int) {
|
||||
@@ -281,8 +278,12 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
listenToMoveSet()
|
||||
displayMainScreen(mainScreenIndex)
|
||||
children().find { it is EvolutionSelectScreen }?.let(this::remove)
|
||||
modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
|
||||
nicknameEntryWidget.setSelectedPokemon(selectedPokemon)
|
||||
if (this::modelWidget.isInitialized) {
|
||||
this.modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
|
||||
}
|
||||
if (this::nicknameEntryWidget.isInitialized) {
|
||||
this.nicknameEntryWidget.setSelectedPokemon(selectedPokemon)
|
||||
}
|
||||
}
|
||||
|
||||
private var moveSetSubscription: ObservableSubscription<MoveSet>? = null
|
||||
@@ -309,7 +310,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
*/
|
||||
private fun displayMainScreen(screen: Int) {
|
||||
// Get stat tab index if currently displaying stat screen
|
||||
var subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).statTabIndex else 0
|
||||
val subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).statTabIndex else 0
|
||||
|
||||
mainScreenIndex = screen
|
||||
if (::mainScreen.isInitialized) remove(mainScreen)
|
||||
@@ -408,10 +409,10 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
renderBackground(context)
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
//this.renderBackground(context)
|
||||
|
||||
schedulingTracker.update(pPartialTicks / 20F)
|
||||
schedulingTracker.update(delta / 20F)
|
||||
|
||||
val x = (width - BASE_WIDTH) / 2
|
||||
val y = (height - BASE_HEIGHT) / 2
|
||||
@@ -427,7 +428,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
height = PORTRAIT_SIZE
|
||||
)
|
||||
|
||||
modelWidget.render(context, pMouseX, pMouseY, pPartialTicks)
|
||||
//modelWidget.render(context, pMouseX, pMouseY, pPartialTicks)
|
||||
|
||||
// Render Base Resource
|
||||
blitk(
|
||||
@@ -576,29 +577,29 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
)
|
||||
|
||||
// Render all added Widgets
|
||||
super.render(context, pMouseX, pMouseY, pPartialTicks)
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
// Render Item Tooltip
|
||||
if (!heldItem.isEmpty) {
|
||||
val itemHovered = pMouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && pMouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.drawItemTooltip(MinecraftClient.getInstance().textRenderer, heldItem, pMouseX, pMouseY)
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.drawItemTooltip(MinecraftClient.getInstance().textRenderer, heldItem, mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this Screen should pause the Game in SinglePlayer
|
||||
*/
|
||||
override fun shouldPause(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun shouldPause(): Boolean = false
|
||||
|
||||
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
|
||||
return children().any { it.mouseScrolled(mouseX, mouseY, amount) }
|
||||
}
|
||||
|
||||
/*
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
return children().any { it.mouseClicked(mouseX, mouseY, button) }
|
||||
}
|
||||
*/
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (sideScreenIndex == MOVE_SWAP || sideScreenIndex == EVOLVE) sideScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
@@ -606,17 +607,17 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
}
|
||||
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
if ((keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_KP_ENTER) && nicknameEntryWidget.isFocused) { // Enter pressed
|
||||
nicknameEntryWidget.isFocused = false
|
||||
if ((keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_KP_ENTER)
|
||||
&& this::nicknameEntryWidget.isInitialized
|
||||
&& this.nicknameEntryWidget.isFocused
|
||||
) {
|
||||
this.focused = null
|
||||
}
|
||||
return nicknameEntryWidget.keyPressed(keyCode, scanCode, modifiers) || super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
override fun charTyped(chr: Char, modifiers: Int): Boolean {
|
||||
return nicknameEntryWidget.charTyped(chr, modifiers) || super.charTyped(chr, modifiers)
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-13
@@ -19,6 +19,7 @@ import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class SummaryButton(
|
||||
@@ -27,7 +28,7 @@ class SummaryButton(
|
||||
val buttonWidth: Number,
|
||||
val buttonHeight: Number,
|
||||
val clickAction: PressAction,
|
||||
private val text: MutableText? = null,
|
||||
private val text: MutableText = Text.empty(),
|
||||
private val resource: Identifier,
|
||||
private val activeResource: Identifier? = null,
|
||||
private val renderRequirement: ((button: SummaryButton) -> Boolean) = { true },
|
||||
@@ -68,18 +69,16 @@ class SummaryButton(
|
||||
)
|
||||
|
||||
// Render Text
|
||||
if (text != null) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = if (largeText) CobblemonResources.DEFAULT_LARGE else null,
|
||||
text = if (boldText) text.bold() else text,
|
||||
x = buttonX + (buttonWidth.toFloat() / 2),
|
||||
y = buttonY + (buttonHeight.toFloat() / 2) - ((TEXT_HEIGHT / 2) * textScale),
|
||||
scale = textScale,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = if (largeText) CobblemonResources.DEFAULT_LARGE else null,
|
||||
text = if (boldText) text.bold() else text,
|
||||
x = buttonX + (buttonWidth.toFloat() / 2),
|
||||
y = buttonY + (buttonHeight.toFloat() / 2) - ((TEXT_HEIGHT / 2) * textScale),
|
||||
scale = textScale,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.summary.featurerenderers
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* Renders an [IntSpeciesFeature] as a bar in the summary screen.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
class BarSummarySpeciesFeatureRenderer(
|
||||
override val name: String,
|
||||
val displayName: MutableText,
|
||||
val min: Int,
|
||||
val max: Int,
|
||||
val colour: Vec3d,
|
||||
val underlay: Identifier,
|
||||
val overlay: Identifier,
|
||||
val pokemon: Pokemon
|
||||
) : SummarySpeciesFeatureRenderer<IntSpeciesFeature> {
|
||||
override fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
|
||||
val value = feature.value
|
||||
val barRatio = (value - min) / (max - min).toFloat()
|
||||
val barWidth = MathHelper.ceil(barRatio * 108)
|
||||
|
||||
blitk(
|
||||
matrixStack = drawContext.matrices,
|
||||
texture = underlay,
|
||||
x = x,
|
||||
y = y,
|
||||
height = 28,
|
||||
width = 124
|
||||
)
|
||||
|
||||
val red = colour.x / 255F
|
||||
val green = colour.y / 255F
|
||||
val blue = colour.z / 255F
|
||||
|
||||
blitk(
|
||||
matrixStack = drawContext.matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 8,
|
||||
y = y + 16,
|
||||
height = 10,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = drawContext.matrices,
|
||||
texture = overlay,
|
||||
x = x / StatWidget.SCALE,
|
||||
y = (y + 16) / StatWidget.SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = StatWidget.SCALE
|
||||
)
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = drawContext,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = displayName.bold(),
|
||||
x = x + 62,
|
||||
y = y + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = drawContext,
|
||||
text = value.toString().text(),
|
||||
x = x + 11,
|
||||
y = y + 6,
|
||||
scale = StatWidget.SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = drawContext,
|
||||
text = "${MathHelper.floor(barRatio * 100)}%".text(),
|
||||
x = x + 113,
|
||||
y = y + 6,
|
||||
scale = StatWidget.SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.summary.featurerenderers
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
|
||||
/**
|
||||
* A renderer for some kind of [SynchronizedSpeciesFeature] so that it will display
|
||||
* in the summary screen of the Pokémon (under other stats).
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
|
||||
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
|
||||
val name: String
|
||||
/** Draws it at a particular position. */
|
||||
fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon, feature: T)
|
||||
fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon): Boolean {
|
||||
val feature = pokemon.getFeature<T>(name) ?: return false
|
||||
render(drawContext, x, y, pokemon, feature)
|
||||
return true
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.summary.widgets
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
@@ -23,15 +22,14 @@ import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class PartySlotWidget(
|
||||
private val pX: Number,
|
||||
private val pY: Number,
|
||||
pX: Number,
|
||||
pY: Number,
|
||||
private val partyWidget: PartyWidget,
|
||||
private val summary: Summary,
|
||||
private val pokemon: Pokemon?,
|
||||
|
||||
-2
@@ -26,9 +26,7 @@ class InfoBlockWidget(
|
||||
private val font: Identifier,
|
||||
) : SoundlessWidget(pX, pY, blockWidth, blockHeight, Text.literal("InfoBlockWidget")) {
|
||||
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = font,
|
||||
|
||||
+1
-2
@@ -15,14 +15,13 @@ import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class ReorderMoveButton(
|
||||
val pX: Int, val pY: Int,
|
||||
private val isUp: Boolean,
|
||||
onPress: PressAction
|
||||
): ButtonWidget((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal(""), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
): ButtonWidget((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.empty(), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 8
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SwapMoveButton(
|
||||
var move: MoveTemplate,
|
||||
var movesWidget: MovesWidget,
|
||||
onPress: PressAction
|
||||
): ButtonWidget((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal(""), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
): ButtonWidget((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.empty(), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
|
||||
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double): Boolean {
|
||||
return false
|
||||
|
||||
+94
-51
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
@@ -54,7 +56,7 @@ class StatWidget(
|
||||
|
||||
private const val WIDTH = 134
|
||||
private const val HEIGHT = 148
|
||||
private const val SCALE = 0.5F
|
||||
const val SCALE = 0.5F
|
||||
|
||||
private const val WHITE = 0x00FFFFFF
|
||||
private const val GREY = 0x00AAAAAA
|
||||
@@ -64,6 +66,7 @@ class StatWidget(
|
||||
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
|
||||
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
|
||||
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
|
||||
private val statsOtherBarTemplate = cobblemonResource("textures/gui/summary/summary_stats_other_bar.png")
|
||||
private val friendshipOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png")
|
||||
private val tabMarkerResource = cobblemonResource("textures/gui/summary/summary_stats_tab_marker.png")
|
||||
private val statIncreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_increase.png")
|
||||
@@ -84,6 +87,10 @@ class StatWidget(
|
||||
}
|
||||
|
||||
var statTabIndex = tabIndex
|
||||
val renderableFeatures = SpeciesFeatures
|
||||
.getFeaturesFor(pokemon.species)
|
||||
.filterIsInstance<SynchronizedSpeciesFeatureProvider<*>>()
|
||||
.mapNotNull { it.getRenderer(pokemon) }
|
||||
|
||||
private fun drawTriangle(
|
||||
colour: Vector3f,
|
||||
@@ -178,6 +185,74 @@ class StatWidget(
|
||||
// drawTriangle(colour, specialAttackPoint, centerPoint, hpPoint)
|
||||
}
|
||||
|
||||
private fun drawFriendship(moduleX: Int, moduleY: Int, matrices: MatrixStack, context: DrawContext, friendship: Int) {
|
||||
val barRatio = friendship / 255F
|
||||
val barWidth = ceil(barRatio * 108)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsOtherBarTemplate,
|
||||
x = moduleX,
|
||||
y = moduleY,
|
||||
height = 28,
|
||||
width = 124
|
||||
)
|
||||
|
||||
val red = 1
|
||||
val green: Number = if (pokemon.friendship >= 160) 0.28 else 0.56
|
||||
val blue: Number = if (pokemon.friendship >= 160) 0.4 else 0.64
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = moduleX + 8,
|
||||
y = moduleY + 18,
|
||||
height = 8,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = friendshipOverlayResource,
|
||||
x = moduleX / SCALE,
|
||||
y = (moduleY + 16) / SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.stats.friendship").bold(),
|
||||
x = moduleX + 62,
|
||||
y = moduleY + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = friendship.toString().text(),
|
||||
x = moduleX + 11,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = "${floor(barRatio * 100)}%".text(),
|
||||
x = moduleX + 113,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val renderChart = statTabIndex != OTHER
|
||||
@@ -328,60 +403,28 @@ class StatWidget(
|
||||
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
|
||||
}
|
||||
} else {
|
||||
// Friendship
|
||||
val friendshipBarWidthMax = 108
|
||||
val friendshipRatio = pokemon.friendship / 255F
|
||||
val friendshipBarWidth = ceil(friendshipRatio * friendshipBarWidthMax)
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 13,
|
||||
y = y + 27,
|
||||
height = 8,
|
||||
width = friendshipBarWidth,
|
||||
red = 0.92,
|
||||
green = if (pokemon.friendship >= 160) 0.28 else 0.7,
|
||||
blue = if (pokemon.friendship >= 160) 0.4 else 0.28
|
||||
)
|
||||
var drawY = y + 11
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = friendshipOverlayResource,
|
||||
x = (x + 5) / SCALE,
|
||||
y = (y + 26) / SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = SCALE
|
||||
)
|
||||
drawFriendship(x + 5, drawY, matrices, context, pokemon.friendship)
|
||||
drawY += 30
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.stats.friendship").bold(),
|
||||
x = x + 67,
|
||||
y = y + 12.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
for (renderableFeature in renderableFeatures) {
|
||||
val rendered = renderableFeature.render(
|
||||
drawContext = context,
|
||||
x = x + 5F,
|
||||
y = drawY.toFloat(),
|
||||
pokemon = pokemon
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = pokemon.friendship.toString().text(),
|
||||
x = x + 16,
|
||||
y = y + 16,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
if (rendered) {
|
||||
drawY += 30
|
||||
}
|
||||
}
|
||||
|
||||
// for (value in summaries) {
|
||||
// drawBarModule(x + 5, drawY, matrices, context, value)
|
||||
// }
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = "${floor(friendshipRatio * 100)}%".text(),
|
||||
x = x + 118,
|
||||
y = y + 16,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user