mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 06:36:25 -04:00
Compare commits
177
Commits
speed-lines
...
dynamax
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a077e74bf2 | ||
|
|
763f1de793 | ||
|
|
62a95b93c5 | ||
|
|
7991f70c09 | ||
|
|
70d393a899 | ||
|
|
5bace46aa6 | ||
|
|
c5fb411636 | ||
|
|
9a8159ee30 | ||
|
|
4217938ada | ||
|
|
4c5f88db06 | ||
|
|
cfd743c4c0 | ||
|
|
1bbfc6dce5 | ||
|
|
6f2235c751 | ||
|
|
50a0f2d858 | ||
|
|
27210512f6 | ||
|
|
53aa124de6 | ||
|
|
a18b54849f | ||
|
|
c868c63a5f | ||
|
|
31df7c92f4 | ||
|
|
3b65d1967e | ||
|
|
f30b15003f | ||
|
|
c4c1b7ac27 | ||
|
|
ea9287cfb5 | ||
|
|
02f845af5c | ||
|
|
3bb3a00cef | ||
|
|
f1ae4163c9 | ||
|
|
05957f2106 | ||
|
|
4b9ea1d4cd | ||
|
|
78dff75db9 | ||
|
|
dbab6404a1 | ||
|
|
e5c4240ae0 | ||
|
|
317112f9bb | ||
|
|
58fda2e261 | ||
|
|
3b442d387a | ||
|
|
2b575c888f | ||
|
|
6e5e572412 | ||
|
|
ce87d0dc48 | ||
|
|
d43c91129c | ||
|
|
d66b9c8d3b | ||
|
|
dc37b381a0 | ||
|
|
1206aaaf13 | ||
|
|
95bcb9a7b8 | ||
|
|
c7d879ec57 | ||
|
|
d5752982d7 | ||
|
|
f1439ec724 | ||
|
|
0cac5714ac | ||
|
|
8529bb119f | ||
|
|
b68b3ad55e | ||
|
|
70c6b74f0f | ||
|
|
5cb7a51163 | ||
|
|
a68b6928aa | ||
|
|
0c26cb026c | ||
|
|
6078e22b14 | ||
|
|
cb1226013d | ||
|
|
0c8318d317 | ||
|
|
8d80bcfd6b | ||
|
|
cfd4fa1785 | ||
|
|
86bd70177c | ||
|
|
ce668b31ff | ||
|
|
943d6e0c55 | ||
|
|
7d82a2f7c8 | ||
|
|
782ba524df | ||
|
|
c571d79035 | ||
|
|
054c22b926 | ||
|
|
d2fce5edb5 | ||
|
|
cba32b7443 | ||
|
|
4a81c24a3d | ||
|
|
776e629850 | ||
|
|
9afb8b04c2 | ||
|
|
90c74eddc8 | ||
|
|
3600178d3b | ||
|
|
66d8a01917 | ||
|
|
68e32b21b5 | ||
|
|
670882bb4d | ||
|
|
9c0708651f | ||
|
|
1b3bb3b5bc | ||
|
|
cd03ef68c3 | ||
|
|
69716b5050 | ||
|
|
015deb4eeb | ||
|
|
96023077ad | ||
|
|
d96c68b5b4 | ||
|
|
dc27867502 | ||
|
|
bfbea377c1 | ||
|
|
f5954fcb33 | ||
|
|
eaa909a9f2 | ||
|
|
5ba4d460b1 | ||
|
|
09143b7ad6 | ||
|
|
aa1e14b4ac | ||
|
|
0f29cdeaf9 | ||
|
|
b976b9dd68 | ||
|
|
fa94ca8033 | ||
|
|
f206352b6e | ||
|
|
cdee981933 | ||
|
|
f2fc32b6a9 | ||
|
|
f46b581327 | ||
|
|
ff1241304b | ||
|
|
dd746e6775 | ||
|
|
44956ec6da | ||
|
|
553ecc8509 | ||
|
|
7b0d488b7c | ||
|
|
3f7a5e1318 | ||
|
|
ce7485f557 | ||
|
|
b5ca5c29d4 | ||
|
|
43f60da22e | ||
|
|
0102c8b392 | ||
|
|
6bb5c5dadf | ||
|
|
dd21fe2ea3 | ||
|
|
656c46817a | ||
|
|
a373f437fa | ||
|
|
0fe3637034 | ||
|
|
4b4be0e2cc | ||
|
|
3e673ae8f0 | ||
|
|
4b3b7443d3 | ||
|
|
d4dc24ec49 | ||
|
|
f54bbe754f | ||
|
|
c3379ddf21 | ||
|
|
5983b17e87 | ||
|
|
8d0ee6dd6f | ||
|
|
a1c1c9bc90 | ||
|
|
87e4f94af5 | ||
|
|
43075f1cb8 | ||
|
|
e1b1e3d5e9 | ||
|
|
a7095b30a3 | ||
|
|
ac5a03eebc | ||
|
|
bd79b58571 | ||
|
|
5e1c233493 | ||
|
|
95e95afef3 | ||
|
|
bf9c54e539 | ||
|
|
b7addd0203 | ||
|
|
7e64938ec9 | ||
|
|
618f66c775 | ||
|
|
fd1d1075db | ||
|
|
1648926aa6 | ||
|
|
3ce22c0759 | ||
|
|
a786adee9e | ||
|
|
de89c11f24 | ||
|
|
f16d233693 | ||
|
|
54d2711583 | ||
|
|
e54f179ccf | ||
|
|
22441e0b09 | ||
|
|
8418bc87c8 | ||
|
|
94ad2f1250 | ||
|
|
bd4df4485e | ||
|
|
0ad45df8f5 | ||
|
|
03f377cf0a | ||
|
|
373092f572 | ||
|
|
8bfc0fefaf | ||
|
|
4e44807523 | ||
|
|
36251fea75 | ||
|
|
06206a7447 | ||
|
|
66e901f88d | ||
|
|
05f089d390 | ||
|
|
23df8f70cf | ||
|
|
bd25e8682a | ||
|
|
906cd54c98 | ||
|
|
a1897f69ca | ||
|
|
a88700969d | ||
|
|
91eb9e597e | ||
|
|
e762542e49 | ||
|
|
b3e5734f39 | ||
|
|
2d5296288f | ||
|
|
cab3f2bf38 | ||
|
|
31efdc4254 | ||
|
|
34aace8a1b | ||
|
|
a877ad5a69 | ||
|
|
5a8c090055 | ||
|
|
98ccf39728 | ||
|
|
3222e2ebca | ||
|
|
ba89ed1574 | ||
|
|
68ab953644 | ||
|
|
4d3030e20e | ||
|
|
2e324e65a1 | ||
|
|
a34b7481f5 | ||
|
|
d2a3905b03 | ||
|
|
16b3d76504 | ||
|
|
363fe76dd1 | ||
|
|
040a29f66e |
@@ -33,3 +33,4 @@ common/src/main/resources/assets/cobblemon/showdown
|
||||
# Generated files
|
||||
*/generated/resources/*
|
||||
/fabric/src/generated/resources/cobblemon-common.accesswidener
|
||||
/.ignored/
|
||||
+35
-7
@@ -3,16 +3,23 @@
|
||||
### Additions
|
||||
- Added over (number here) ruin structures, where you can find Pokémon-themed Armor Trims and Pottery Sherds, Tumblestones, and more.
|
||||
- Added (number here) fossil structures, where you can brush Suspicious Sand/Gravel for Fossils.
|
||||
- Added the Data Monitor, Fossil Compartment, and Restoration Tube. Placed in the correct formation, you can use these to create a Restoration Machine where you can bring fossils back to life.
|
||||
- Added the Data Monitor, Fossil Compartment, and Restoration Tank. Placed in the correct formation, you can use these to create a Restoration Machine where you can bring fossils back to life.
|
||||
- Added 3 variants of Tumblestones, which can be planted near Lava or Magma to grow harvestable Tumblestone Clusters.
|
||||
- Added Tumblestone Blocks, a storage block crafted from 9 Tumblestones.
|
||||
- Added Ancient Poké Balls, which are aesthetic variants crafted from Tumblestones.
|
||||
- Added the Ancient Feather, Wing, and Jet Balls, which fly further than regular Poké Balls.
|
||||
- Added the Ancient Heavy, Leaden, and Gigaton Balls, which are heavier and don't fly as far as regular Poké Balls. These will recieve more functionality in a future update.
|
||||
- Added the Ancient Heavy, Leaden, and Gigaton Balls, which are heavier and don't fly as far as regular Poké Balls. These will receive more functionality in a future update.
|
||||
- Added Original Trainer functionality. To edit the OT through commands, you must specify originaltrainertype=<Player/NPC>, and originaltrainer=<Username or UUID/NPC Name>
|
||||
- Added support for planting Apricorn trees, berries, and mints in [Botany Pots](https://modrinth.com/mod/botany-pots)
|
||||
- Added the hidden "True Vivillionaire" advancement.
|
||||
- The "Poké Ball" Vivillon variant is now obtainable by evolving a Spewpa after obtaining the "Aspiring Vivillionaire" advancement.
|
||||
- Added dynamic lighting support for any Species or Forms when using [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) or [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), at this time we don't ship with any defaults for this feature but addon creators can make use of this feature.
|
||||
- Added 6 Pokémon-themed Pottery Sherds, obtained from ruin structures.
|
||||
- Added a Pokémon-themed armor trim, obtained from ruin structures.
|
||||
- You can now find Nether Fire Stone Ore in, surprisingly, the Nether.
|
||||
- Added effects for many Poké Balls when sending out Pokémon or capturing.
|
||||
- Added a new send-out ball toss animation that showcases the ball used for the Pokémon.
|
||||
- Added simple move animations for Pokémon in battle with a proof of concept for more complicated animations.
|
||||
|
||||
### Changes
|
||||
- Pokémon now transition more smoothly between different poses.
|
||||
@@ -23,6 +30,8 @@
|
||||
- Renamed the "Vivillionaire" advancement to "Aspiring Vivillionaire"
|
||||
- Removed the Inferno, Forsaken, Poké Ball, and Void patterns from the Aspiring Vivillionaire advancement.
|
||||
- Updated movesets to add Gen 9 DLC moves.
|
||||
- Apricorn Sprouts can now be planted directly on Apricorn leaves to create a new Apricorn of the same color.
|
||||
- Added tooltips to interaction GUIs to make it clearer what they do.
|
||||
|
||||
### Fixes
|
||||
- Re-added the recipe for the Iron Vitamin.
|
||||
@@ -31,21 +40,26 @@
|
||||
- Fixed battle lang for Poison Touch Ability
|
||||
- Fixed Cubone not being able to evolve into Alolan Marowak.
|
||||
- Fixed Squawkabilly forms not being recognized.
|
||||
- Fixed the bounce animation for Poké Balls being delayed by like, a full second.
|
||||
- Note blocks now play the correct sound on the Apricorn Plank based blocks.
|
||||
- Fixed an issue with transformed parts in posers causing whacky positional issues.
|
||||
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
|
||||
- Adds a translation string name to the Empty Pokeball entity.
|
||||
- Fixed very many issues with Bedrock particle effects.
|
||||
|
||||
### Developer
|
||||
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (albeit in a quick-fix way).
|
||||
- Rebuilt the scheduling API to more clearly force side choices and allow more local temporal frames of reference for tasks.
|
||||
- Added dialogue API.
|
||||
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed).
|
||||
- Rebuilt the scheduling API to more clearly force side choices and temporal frames of reference for tasks.
|
||||
- Added dialogue API and data registry.
|
||||
- Opened up the ItemDropEntry class to allow for it to be extended.
|
||||
- Added a new "advancement" evolution variant that takes an advancement identifier and succeeds if the player has the advancement.
|
||||
- Made spawning API capable of non-entity spawning
|
||||
- Made PokemonProperties.parse more interoperable with Java.
|
||||
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
|
||||
- API to support dynamic lighting in any mod that implements this feature has been added to all Species & Forms, see LightingData class for details.
|
||||
|
||||
### Datapack & Resourcepack Creators
|
||||
- The maximum amount of fossils that can fit in the Fossil Compartment can be adjusted in the config.
|
||||
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
|
||||
- Custom fossils can be defined using a list of items and the resulting Pokémon. An example of `aerodactyl.json`:
|
||||
```JSON
|
||||
{
|
||||
@@ -64,6 +78,19 @@
|
||||
- Added 3 new item tags: `ancient_poke_balls`, `fossils`, and `tumblestones`.
|
||||
- Added a spawn rules system to modify general spawning behaviour, see the [wiki](https://wiki.cobblemon.com/index.php/Spawn_Rules) for more information.
|
||||
- Added dialogue datapack folder and /opendialogue command.
|
||||
- Added the item tag ``cobblemon:held/leaves_leftovers`` this can be used to flag apple-like items that can create leftovers when eaten.
|
||||
- You can now add support for dynamic lighting implementations, Cobblemon ships with a default implementation for [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) and the [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), add the following to any species or form:
|
||||
```JSON
|
||||
{
|
||||
"lightingData": {
|
||||
"lightLevel": 14,
|
||||
"_lightLevelCommentRemoveMe": "Above supports 0 to 15",
|
||||
"liquidGlowMode": "LAND",
|
||||
"_liquidGlowModeCommentRemoveMe": "Above supports LAND, UNDERWATER or BOTH"
|
||||
}
|
||||
}
|
||||
```
|
||||
- Following up on this change ``light_source`` was removed as a possible shoulder effect due to becoming unnecessary and never having had a default implementation.
|
||||
|
||||
## [1.4.1 (December 23rd, 2023)](#1-4-1)
|
||||
|
||||
@@ -81,6 +108,7 @@
|
||||
- Added cries to Beldum, Metang and Metagross.
|
||||
- Added a /bedrockparticle command to run Snowstorm-format particle effects.
|
||||
- Added data for Dipplin, Fezandipiti, Munkidori, Ogerpon, Okidogi, Poltchageist and Sinistcha.
|
||||
- Added additional nickname trigger "Grumm" for Inkay's evolution.
|
||||
|
||||
### Changes
|
||||
- Using Potions, Status Heals, Ethers, and Antidotes will now return a glass bottle
|
||||
@@ -998,4 +1026,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
-1
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
base
|
||||
id("cobblemon.root-conventions")
|
||||
id ("net.nemerosa.versioning") version "2.8.2"
|
||||
}
|
||||
|
||||
group = "com.cobblemon.mod"
|
||||
@@ -8,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"
|
||||
version = "$version-SNAPSHOT-${versioning.info.branch}-${versioning.info.build}"
|
||||
}
|
||||
@@ -33,6 +33,7 @@ dependencies {
|
||||
modApi(libs.molang)
|
||||
compileOnlyApi(libs.jeiApi)
|
||||
modCompileOnly(libs.adornFabric)
|
||||
modCompileOnly(libs.lambDynamicLights) { isTransitive = false }
|
||||
|
||||
// For Showdown
|
||||
modCompileOnly(libs.graal)
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.compat;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonEntities;
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokemon.FormData;
|
||||
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
|
||||
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
// Java class due to relying on entrypoint on Fabric side
|
||||
public class LambDynamicLightsCompat {
|
||||
|
||||
// This is all called on client side
|
||||
// These things are checked every tick so try to keep it as free as possible
|
||||
// That being said it's unlikely school Toshiba person is running dynamic lighting and shaders to enjoy this
|
||||
// Make sure not to call anything Fabric specific :)
|
||||
public static void hookCompat() {
|
||||
// Pokémon entities
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
CobblemonEntities.POKEMON,
|
||||
DynamicLightHandler.makeHandler(
|
||||
pokemon -> resolvedPokemonLightLevel(pokemon, false),
|
||||
pokemon -> true
|
||||
)
|
||||
);
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
CobblemonEntities.POKEMON,
|
||||
DynamicLightHandler.makeHandler(
|
||||
pokemon -> resolvedPokemonLightLevel(pokemon, true),
|
||||
pokemon -> false
|
||||
)
|
||||
);
|
||||
|
||||
// Shouldered Pokémon
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
EntityType.PLAYER,
|
||||
DynamicLightHandler.makeHandler(
|
||||
player -> resolvedShoulderLightLevel(player, false),
|
||||
player -> true
|
||||
)
|
||||
);
|
||||
DynamicLightHandlers.registerDynamicLightHandler(
|
||||
EntityType.PLAYER,
|
||||
DynamicLightHandler.makeHandler(
|
||||
player -> resolvedShoulderLightLevel(player, true),
|
||||
player -> false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int resolvedPokemonLightLevel(PokemonEntity pokemon, boolean underwater) {
|
||||
return extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
|
||||
}
|
||||
|
||||
private static int resolvedShoulderLightLevel(PlayerEntity player, boolean underwater) {
|
||||
final Pair<PokemonOnShoulderRenderer.ShoulderData, PokemonOnShoulderRenderer.ShoulderData> shoulderDataPair = PokemonOnShoulderRenderer.shoulderDataOf(player);
|
||||
final Optional<Integer> leftLightLevel = extractShoulderLightLevel(shoulderDataPair.getLeft(), underwater);
|
||||
final Optional<Integer> rightLightLevel = extractShoulderLightLevel(shoulderDataPair.getRight(), underwater);
|
||||
return Math.max(leftLightLevel.orElse(0), rightLightLevel.orElse(0));
|
||||
}
|
||||
|
||||
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
|
||||
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(form.getLightingData().getLightLevel());
|
||||
}
|
||||
|
||||
private static Optional<Integer> extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
|
||||
if (shoulderData == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return extractFormLightLevel(shoulderData.getForm(), underwater);
|
||||
}
|
||||
|
||||
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
|
||||
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.sherds.CobblemonSherds;
|
||||
import net.minecraft.block.DecoratedPotPatterns;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(DecoratedPotPatterns.class)
|
||||
public abstract class DecoratedPotPatternsMixin {
|
||||
@Inject(method = "fromSherd", at=@At("HEAD"), cancellable = true)
|
||||
private static void cobblemon$getCobblemonSherdTexture(Item sherd,
|
||||
CallbackInfoReturnable<RegistryKey<String>> cir) {
|
||||
if (CobblemonSherds.INSTANCE.getSherdToPattern().containsKey(sherd)) {
|
||||
cir.setReturnValue(CobblemonSherds.INSTANCE.getSherdToPattern().get(sherd));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,4 +40,5 @@ public class GameRendererMixin {
|
||||
}
|
||||
lastTime = newTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.api.snowstorm.ParticleMaterials;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.client.particle.ParticleManager;
|
||||
import net.minecraft.client.particle.ParticleTextureSheet;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(ParticleManager.class)
|
||||
final class ParticleManagerMixin {
|
||||
@Mutable
|
||||
@Final
|
||||
@Shadow
|
||||
private static List<ParticleTextureSheet> PARTICLE_TEXTURE_SHEETS;
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "<clinit>")
|
||||
private static void lodestone$addTypes(CallbackInfo ci) {
|
||||
PARTICLE_TEXTURE_SHEETS = ImmutableList.<ParticleTextureSheet>builder().addAll(PARTICLE_TEXTURE_SHEETS)
|
||||
.add(ParticleMaterials.INSTANCE.getADD(), ParticleMaterials.INSTANCE.getALPHA(), ParticleMaterials.INSTANCE.getBLEND(), ParticleMaterials.INSTANCE.getOPAQUE()).build();
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents;
|
||||
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
|
||||
import com.cobblemon.mod.common.api.storage.NoPokemonStoreException;
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore;
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon;
|
||||
import com.cobblemon.mod.common.util.CompoundTagExtensionsKt;
|
||||
import com.cobblemon.mod.common.util.DataKeys;
|
||||
@@ -22,11 +23,8 @@ import net.minecraft.entity.ItemEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.world.World;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@@ -40,8 +38,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.cobblemon.mod.common.util.LocalizationUtilsKt.lang;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class PlayerMixin extends LivingEntity {
|
||||
|
||||
@@ -144,7 +140,7 @@ public abstract class PlayerMixin extends LivingEntity {
|
||||
)
|
||||
public void onEatFood(World world, ItemStack stack, CallbackInfoReturnable<ItemStack> cir) {
|
||||
if (!getWorld().isClient) {
|
||||
if (stack.getItem() == Items.APPLE && getWorld().random.nextDouble() < Cobblemon.config.getAppleLeftoversChance()) {
|
||||
if (stack.isIn(CobblemonItemTags.LEAVES_LEFTOVERS) && getWorld().random.nextDouble() < Cobblemon.config.getAppleLeftoversChance()) {
|
||||
ItemStack leftovers = new ItemStack(CobblemonItems.LEFTOVERS);
|
||||
ServerPlayerEntity player = Objects.requireNonNull(getServer()).getPlayerManager().getPlayer(uuid);
|
||||
assert player != null;
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
|
||||
import com.cobblemon.mod.common.api.net.serializers.IdentifierDataSerializer
|
||||
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
|
||||
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
|
||||
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
|
||||
@@ -102,6 +103,7 @@ import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
|
||||
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.sherds.CobblemonSherds
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.trade.TradeManager
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -235,6 +237,7 @@ object Cobblemon {
|
||||
TrackedDataHandlerRegistry.register(Vec3DataSerializer)
|
||||
TrackedDataHandlerRegistry.register(StringSetDataSerializer)
|
||||
TrackedDataHandlerRegistry.register(PoseTypeDataSerializer)
|
||||
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
|
||||
|
||||
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
|
||||
}
|
||||
@@ -385,6 +388,8 @@ object Cobblemon {
|
||||
true
|
||||
).ifSuccessful { it.mute = true }
|
||||
}
|
||||
|
||||
CobblemonSherds.registerSherds()
|
||||
}
|
||||
|
||||
fun getLevel(dimension: RegistryKey<World>): World? {
|
||||
|
||||
@@ -12,9 +12,9 @@ import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PCBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilCompartmentBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilTubeBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilAnalyzerBlockEntity
|
||||
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.platform.PlatformRegistry
|
||||
@@ -48,21 +48,27 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val FOSSIL_MULTIBLOCK: BlockEntityType<FossilMultiblockEntity> = this.create("fossil_multiblock",
|
||||
BlockEntityType.Builder.create({ pos, state -> FossilMultiblockEntity(pos, state, FossilMultiblockBuilder(pos)) },
|
||||
CobblemonBlocks.FOSSIL_MONITOR
|
||||
CobblemonBlocks.MONITOR
|
||||
).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_TUBE: BlockEntityType<FossilTubeBlockEntity> = this.create("fossil_tube",
|
||||
BlockEntityType.Builder.create({ pos, state -> FossilTubeBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
|
||||
CobblemonBlocks.FOSSIL_TUBE
|
||||
val RESTORATION_TANK: BlockEntityType<RestorationTankBlockEntity> = this.create("restoration_tank",
|
||||
BlockEntityType.Builder.create({ pos, state -> RestorationTankBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
|
||||
CobblemonBlocks.RESTORATION_TANK
|
||||
).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_COMPARTMENT: BlockEntityType<FossilCompartmentBlockEntity> = this.create("fossil_compartment",
|
||||
BlockEntityType.Builder.create({ pos, state -> FossilCompartmentBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
|
||||
CobblemonBlocks.FOSSIL_COMPARTMENT
|
||||
val FOSSIL_ANALYZER: BlockEntityType<FossilAnalyzerBlockEntity> = this.create("fossil_analyzer",
|
||||
BlockEntityType.Builder.create({ pos, state -> FossilAnalyzerBlockEntity(pos, state, FossilMultiblockBuilder(pos)) },
|
||||
CobblemonBlocks.FOSSIL_ANALYZER
|
||||
).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val DYNITE_ORE: BlockEntityType<DyniteOreBlockEntity> = this.create("dynite_ore",
|
||||
BlockEntityType.Builder.create(::DyniteOreBlockEntity, CobblemonBlocks.DYNITE_ORE)
|
||||
.build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ package com.cobblemon.mod.common
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.block.*
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.fossilmachine.FossilCompartmentBlock
|
||||
import com.cobblemon.mod.common.block.fossilmachine.FossilMonitorBlock
|
||||
import com.cobblemon.mod.common.block.fossilmachine.FossilTubeBlock
|
||||
import com.cobblemon.mod.common.block.FossilAnalyzerBlock
|
||||
import com.cobblemon.mod.common.block.MonitorBlock
|
||||
import com.cobblemon.mod.common.block.RestorationTankBlock
|
||||
import com.cobblemon.mod.common.mixin.invoker.DoorBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.PressurePlateBlockInvoker
|
||||
import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
|
||||
@@ -57,6 +57,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
@JvmField
|
||||
val FIRE_STONE_ORE = evolutionStoneOre("fire_stone_ore")
|
||||
@JvmField
|
||||
val NETHER_FIRE_STONE_ORE = evolutionStoneOre("nether_fire_stone_ore")
|
||||
@JvmField
|
||||
val ICE_STONE_ORE = evolutionStoneOre("ice_stone_ore")
|
||||
@JvmField
|
||||
val LEAF_STONE_ORE = evolutionStoneOre("leaf_stone_ore")
|
||||
@@ -94,6 +96,15 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
@JvmField
|
||||
val DEEPSLATE_WATER_STONE_ORE = this.deepslateEvolutionStoneOre("deepslate_water_stone_ore")
|
||||
|
||||
@JvmField
|
||||
val DYNITE_ORE = this.create("dynite_ore", DyniteOreBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.BRIGHT_RED)
|
||||
.strength(4.5f, 3f)
|
||||
.requiresTool()
|
||||
.sounds(BlockSoundGroup.METAL)
|
||||
))
|
||||
|
||||
// Apricorns
|
||||
@JvmField
|
||||
val APRICORN_LOG = log("apricorn_log", arg2 = MapColor.BROWN)
|
||||
@@ -158,31 +169,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
|
||||
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@JvmField
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", GrowableStoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
|
||||
@JvmField
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", GrowableStoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", GrowableStoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(AbstractBlock.Settings.create()
|
||||
@@ -222,38 +233,41 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_MONITOR = create(
|
||||
"fossil_monitor",
|
||||
FossilMonitorBlock(
|
||||
val MONITOR = create(
|
||||
"monitor",
|
||||
MonitorBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.IRON_GRAY)
|
||||
.sounds(BlockSoundGroup.METAL)
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.strength(2f)
|
||||
.luminance { if (it.get(FossilMonitorBlock.PROGRESS) > 0) 15 else 0 }
|
||||
.requiresTool()
|
||||
.strength(5.0F, 6.0F)
|
||||
.luminance { if (it.get(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 15 else 0 }
|
||||
)
|
||||
)
|
||||
@JvmField
|
||||
val FOSSIL_COMPARTMENT = create(
|
||||
"fossil_compartment",
|
||||
FossilCompartmentBlock(
|
||||
val FOSSIL_ANALYZER = create(
|
||||
"fossil_analyzer",
|
||||
FossilAnalyzerBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.IRON_GRAY)
|
||||
.sounds(BlockSoundGroup.METAL)
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.strength(2f)
|
||||
.requiresTool()
|
||||
.strength(5.0F, 6.0F)
|
||||
.nonOpaque()
|
||||
)
|
||||
)
|
||||
@JvmField
|
||||
val FOSSIL_TUBE = create(
|
||||
"fossil_tube",
|
||||
FossilTubeBlock(
|
||||
val RESTORATION_TANK = create(
|
||||
"restoration_tank",
|
||||
RestorationTankBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.IRON_GRAY)
|
||||
.sounds(BlockSoundGroup.METAL)
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.strength(2f)
|
||||
.requiresTool()
|
||||
.strength(5.0F, 6.0F)
|
||||
.nonOpaque()
|
||||
)
|
||||
)
|
||||
|
||||
@@ -11,6 +11,8 @@ package com.cobblemon.mod.common
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatEntity
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonChestBoatEntity
|
||||
import com.cobblemon.mod.common.entity.fallingstar.FallingStarEntity
|
||||
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
|
||||
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -66,6 +68,24 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, RegistryKey
|
||||
.build(CHEST_BOAT_KEY.toString())
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GENERIC_BEDROCK_ENTITY_KEY = cobblemonResource("generic_bedrock")
|
||||
@JvmField
|
||||
val GENERIC_BEDROCK_ENTITY: EntityType<GenericBedrockEntity> = this.create(
|
||||
GENERIC_BEDROCK_ENTITY_KEY.path,
|
||||
EntityType.Builder.create({ _, world -> GenericBedrockEntity(world) }, SpawnGroup.MISC)
|
||||
.build(GENERIC_BEDROCK_ENTITY_KEY.toString())
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val FALLING_STAR_ENTITY_KEY = cobblemonResource("falling_star")
|
||||
@JvmField
|
||||
val FALLING_STAR_ENTITY: EntityType<FallingStarEntity> = this.create(
|
||||
FALLING_STAR_ENTITY_KEY.path,
|
||||
EntityType.Builder.create({ _, world -> FallingStarEntity(world) }, SpawnGroup.MISC)
|
||||
.build(FALLING_STAR_ENTITY_KEY.toString())
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val POKE_BOBBER_KEY = cobblemonResource("poke_bobber")
|
||||
@JvmField
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.*
|
||||
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
|
||||
import com.cobblemon.mod.common.item.battle.DireHitItem
|
||||
import com.cobblemon.mod.common.item.battle.GuardSpecItem
|
||||
import com.cobblemon.mod.common.item.battle.XStatItem
|
||||
@@ -38,6 +39,14 @@ import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.effect.StatusEffectInstance
|
||||
import net.minecraft.entity.effect.StatusEffects
|
||||
import net.minecraft.item.*
|
||||
import net.minecraft.item.BlockItem
|
||||
import net.minecraft.item.FoodComponent
|
||||
import net.minecraft.item.HangingSignItem
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.SignItem
|
||||
import net.minecraft.item.SmithingTemplateItem
|
||||
import net.minecraft.item.StewItem
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
@@ -171,19 +180,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val WHITE_APRICORN = apricornItem("white", ApricornItem(CobblemonBlocks.WHITE_APRICORN))
|
||||
|
||||
@JvmField
|
||||
val RED_APRICORN_SEED = apricornSeedItem("red", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING))
|
||||
val RED_APRICORN_SEED = apricornSeedItem("red", ApricornSeedItem(CobblemonBlocks.RED_APRICORN_SAPLING, CobblemonBlocks.RED_APRICORN))
|
||||
@JvmField
|
||||
val YELLOW_APRICORN_SEED = apricornSeedItem("yellow", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING))
|
||||
val YELLOW_APRICORN_SEED = apricornSeedItem("yellow", ApricornSeedItem(CobblemonBlocks.YELLOW_APRICORN_SAPLING, CobblemonBlocks.YELLOW_APRICORN))
|
||||
@JvmField
|
||||
val GREEN_APRICORN_SEED = apricornSeedItem("green", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING))
|
||||
val GREEN_APRICORN_SEED = apricornSeedItem("green", ApricornSeedItem(CobblemonBlocks.GREEN_APRICORN_SAPLING, CobblemonBlocks.GREEN_APRICORN))
|
||||
@JvmField
|
||||
val BLUE_APRICORN_SEED = apricornSeedItem("blue", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING))
|
||||
val BLUE_APRICORN_SEED = apricornSeedItem("blue", ApricornSeedItem(CobblemonBlocks.BLUE_APRICORN_SAPLING, CobblemonBlocks.BLUE_APRICORN))
|
||||
@JvmField
|
||||
val PINK_APRICORN_SEED = apricornSeedItem("pink", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING))
|
||||
val PINK_APRICORN_SEED = apricornSeedItem("pink", ApricornSeedItem(CobblemonBlocks.PINK_APRICORN_SAPLING, CobblemonBlocks.PINK_APRICORN))
|
||||
@JvmField
|
||||
val BLACK_APRICORN_SEED = apricornSeedItem("black", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING))
|
||||
val BLACK_APRICORN_SEED = apricornSeedItem("black", ApricornSeedItem(CobblemonBlocks.BLACK_APRICORN_SAPLING, CobblemonBlocks.BLACK_APRICORN))
|
||||
@JvmField
|
||||
val WHITE_APRICORN_SEED = apricornSeedItem("white", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING))
|
||||
val WHITE_APRICORN_SEED = apricornSeedItem("white", ApricornSeedItem(CobblemonBlocks.WHITE_APRICORN_SAPLING, CobblemonBlocks.WHITE_APRICORN))
|
||||
|
||||
@JvmField
|
||||
val APRICORN_LOG = blockItem("apricorn_log", CobblemonBlocks.APRICORN_LOG)
|
||||
@@ -224,11 +233,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", HangingSignItem(CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_TUBE = blockItem("fossil_tube", CobblemonBlocks.FOSSIL_TUBE)
|
||||
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
|
||||
@JvmField
|
||||
val FOSSIL_COMPARTMENT = blockItem("fossil_compartment", CobblemonBlocks.FOSSIL_COMPARTMENT)
|
||||
val FOSSIL_ANALYZER = blockItem("fossil_analyzer", CobblemonBlocks.FOSSIL_ANALYZER)
|
||||
@JvmField
|
||||
val FOSSIL_MONITOR = blockItem("fossil_monitor", CobblemonBlocks.FOSSIL_MONITOR)
|
||||
val MONITOR = blockItem("monitor", CobblemonBlocks.MONITOR)
|
||||
@JvmField
|
||||
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE)
|
||||
@JvmField
|
||||
@@ -568,6 +577,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val DEEPSLATE_WATER_STONE_ORE = blockItem("deepslate_water_stone_ore", CobblemonBlocks.DEEPSLATE_WATER_STONE_ORE)
|
||||
@JvmField
|
||||
val DRIPSTONE_MOON_STONE_ORE = blockItem("dripstone_moon_stone_ore", CobblemonBlocks.DRIPSTONE_MOON_STONE_ORE)
|
||||
@JvmField
|
||||
val NETHER_FIRE_STONE_ORE = blockItem("nether_fire_stone_ore", CobblemonBlocks.NETHER_FIRE_STONE_ORE)
|
||||
|
||||
@JvmField
|
||||
val DYNITE_ORE = blockItem("dynite_ore", CobblemonBlocks.DYNITE_ORE)
|
||||
|
||||
@JvmField
|
||||
val DAWN_STONE = noSettingsItem("dawn_stone")
|
||||
@JvmField
|
||||
@@ -761,6 +776,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val HELIX_SHERD = noSettingsItem("helix_sherd")
|
||||
@JvmField
|
||||
val NOSTALGIC_SHERD = noSettingsItem("nostalgic_sherd")
|
||||
@JvmField
|
||||
val SUSPICIOUS_SHERD = noSettingsItem("suspicious_sherd")
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE = this.create("tumblestone", TumblestoneItem(Item.Settings(), CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE))
|
||||
@@ -803,6 +820,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
|
||||
"automaton_armor_trim_smithing_template",
|
||||
SmithingTemplateItem.of(CobblemonArmorTrims.AUTOMATON)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val POKEROD = pokerodItem("pokerod")
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
|
||||
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
|
||||
import com.cobblemon.mod.common.client.net.animation.PlayPoseableAnimationHandler
|
||||
import com.cobblemon.mod.common.client.net.battle.BattleApplyPassResponseHandler
|
||||
import com.cobblemon.mod.common.client.net.battle.BattleCaptureEndHandler
|
||||
import com.cobblemon.mod.common.client.net.battle.BattleCaptureShakeHandler
|
||||
@@ -37,8 +38,10 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
|
||||
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
|
||||
@@ -48,7 +51,6 @@ import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
|
||||
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.sound.PokemonCryHandler
|
||||
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.spawn.SpawnExtraDataEntityHandler
|
||||
import com.cobblemon.mod.common.client.net.starter.StarterUIPacketHandler
|
||||
@@ -73,6 +75,7 @@ import com.cobblemon.mod.common.client.net.trade.TradeOfferNotificationHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
@@ -85,6 +88,8 @@ import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPack
|
||||
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.fossil.FossilRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.fossil.NaturalMaterialRegistrySyncPacket
|
||||
@@ -97,8 +102,8 @@ import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.Add
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.ClearEvolutionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.RemoveEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.PokemonCryPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnGenericBedrockPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.starter.OpenStarterUIPacket
|
||||
@@ -314,12 +319,15 @@ object CobblemonNetwork : NetworkManager {
|
||||
|
||||
// Effects
|
||||
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
|
||||
this.createClientBound(SpawnSnowstormEntityParticlePacket.ID, SpawnSnowstormEntityParticlePacket::decode, SpawnSnowstormEntityParticleHandler)
|
||||
this.createClientBound(RunPosableMoLangPacket.ID, RunPosableMoLangPacket::decode, RunPosableMoLangHandler)
|
||||
|
||||
// Hax
|
||||
this.createClientBound(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler)
|
||||
this.createClientBound(SpawnPokemonPacket.ID, SpawnPokemonPacket::decode, SpawnExtraDataEntityHandler())
|
||||
this.createClientBound(SpawnPokeballPacket.ID, SpawnPokeballPacket::decode, SpawnExtraDataEntityHandler())
|
||||
this.createClientBound(ToastPacket.ID, ToastPacket::decode, ToastPacketHandler)
|
||||
this.createClientBound(SpawnGenericBedrockPacket.ID, SpawnGenericBedrockPacket::decode, SpawnExtraDataEntityHandler())
|
||||
|
||||
// Trade packets
|
||||
this.createClientBound(TradeAcceptanceChangedPacket.ID, TradeAcceptanceChangedPacket::decode, TradeAcceptanceChangedHandler)
|
||||
@@ -337,7 +345,7 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler)
|
||||
|
||||
// Behaviours
|
||||
this.createClientBound(PokemonCryPacket.ID, PokemonCryPacket::decode, PokemonCryHandler)
|
||||
this.createClientBound(PlayPoseableAnimationPacket.ID, PlayPoseableAnimationPacket::decode, PlayPoseableAnimationHandler)
|
||||
|
||||
// Move select packets
|
||||
this.createClientBound(OpenMoveCallbackPacket.ID, OpenMoveCallbackPacket::decode, OpenMoveCallbackHandler)
|
||||
|
||||
@@ -55,6 +55,10 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
val POKE_BALL_SEND_OUT = this.create("poke_ball.send_out")
|
||||
@JvmField
|
||||
val POKE_BALL_RECALL = this.create("poke_ball.recall")
|
||||
@JvmField
|
||||
val POKE_BALL_THROW = this.create("poke_ball.throw")
|
||||
@JvmField
|
||||
val POKE_BALL_TRAIL = this.create("poke_ball.trail")
|
||||
|
||||
@JvmField
|
||||
val ITEM_USE = this.create("item.use")
|
||||
@@ -62,6 +66,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
val CAN_EVOLVE = this.create("pokemon.can_evolve")
|
||||
@JvmField
|
||||
val EVOLVING = this.create("pokemon.evolving")
|
||||
@JvmField
|
||||
val EVOLVE = this.create("evolution.evolve")
|
||||
|
||||
@JvmField
|
||||
val PVN_BATTLE = this.create("battle.pvn.default")
|
||||
@@ -123,6 +129,43 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
@JvmField
|
||||
val TUMBLESTONE_STEP = this.create("tumblestone.step")
|
||||
|
||||
@JvmField
|
||||
val IMPACT_NORMAL = this.create("impact.normal")
|
||||
@JvmField
|
||||
val IMPACT_BUG = this.create("impact.bug")
|
||||
@JvmField
|
||||
val IMPACT_DARK = this.create("impact.dark")
|
||||
@JvmField
|
||||
val IMPACT_DRAGON = this.create("impact.dragon")
|
||||
@JvmField
|
||||
val IMPACT_ELECTRIC = this.create("impact.electric")
|
||||
@JvmField
|
||||
val IMPACT_FAIRY = this.create("impact.fairy")
|
||||
@JvmField
|
||||
val IMPACT_FIGHTING = this.create("impact.fighting")
|
||||
@JvmField
|
||||
val IMPACT_FIRE = this.create("impact.fire")
|
||||
@JvmField
|
||||
val IMPACT_FLYING = this.create("impact.flying")
|
||||
@JvmField
|
||||
val IMPACT_GHOST = this.create("impact.ghost")
|
||||
@JvmField
|
||||
val IMPACT_GRASS = this.create("impact.grass")
|
||||
@JvmField
|
||||
val IMPACT_GROUND = this.create("impact.ground")
|
||||
@JvmField
|
||||
val IMPACT_ICE = this.create("impact.ice")
|
||||
@JvmField
|
||||
val IMPACT_POISON = this.create("impact.poison")
|
||||
@JvmField
|
||||
val IMPACT_PSYCHIC = this.create("impact.psychic")
|
||||
@JvmField
|
||||
val IMPACT_ROCK = this.create("impact.rock")
|
||||
@JvmField
|
||||
val IMPACT_STEEL = this.create("impact.steel")
|
||||
@JvmField
|
||||
val IMPACT_WATER = this.create("impact.water")
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
TUMBLESTONE_BREAK,
|
||||
|
||||
@@ -8,9 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.battles.model
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangEnvironment
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.Environment
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
@@ -18,6 +22,8 @@ import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
@@ -464,4 +470,13 @@ open class PokemonBattle(
|
||||
LOGGER.error("Missing interpretation on '{}' action: \nPublic » {}\nPrivate » {}", publicMessage.id, publicMessage.rawMessage, privateMessage.rawMessage)
|
||||
return Text.literal("Missing interpretation on '${publicMessage.id}' action please report to the developers").red()
|
||||
}
|
||||
|
||||
fun addQueryFunctions(queryStruct: QueryStruct): QueryStruct {
|
||||
queryStruct.addFunction("pvp") { DoubleValue(isPvP) }
|
||||
queryStruct.addFunction("pvn") { DoubleValue(isPvN) }
|
||||
queryStruct.addFunction("pvw") { DoubleValue(isPvW) }
|
||||
queryStruct.addFunction("has_rule") { params -> DoubleValue(params.getString(0) in format.ruleSet) }
|
||||
|
||||
return queryStruct
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.PokemonInteractionGUICreationEvent
|
||||
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
|
||||
import com.cobblemon.mod.common.api.events.world.BigRootPropagatedEvent
|
||||
@@ -100,6 +101,9 @@ object CobblemonEvents {
|
||||
@JvmField
|
||||
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
|
||||
|
||||
@JvmField
|
||||
/** CLIENT ONLY! */
|
||||
val POKEMON_INTERACTION_GUI_CREATION = EventObservable<PokemonInteractionGUICreationEvent>()
|
||||
@JvmField
|
||||
val POKEMON_ENTITY_SAVE = EventObservable<PokemonEntitySaveEvent>()
|
||||
@JvmField
|
||||
@@ -153,4 +157,8 @@ object CobblemonEvents {
|
||||
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
|
||||
@JvmField
|
||||
val BIG_ROOT_PROPAGATED = CancelableObservable<BigRootPropagatedEvent>()
|
||||
@JvmField
|
||||
val HELD_ITEM_PRE = CancelableObservable<HeldItemEvent.Pre>()
|
||||
@JvmField
|
||||
val HELD_ITEM_POST = EventObservable<HeldItemEvent.Post>()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
/**
|
||||
* The base for all the events related to held items.
|
||||
*
|
||||
* @see [Pre]
|
||||
* @see [Post]
|
||||
*/
|
||||
interface HeldItemEvent {
|
||||
|
||||
/**
|
||||
* The [Pokemon] triggering this event.
|
||||
*/
|
||||
val pokemon: Pokemon
|
||||
|
||||
/**
|
||||
* Fired at the start of [Pokemon.swapHeldItem].
|
||||
*
|
||||
* This event should be used to mutate the results of this transaction.
|
||||
*
|
||||
* Canceling this event will prevent the operation and causes the return to be item attempted to be given originally.
|
||||
*
|
||||
* @property pokemon The [Pokemon] triggering this event.
|
||||
* @property receiving The [ItemStack] being sent received from the transaction. By default, this is the item that triggered the interaction.
|
||||
* @property returning The [ItemStack] being sent back from the transaction. By default, this is the currently held item.
|
||||
* @property decrement If the operation should decrement the [receiving] [ItemStack.count], this is handled in the implementation.
|
||||
*
|
||||
* @see [Post]
|
||||
*/
|
||||
data class Pre(override val pokemon: Pokemon, var receiving: ItemStack, var returning: ItemStack, var decrement: Boolean) : HeldItemEvent, Cancelable()
|
||||
|
||||
/**
|
||||
* Fired at the end of [Pokemon.swapHeldItem].
|
||||
*
|
||||
* @property pokemon The [Pokemon] triggering this event.
|
||||
* @property received The [ItemStack] considered as received and set to the [pokemon]. This is a copy and mutation will not be taken into account.
|
||||
* @property returned The [ItemStack] returned by the method call. This is a copy and mutation will not be taken into account.
|
||||
* @property decremented If the [received] [ItemStack] got decremented.
|
||||
*
|
||||
* @see [Pre]
|
||||
*/
|
||||
data class Post(override val pokemon: Pokemon, val received: ItemStack, val returned: ItemStack, val decremented: Boolean) : HeldItemEvent
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.interaction
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.interact.wheel.InteractWheelOption
|
||||
import com.cobblemon.mod.common.client.gui.interact.wheel.Orientation
|
||||
import com.google.common.collect.Multimap
|
||||
import java.util.UUID
|
||||
|
||||
data class PokemonInteractionGUICreationEvent(
|
||||
val pokemonID: UUID,
|
||||
val mountShoulder: Boolean,
|
||||
val options: Multimap<Orientation, InteractWheelOption>
|
||||
) {
|
||||
fun addFillingOption(option: InteractWheelOption) {
|
||||
options.put(getNextFreeOrientation(), option)
|
||||
}
|
||||
|
||||
fun addOption(orientation: Orientation, option: InteractWheelOption) {
|
||||
options.put(orientation, option)
|
||||
}
|
||||
|
||||
private fun getNextFreeOrientation(): Orientation {
|
||||
var largest = Orientation.TOP_LEFT
|
||||
for(orientation in Orientation.values()) {
|
||||
if(!options.containsKey(orientation)) {
|
||||
return orientation
|
||||
} else {
|
||||
if(options.get(orientation).size < options.get(largest).size) {
|
||||
largest = orientation
|
||||
}
|
||||
}
|
||||
}
|
||||
return largest
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,8 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.util.isInt
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import net.minecraft.block.Block
|
||||
@@ -51,7 +52,30 @@ object MoLangFunctions {
|
||||
"is_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() != null) },
|
||||
"to_number" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0) },
|
||||
"to_int" to java.util.function.Function { params -> DoubleValue(params.get<MoValue>(0).asString().toIntOrNull() ?: 0) },
|
||||
"to_string" to java.util.function.Function { params -> StringValue(params.get<MoValue>(0).asString()) }
|
||||
"to_string" to java.util.function.Function { params -> StringValue(params.get<MoValue>(0).asString()) },
|
||||
"do_effect_walks" to java.util.function.Function { _ ->
|
||||
DoubleValue(Cobblemon.config.walkingInBattleAnimations)
|
||||
},
|
||||
"random" to java.util.function.Function { params ->
|
||||
val options = mutableListOf<MoValue>()
|
||||
var index = 0
|
||||
while (params.contains(index)) {
|
||||
options.add(params.get(index))
|
||||
index++
|
||||
}
|
||||
return@Function options.random() // Can throw an exception if they specified no args. They'd be idiots though.
|
||||
},
|
||||
"curve" to java.util.function.Function { params ->
|
||||
val curveName = params.getString(0)
|
||||
val curve = WaveFunctions.functions[curveName] ?: throw IllegalArgumentException("Unknown curve: $curveName")
|
||||
return@Function ObjectValue(curve)
|
||||
},
|
||||
"array" to java.util.function.Function { params ->
|
||||
val values = params.params
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
|
||||
return@Function array
|
||||
}
|
||||
)
|
||||
val biomeFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
val worldFunctions = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
@@ -110,6 +134,11 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T : QueryStruct> T.addFunction(name: String, function: java.util.function.Function<MoParams, Any>): T {
|
||||
this.functions[name] = function
|
||||
return this
|
||||
}
|
||||
|
||||
fun MoLangEnvironment.getQueryStruct(name: String = "query") = structs.getOrPut(name) { QueryStruct(hashMapOf()) } as QueryStruct
|
||||
|
||||
fun MoLangRuntime.setup(): MoLangRuntime {
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.MoStruct
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategory
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
@@ -45,8 +51,22 @@ open class MoveTemplate(
|
||||
val pp: Int,
|
||||
val priority: Int,
|
||||
val critRatio: Double,
|
||||
val effectChances: Array<Double>
|
||||
val effectChances: Array<Double>,
|
||||
val actionEffect: ActionEffectTimeline?
|
||||
) {
|
||||
val struct: MoStruct by lazy {
|
||||
QueryStruct(hashMapOf())
|
||||
.addFunction("name") { StringValue(name) }
|
||||
.addFunction("type") { StringValue(elementalType.name) }
|
||||
.addFunction("damage_category") { StringValue(damageCategory.name) }
|
||||
.addFunction("power") { DoubleValue(power) }
|
||||
.addFunction("target") { StringValue(target.name) }
|
||||
.addFunction("accuracy") { DoubleValue(accuracy) }
|
||||
.addFunction("pp") { DoubleValue(pp) }
|
||||
.addFunction("priority") { DoubleValue(priority) }
|
||||
.addFunction("crit_ratio") { DoubleValue(critRatio) }
|
||||
}
|
||||
|
||||
|
||||
val displayName: MutableText
|
||||
get() = lang("move.$name")
|
||||
@@ -65,7 +85,8 @@ open class MoveTemplate(
|
||||
pp = 5,
|
||||
priority = 0,
|
||||
critRatio = 0.0,
|
||||
effectChances = emptyArray()
|
||||
effectChances = emptyArray(),
|
||||
actionEffect = null
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -10,12 +10,15 @@ package com.cobblemon.mod.common.api.moves
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.categories.DamageCategories
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.MoveTarget
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
@@ -72,7 +75,18 @@ object Moves : DataRegistry {
|
||||
effectChances += secondaryMember.get("chance").asDouble
|
||||
}
|
||||
}
|
||||
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray())
|
||||
val actionEffect = ActionEffects.actionEffects[id.asIdentifierDefaultingNamespace()]
|
||||
?: run {
|
||||
ActionEffects.actionEffects["generic_move".asIdentifierDefaultingNamespace()]
|
||||
// if (damageCategory == DamageCategories.STATUS) {
|
||||
// ActionEffects.actionEffects[cobblemonResource("status")]
|
||||
// } else {
|
||||
// val type = elementalType.name.lowercase()
|
||||
// val category = damageCategory.name.lowercase()
|
||||
// ActionEffects.actionEffects["${category}_$type".asIdentifierDefaultingNamespace()]
|
||||
// }
|
||||
}
|
||||
val move = MoveTemplate(id, num, elementalType, damageCategory, power, target, accuracy, pp, priority, critRatio, effectChances.toTypedArray(), actionEffect)
|
||||
this.register(move)
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Caught exception trying to resolve the move '{}'", id, e)
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
/**
|
||||
* An action effect will run and execute a series of 'keyframes', with each running once
|
||||
* the previous has completed. The entire effect is considered complete for the given context
|
||||
* once all the 'holds' provided in the context have been cleared or once every keyframe
|
||||
* has finished.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 20th, 2023
|
||||
*/
|
||||
class ActionEffectTimeline(
|
||||
val timeline: List<ActionEffectKeyframe> = mutableListOf(),
|
||||
val condition: Expression = "true".asExpression()
|
||||
) {
|
||||
companion object {
|
||||
val NONE = ActionEffectTimeline()
|
||||
}
|
||||
|
||||
fun run(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
return if (timeline.isEmpty() || !context.runtime.resolveBoolean(condition)) {
|
||||
CompletableFuture.completedFuture(Unit)
|
||||
} else {
|
||||
val finalFuture = CompletableFuture<Unit>()
|
||||
// .toList copy because I'm paranoid about iterators being trying to share between identical effects playing
|
||||
chainKeyframes(context, timeline.toList().iterator(), finalFuture)
|
||||
finalFuture
|
||||
}
|
||||
}
|
||||
|
||||
fun chainKeyframes(context: ActionEffectContext, iterator: Iterator<ActionEffectKeyframe>, finalFuture: CompletableFuture<Unit>) {
|
||||
if (!iterator.hasNext()) {
|
||||
finalFuture.complete(Unit)
|
||||
} else {
|
||||
val keyframe = iterator.next()
|
||||
context.currentKeyframes.add(keyframe)
|
||||
keyframe.play(context)
|
||||
.thenRun { context.currentKeyframes.remove(keyframe) }
|
||||
.thenApply { chainKeyframes(context, iterator, finalFuture) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ActionEffectContext(
|
||||
val actionEffect: ActionEffectTimeline,
|
||||
val holds: MutableSet<String> = mutableSetOf(),
|
||||
val providers: MutableList<Any> = mutableListOf(),
|
||||
val runtime: MoLangRuntime,
|
||||
var canBeInterrupted: Boolean = false,
|
||||
var interrupted: Boolean = false,
|
||||
var currentKeyframes: MutableList<ActionEffectKeyframe> = mutableListOf()
|
||||
) {
|
||||
inline fun <reified T> findOneProvider() = providers.filterIsInstance<T>().firstOrNull()
|
||||
|
||||
|
||||
}
|
||||
|
||||
class UsersProvider(users: List<Entity>): EntityProvider {
|
||||
override val entities = users
|
||||
constructor(vararg users: Entity): this(users.toList())
|
||||
}
|
||||
|
||||
class TargetsProvider(targets: List<Entity>): EntityProvider {
|
||||
override val entities = targets
|
||||
constructor(vararg targets: Entity): this(targets.toList())
|
||||
}
|
||||
|
||||
interface EntityProvider {
|
||||
val entities: List<Entity>
|
||||
}
|
||||
|
||||
//class MoveAnimationKeyframe(
|
||||
// val sound: Identifier? = null,
|
||||
// val animation: List<AnimationWithMoments> = listOf(),
|
||||
// val effects: List<ActionEffect> = listOf()
|
||||
//)
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.keyframes.*
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.*
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.awt.Color
|
||||
import net.minecraft.predicate.NumberRange
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* Data registry containing all of the [ActionEffectTimeline]s that can be triggered from various actions.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 21st, 2023
|
||||
*/
|
||||
object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
|
||||
override val id: Identifier = cobblemonResource("action_effects")
|
||||
override val type: ResourceType = ResourceType.SERVER_DATA
|
||||
override val observable = SimpleObservable<ActionEffects>()
|
||||
|
||||
init {
|
||||
ActionEffectKeyframe.register<AnimationActionEffectKeyframe>("animation")
|
||||
ActionEffectKeyframe.register<EntityMoLangActionEffectKeyframe>("entity_molang")
|
||||
ActionEffectKeyframe.register<MoLangActionEffectKeyframe>("molang")
|
||||
ActionEffectKeyframe.register<ParallelActionEffectKeyframe>("parallel")
|
||||
ActionEffectKeyframe.register<CanInterruptActionEffectKeyframe>("can_interrupt")
|
||||
ActionEffectKeyframe.register<CannotInterruptActionEffectKeyframe>("cannot_interrupt")
|
||||
ActionEffectKeyframe.register<RemoveHoldsActionEffectKeyframe>("remove_holds")
|
||||
ActionEffectKeyframe.register<AddHoldsActionEffectKeyframe>("add_holds")
|
||||
ActionEffectKeyframe.register<MoveToTargetActionEffectKeyframe>("move_to_target")
|
||||
ActionEffectKeyframe.register<ReturnToPositionActionEffectKeyframe>("return_to_position")
|
||||
ActionEffectKeyframe.register<PauseActionEffectKeyframe>("pause")
|
||||
ActionEffectKeyframe.register<SavePositionActionEffectKeyframe>("save_position")
|
||||
ActionEffectKeyframe.register<ForkActionEffectKeyframe>("fork")
|
||||
ActionEffectKeyframe.register<SequenceActionEffectKeyframe>("sequence")
|
||||
ActionEffectKeyframe.register<RunActionEffectKeyframe>("run_action_effect")
|
||||
ActionEffectKeyframe.register<EntityParticlesActionEffectKeyframe>("entity_particles")
|
||||
ActionEffectKeyframe.register<EntitySoundActionEffectKeyframe>("entity_sound")
|
||||
}
|
||||
|
||||
override val gson = GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ActionEffectKeyframe::class.java, ActionEffectKeyframeAdapter)
|
||||
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
|
||||
.registerTypeAdapter(Box::class.java, BoxAdapter)
|
||||
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, VerboseIntRangeAdapter)
|
||||
.registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
|
||||
.registerTypeAdapter(Stat::class.java, CobblemonStatTypeAdapter)
|
||||
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(
|
||||
TypeToken.getParameterized(
|
||||
TypeToken.get(List::class.java).type,
|
||||
TypeToken.get(ActionEffectKeyframe::class.java).type
|
||||
).type,
|
||||
SingleToPluralAdapter(ActionEffectKeyframe::class.java) { it }
|
||||
)
|
||||
.create()
|
||||
|
||||
override val typeToken: TypeToken<ActionEffectTimeline> = TypeToken.get(ActionEffectTimeline::class.java)
|
||||
override val resourcePath = "action_effects"
|
||||
|
||||
val actionEffects = mutableMapOf<Identifier, ActionEffectTimeline>()
|
||||
override fun reload(data: Map<Identifier, ActionEffectTimeline>) {
|
||||
actionEffects.clear()
|
||||
actionEffects.putAll(data)
|
||||
observable.emit(this)
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* A single component of an action effect. When played it must return a completable future that completes
|
||||
* when the action this keyframe represents is finished. [interrupt] is provided for when something successfully
|
||||
* interrupts the effect so that if any cancellation logic is necessary, it can be provided.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 26th, 2023
|
||||
*/
|
||||
interface ActionEffectKeyframe {
|
||||
companion object {
|
||||
val types = mutableMapOf<String, Class<out ActionEffectKeyframe>>()
|
||||
|
||||
inline fun <reified T : ActionEffectKeyframe> register(type: String) {
|
||||
types[type] = T::class.java
|
||||
}
|
||||
}
|
||||
|
||||
fun play(context: ActionEffectContext): CompletableFuture<Unit>
|
||||
fun interrupt(context: ActionEffectContext) {}
|
||||
fun skip(): CompletableFuture<Unit> = CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
+27
@@ -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.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* An [ActionEffectKeyframe] that adds some holds from the context for future code to recognize as blocking some
|
||||
* functionality from proceeding.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 27th, 2023
|
||||
*/
|
||||
class AddHoldsActionEffectKeyframe : ActionEffectKeyframe {
|
||||
val holds = mutableSetOf<String>()
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
context.holds.addAll(holds)
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
|
||||
class AnimationActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
|
||||
var delay = "0".asExpressionLike()
|
||||
var visibilityRange = 200
|
||||
var animation: Set<String> = setOf("physical")
|
||||
var variables: List<Expression> = listOf()
|
||||
override val entityCondition = "q.entity.is_user".asExpressionLike()
|
||||
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val entities = context.providers
|
||||
.filterIsInstance<EntityProvider>()
|
||||
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
|
||||
|
||||
val expressions = variables.map { it.originalString }.toSet()
|
||||
|
||||
// Treat them as expressions if possible but otherwise yeah just send them as strings
|
||||
val animation = animation.map {
|
||||
try {
|
||||
it.asExpressionLike().resolveString(context.runtime).takeIf { it != "0.0" } ?: it
|
||||
} catch (e: Exception) {
|
||||
it
|
||||
}
|
||||
}.toSet()
|
||||
|
||||
for (entity in entities) {
|
||||
val world = entity.world as ServerWorld
|
||||
val players = world.getPlayers { it.distanceTo(entity) <= visibilityRange }
|
||||
val pkt = PlayPoseableAnimationPacket(entity.id, animation = animation, expressions = expressions)
|
||||
players.forEach { it.sendPacket(pkt) }
|
||||
}
|
||||
|
||||
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class CanInterruptActionEffectKeyframe : ConditionalActionEffectKeyframe() {
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
context.canBeInterrupted = true
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class CannotInterruptActionEffectKeyframe : ConditionalActionEffectKeyframe() {
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
context.canBeInterrupted = true
|
||||
return CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
+27
@@ -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.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
abstract class ConditionalActionEffectKeyframe : ActionEffectKeyframe {
|
||||
var condition: ExpressionLike = "true".asExpressionLike()
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
return if (condition.resolveBoolean(context.runtime)) {
|
||||
playWhenTrue(context)
|
||||
} else {
|
||||
skip()
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit>
|
||||
}
|
||||
+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.moves.animations.keyframes
|
||||
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
/**
|
||||
* An action effect keyframe that plays for all entities for which the condition is true.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 21st, 2024
|
||||
*/
|
||||
interface EntityConditionalActionEffectKeyframe {
|
||||
val entityCondition: ExpressionLike
|
||||
fun test(context: ActionEffectContext, entity: Entity, isUser: Boolean): Boolean {
|
||||
// TODO this should use the entity's own query struct
|
||||
context.runtime.environment.getQueryStruct().addFunction("entity") {
|
||||
QueryStruct(hashMapOf())
|
||||
.addFunction("uuid") { StringValue(entity.uuidAsString) }
|
||||
.addFunction("is_user") { DoubleValue(isUser) }
|
||||
}
|
||||
return entityCondition.resolveBoolean(context.runtime)
|
||||
}
|
||||
}
|
||||
+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.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
|
||||
/**
|
||||
* An action effect keyframe that will run client-side MoLang for all entities that fit the entity condition.
|
||||
*
|
||||
* This MoLang executes within that entity's runtime, meaning you can trigger animations or locational particles
|
||||
* or sounds.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 30th, 2023
|
||||
*/
|
||||
class EntityMoLangActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
|
||||
var delay = "0".asExpressionLike()
|
||||
val expressions = mutableSetOf<String>()
|
||||
val visibilityRange = 200
|
||||
override val entityCondition: ExpressionLike = "q.entity.is_user".asExpressionLike()
|
||||
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val entities = context.providers
|
||||
.filterIsInstance<EntityProvider>()
|
||||
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
|
||||
|
||||
for (entity in entities) {
|
||||
val world = entity.world as ServerWorld
|
||||
val players = world.getPlayers { it.distanceTo(entity) <= visibilityRange }
|
||||
val pkt = RunPosableMoLangPacket(entityId = entity.id, expressions = expressions)
|
||||
players.forEach { it.sendPacket(pkt) }
|
||||
}
|
||||
|
||||
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.entity.Poseable
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
|
||||
/**
|
||||
* Spawns particles on the entity based on the given particle effect and locator.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 21st, 2024
|
||||
*/
|
||||
class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
|
||||
override val entityCondition = "q.entity.is_user".asExpressionLike()
|
||||
var effect: String? = null
|
||||
var locator: String = "root"
|
||||
val delay: ExpressionLike = "0".asExpressionLike()
|
||||
val visibilityRange = 200
|
||||
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val entities = context.providers
|
||||
.filterIsInstance<EntityProvider>()
|
||||
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
|
||||
|
||||
val effectIdentifier = try {
|
||||
effect?.asExpressionLike()?.resolveString(context.runtime)
|
||||
} catch (e: Exception) {
|
||||
effect
|
||||
}?.asIdentifierDefaultingNamespace() ?: return skip()
|
||||
|
||||
entities.filter { it is Poseable }.forEach { entity ->
|
||||
val packet = SpawnSnowstormEntityParticlePacket(effectIdentifier, entity.id, locator)
|
||||
val players = (entity.world as ServerWorld).getPlayers { it.distanceTo(entity) <= visibilityRange }
|
||||
packet.sendToPlayers(players)
|
||||
}
|
||||
|
||||
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
|
||||
/**
|
||||
* An action effect keyframe that plays a positioned sound for all matching entities.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 21st, 2024
|
||||
*/
|
||||
class EntitySoundActionEffectKeyframe : ConditionalActionEffectKeyframe(), EntityConditionalActionEffectKeyframe {
|
||||
override val entityCondition = "q.entity.is_user".asExpressionLike()
|
||||
var sound: String? = null
|
||||
val delay: ExpressionLike = "0".asExpressionLike()
|
||||
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val entities = context.providers
|
||||
.filterIsInstance<EntityProvider>()
|
||||
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
|
||||
|
||||
val soundIdentifier = try {
|
||||
sound?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: sound
|
||||
} catch (e: Exception) {
|
||||
sound
|
||||
}?.asIdentifierDefaultingNamespace() ?: return skip()
|
||||
|
||||
entities.forEach { entity ->
|
||||
val soundEvent = entity.world.registryManager.get(RegistryKeys.SOUND_EVENT).get(soundIdentifier) ?: return skip()
|
||||
entity.playSound(soundEvent, 1f, 1f)
|
||||
}
|
||||
|
||||
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class ForkActionEffectKeyframe(
|
||||
val condition: ExpressionLike = "true".asExpressionLike(),
|
||||
val ifTrue: List<ActionEffectKeyframe> = listOf(),
|
||||
val ifFalse: List<ActionEffectKeyframe> = listOf()
|
||||
) : ActionEffectKeyframe {
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val future = CompletableFuture<Unit>()
|
||||
/*
|
||||
* Using .toList() before running chainKeyframes because iterators can be weird about sharing,
|
||||
* .toList() creates a copy of the list and then grabs its iterator so much safer (three parachutes)
|
||||
*/
|
||||
if (!condition.resolveBoolean(context.runtime)) {
|
||||
context.actionEffect.chainKeyframes(context, ifFalse.toList().iterator(), future)
|
||||
} else {
|
||||
context.actionEffect.chainKeyframes(context, ifTrue.toList().iterator(), future)
|
||||
}
|
||||
|
||||
return future
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* An action effect keyframe that simply executes some MoLang on the effect context's runtime.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 21st, 2024
|
||||
*/
|
||||
class MoLangActionEffectKeyframe : ActionEffectKeyframe {
|
||||
val expressions: ExpressionLike = "0".asExpressionLike()
|
||||
val delay: ExpressionLike = "0".asExpressionLike()
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
expressions.resolve(context.runtime)
|
||||
return delayedFuture(seconds = delay.resolveFloat(context.runtime), serverThread = true)
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonNavigation
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class MoveToTargetActionEffectKeyframe : ActionEffectKeyframe {
|
||||
val speed = 1F
|
||||
val timeout = "4".asExpressionLike()
|
||||
var proximity = -1F
|
||||
val timeoutActionEffect: Identifier? = null
|
||||
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val user = context.findOneProvider<UsersProvider>()?.entities?.firstOrNull() as? PokemonEntity ?: return CompletableFuture.completedFuture(Unit)
|
||||
val target = context.findOneProvider<TargetsProvider>()?.entities?.firstOrNull() ?: return CompletableFuture.completedFuture(Unit)
|
||||
|
||||
val future = CompletableFuture<Unit>()
|
||||
|
||||
var timedOut = false
|
||||
|
||||
val proximity = this.proximity.takeIf { it != -1F } ?: (sqrt(2 * user.boundingBox.xLength.pow(2)) + 1.5F + sqrt(2 * target.boundingBox.xLength.pow(2))).toFloat()
|
||||
|
||||
if (target.distanceTo(user) !in proximity..20F) {
|
||||
future.complete(Unit)
|
||||
return future
|
||||
}
|
||||
|
||||
val timeoutEffect = timeoutActionEffect?.let { ActionEffects.actionEffects[it] } ?: ActionEffectTimeline.NONE
|
||||
|
||||
val nav = user.navigation
|
||||
val navContext = PokemonNavigation.NavigationContext(
|
||||
destinationProximity = proximity,
|
||||
onArrival = {
|
||||
if (!future.isDone && !timedOut) {
|
||||
future.complete(Unit)
|
||||
}
|
||||
},
|
||||
onCannotReach = {
|
||||
if (!future.isDone && !timedOut) {
|
||||
timedOut = true
|
||||
timeoutEffect.run(context).thenApply { future.complete(Unit) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
afterOnServer(seconds = timeout.resolveFloat(context.runtime)) {
|
||||
if (!future.isDone && !timedOut) {
|
||||
timedOut = true
|
||||
timeoutEffect.run(context).thenApply { future.complete(Unit) }
|
||||
nav.stop()
|
||||
}
|
||||
}
|
||||
|
||||
nav.startMovingTo(target.x, target.y, target.z, speed.toDouble(), navContext)
|
||||
return future
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class ParallelActionEffectKeyframe : ConditionalActionEffectKeyframe() {
|
||||
var keyframes: MutableList<ActionEffectKeyframe> = mutableListOf()
|
||||
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
return CompletableFuture.allOf(
|
||||
*keyframes.map {
|
||||
context.currentKeyframes.add(it)
|
||||
it.play(context).thenRun { context.currentKeyframes.remove(it) }
|
||||
}.toTypedArray()
|
||||
).thenApply {} // Map Void -> Unit at the end
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
|
||||
class PauseActionEffectKeyframe : ConditionalActionEffectKeyframe() {
|
||||
val pause = "1".asExpressionLike()
|
||||
override fun playWhenTrue(context: ActionEffectContext) = delayedFuture(seconds = pause.resolveFloat(context.runtime), serverThread = true)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.scheduling.delayedFuture
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* An [ActionEffectKeyframe] that clears some holds from the context, potentially marking the effect as
|
||||
* complete as far as any blocked actions are concerned. If no holds are specified, then all holds are
|
||||
* removed.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 27th, 2023
|
||||
*/
|
||||
class RemoveHoldsActionEffectKeyframe : ActionEffectKeyframe {
|
||||
val delay = 0F
|
||||
val holds = mutableSetOf<String>()
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
return delayedFuture(seconds = delay, serverThread = true).thenApply {
|
||||
if (holds.isEmpty()) {
|
||||
context.holds.clear()
|
||||
} else {
|
||||
context.holds.removeAll(holds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.scheduling.after
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonNavigation
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
class ReturnToPositionActionEffectKeyframe : ActionEffectKeyframe {
|
||||
val speed = 1F
|
||||
val timeout = "4".asExpressionLike()
|
||||
val timeoutActionEffect: Identifier? = null
|
||||
|
||||
override fun play(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val user = context.findOneProvider<UsersProvider>()?.entities?.firstOrNull() as? PokemonEntity ?: return skip()
|
||||
val future = CompletableFuture<Unit>()
|
||||
|
||||
val pos = (context.runtime.environment.getValue(setOf("${user.uuidAsString}-pos").iterator())?.value() as? ObjectValue<*>)?.obj as? Vec3d ?: return skip()
|
||||
|
||||
var timedOut = false
|
||||
|
||||
if (pos.distanceTo(user.pos) > 20) {
|
||||
future.complete(Unit)
|
||||
return future
|
||||
}
|
||||
|
||||
val timeoutEffect = timeoutActionEffect?.let { ActionEffects.actionEffects[it] } ?: ActionEffectTimeline.NONE
|
||||
|
||||
val nav = user.navigation
|
||||
val navContext = PokemonNavigation.NavigationContext(
|
||||
onArrival = {
|
||||
if (!future.isDone && !timedOut) {
|
||||
future.complete(Unit)
|
||||
}
|
||||
},
|
||||
onCannotReach = {
|
||||
if (!future.isDone && !timedOut) {
|
||||
timedOut = true
|
||||
timeoutEffect.run(context).thenApply { future.complete(Unit) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
after(seconds = timeout.resolveFloat(context.runtime), serverThread = true) {
|
||||
if (!future.isDone && !timedOut) {
|
||||
timedOut = true
|
||||
timeoutEffect.run(context).thenApply { future.complete(Unit) }
|
||||
nav.stop()
|
||||
}
|
||||
}
|
||||
|
||||
nav.startMovingTo(pos.x, pos.y, pos.z, speed.toDouble(), navContext)
|
||||
return future
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* An action effect keyframe that simply runs another action effect based on an identifier. This action
|
||||
* effect is marked complete only when the action effect it runs is complete unless [waitForActionEffect] is
|
||||
* false.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since January 21st, 2024
|
||||
*/
|
||||
class RunActionEffectKeyframe : ConditionalActionEffectKeyframe() {
|
||||
val actionEffect: Identifier? = null
|
||||
val waitForActionEffect = true
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
return if (actionEffect != null) {
|
||||
ActionEffects.actionEffects[actionEffect]?.run(context)?.takeIf { waitForActionEffect } ?: skip()
|
||||
} else {
|
||||
skip()
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class SavePositionActionEffectKeyframe: ConditionalActionEffectKeyframe() {
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val user = context.findOneProvider<UsersProvider>()?.entities?.firstOrNull() ?: return skip()
|
||||
context.runtime.environment.setSimpleVariable("${user.uuidAsString}-pos", ObjectValue(user.pos))
|
||||
return skip()
|
||||
}
|
||||
}
|
||||
+26
@@ -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.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* A sequence of action effects that are dependent on a condition.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since October 29th, 2023
|
||||
*/
|
||||
class SequenceActionEffectKeyframe(val keyframes: List<ActionEffectKeyframe> = listOf()) : ConditionalActionEffectKeyframe() {
|
||||
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
|
||||
val future = CompletableFuture<Unit>()
|
||||
context.actionEffect.chainKeyframes(context, keyframes.toList().iterator(), future)
|
||||
return future
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.multiblock
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
|
||||
@@ -42,5 +42,7 @@ interface MultiblockStructure {
|
||||
fun getComparatorOutput(state: BlockState, world: World?, pos: BlockPos?): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
fun markRemoved(world: World):Unit
|
||||
fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?)
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.net.serializers
|
||||
|
||||
import net.minecraft.entity.data.TrackedDataHandler
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Data serializer of [Identifier] for DataTracker things.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since May 22nd, 2023
|
||||
*/
|
||||
object IdentifierDataSerializer : TrackedDataHandler<Identifier> {
|
||||
override fun copy(value: Identifier) = Identifier(value.namespace, value.path)
|
||||
override fun read(buf: PacketByteBuf) = Identifier(buf.readString(), buf.readString())
|
||||
override fun write(buf: PacketByteBuf, value: Identifier) {
|
||||
buf.writeString(value.namespace)
|
||||
buf.writeString(value.path)
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
createDefault("ancient_feather_ball", throwPower = 2.5f, ancient = true)
|
||||
createDefault("ancient_wing_ball", throwPower = 2.5f, ancient = true)
|
||||
createDefault("ancient_jet_ball", throwPower = 2.5f, ancient = true)
|
||||
createDefault("ancient_origin_ball", ancient = true)
|
||||
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
|
||||
// Luxury ball effect
|
||||
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOWEST) { event ->
|
||||
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
|
||||
|
||||
-2
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.pokemon.effect
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.effects.LightSourceEffect
|
||||
import com.cobblemon.mod.common.pokemon.effects.PotionBaseEffect
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
@@ -29,7 +28,6 @@ object ShoulderEffectRegistry {
|
||||
private val effects = mutableMapOf<String, Class<out ShoulderEffect>>()
|
||||
|
||||
// Effects - START
|
||||
val LIGHT_SOURCE = register("light_source", LightSourceEffect::class.java)
|
||||
val POTION_EFFECT = register("potion_effect", PotionBaseEffect::class.java)
|
||||
// Effects - END
|
||||
|
||||
|
||||
@@ -16,11 +16,14 @@ import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.sound.SoundCategory
|
||||
@@ -92,6 +95,7 @@ interface Evolution : EvolutionLike {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts this evolution as soon as possible.
|
||||
* This will not present a choice to the client regardless of [optional].
|
||||
@@ -99,25 +103,57 @@ interface Evolution : EvolutionLike {
|
||||
* @param pokemon The [Pokemon] being evolved.
|
||||
*/
|
||||
fun forceEvolve(pokemon: Pokemon) {
|
||||
// This is a switch to enable/disable the evolution effect while we get particles improved
|
||||
val useEvolutionEffect = false
|
||||
|
||||
if (pokemon.state is ShoulderedState) {
|
||||
pokemon.tryRecallWithAnimation()
|
||||
}
|
||||
// ToDo Once implemented queue evolution for a pokemon state that is not in battle, start animation instead of instantly doing all of this
|
||||
|
||||
val pokemonEntity = pokemon.entity
|
||||
if (pokemonEntity == null || !useEvolutionEffect) {
|
||||
evolutionMethod(pokemon)
|
||||
} else {
|
||||
pokemonEntity.evolutionEntity = pokemon.getOwnerPlayer()?.let { GenericBedrockEntity(world = it.world) }
|
||||
val evolutionEntity = pokemon.entity!!.evolutionEntity
|
||||
evolutionEntity?.apply {
|
||||
category = cobblemonResource("evolution")
|
||||
colliderHeight = pokemonEntity.height
|
||||
colliderWidth = pokemonEntity.width
|
||||
scale = pokemonEntity.scaleFactor
|
||||
syncAge = true // Otherwise particle animation will be starting from zero even if you come along partway through
|
||||
setPosition(pokemonEntity.x, pokemonEntity.y, pokemonEntity.z)
|
||||
}
|
||||
pokemon.getOwnerPlayer()?.world?.spawnEntity(evolutionEntity)
|
||||
afterOnServer(seconds = 9F) {
|
||||
if (!pokemonEntity.isRemoved) {
|
||||
evolutionMethod(pokemon)
|
||||
afterOnServer(seconds = 1.5F) { pokemonEntity.cry() }
|
||||
afterOnServer(seconds = 3F) {
|
||||
if (evolutionEntity != null) {
|
||||
evolutionEntity.kill()
|
||||
if (!pokemonEntity.isRemoved) {
|
||||
pokemonEntity.evolutionEntity = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun evolutionMethod(pokemon: Pokemon) {
|
||||
this.result.apply(pokemon)
|
||||
this.learnableMoves.forEach { move ->
|
||||
if (pokemon.moveSet.hasSpace()) {
|
||||
pokemon.moveSet.add(move.create())
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pokemon.benchedMoves.add(BenchedMove(move, 0))
|
||||
}
|
||||
pokemon.getOwnerPlayer()?.sendMessage(lang("experience.learned_move", pokemon.getDisplayName(), move.displayName))
|
||||
}
|
||||
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
|
||||
pokemon.evolutions.filterIsInstance<PassiveEvolution>()
|
||||
.forEach { evolution ->
|
||||
evolution.attemptEvolution(pokemon)
|
||||
}
|
||||
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
|
||||
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLVING, SoundCategory.NEUTRAL, 1F, 1F)
|
||||
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
|
||||
}
|
||||
|
||||
@@ -8,13 +8,27 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.scheduling
|
||||
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
@Deprecated("Use afterOnServer or afterOnClient; ambiguous side is not good for your health")
|
||||
@JvmOverloads
|
||||
|
||||
|
||||
fun after(ticks: Int = 0, seconds: Float = 0F, serverThread: Boolean = false, action: () -> Unit) {
|
||||
val scheduler = if (serverThread) ServerTaskTracker else ClientTaskTracker
|
||||
scheduler.after(seconds = seconds + ticks / 20F, action)
|
||||
}
|
||||
|
||||
fun delayedFuture(ticks: Int = 0, seconds: Float = 0F, serverThread: Boolean = false): CompletableFuture<Unit> {
|
||||
val future = CompletableFuture<Unit>()
|
||||
if (ticks == 0 && seconds == 0F) {
|
||||
future.complete(Unit)
|
||||
} else {
|
||||
after(ticks = ticks, seconds = seconds, serverThread = serverThread) { future.complete(Unit) }
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
/**
|
||||
* Delayed task is created to run on the main thread. This is for when the task
|
||||
* being completed after the delay does things like entity removal or other thread-unsafe actions.
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.mojang.serialization.DynamicOps
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import com.mojang.serialization.codecs.UnboundedMapCodec
|
||||
import kotlin.math.floor
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
@@ -44,6 +45,7 @@ interface MoLangCurve : CodecMapped {
|
||||
registerSubtype(CurveType.LINEAR, LinearMoLangCurve::class.java, LinearMoLangCurve.CODEC)
|
||||
registerSubtype(CurveType.CATMULL_ROM, CatmullRomMoLangCurve::class.java, CatmullRomMoLangCurve.CODEC)
|
||||
registerSubtype(CurveType.BEZIER, BezierMoLangCurve::class.java, BezierMoLangCurve.CODEC)
|
||||
registerSubtype(CurveType.BEZIER_CHAIN, BezierChainMoLangCurve::class.java, BezierChainMoLangCurve.CODEC)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +54,7 @@ interface MoLangCurve : CodecMapped {
|
||||
var input: Expression
|
||||
fun resolve(runtime: MoLangRuntime, inputValue: Double): Double
|
||||
fun apply(runtime: MoLangRuntime) {
|
||||
val inputValue = runtime.resolveDouble(input)
|
||||
runtime.environment.setSimpleVariable(name, DoubleValue(resolve(runtime, inputValue)))
|
||||
runtime.environment.setSimpleVariable(name, DoubleValue(resolve(runtime, runtime.resolveDouble(input))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +166,103 @@ class CatmullRomMoLangCurve(
|
||||
}
|
||||
}
|
||||
|
||||
class BezierChainMoLangCurve(
|
||||
override var name: String = "variable",
|
||||
override var input: Expression = NumberExpression(0.5),
|
||||
var nodes: Map<Double, BezierChainNode> = emptyMap()
|
||||
) : MoLangCurve {
|
||||
var nodePairs: List<BezierChainNodePair> = emptyList()
|
||||
|
||||
fun deriveNodePairs() {
|
||||
val nodePairs = mutableListOf<BezierChainNodePair>()
|
||||
val sortedNodes = nodes.entries.sortedBy { it.key }
|
||||
for (i in 1 until sortedNodes.size) {
|
||||
val node1 = sortedNodes[i - 1]
|
||||
val node2 = sortedNodes[i]
|
||||
|
||||
nodePairs.add(BezierChainNodePair(node1.key, node2.key, node1.value, node2.value))
|
||||
}
|
||||
this.nodePairs = nodePairs
|
||||
}
|
||||
|
||||
init {
|
||||
deriveNodePairs()
|
||||
}
|
||||
|
||||
class BezierChainNode(var value: Double, var slope: Double) {
|
||||
fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeDouble(value)
|
||||
buffer.writeDouble(slope)
|
||||
}
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
value = buffer.readDouble()
|
||||
slope = buffer.readDouble()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<BezierChainNode> { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.DOUBLE.fieldOf("value").forGetter { it.value },
|
||||
PrimitiveCodec.DOUBLE.fieldOf("slope").forGetter { it.slope }
|
||||
).apply(instance, ::BezierChainNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BezierChainNodePair(
|
||||
val startTime: Double,
|
||||
val endTime: Double,
|
||||
node1: BezierChainNode,
|
||||
node2: BezierChainNode
|
||||
) {
|
||||
val curve: CubedBezierCurve
|
||||
init {
|
||||
val v1 = node1.value
|
||||
val v2 = node1.value + node1.slope / 3
|
||||
val v3 = node2.value - node2.slope / 3
|
||||
val v4 = node2.value
|
||||
curve = CubedBezierCurve(v1, v2, v3, v4)
|
||||
}
|
||||
}
|
||||
|
||||
class BezierChainPointData(val time: Double, val value: Double, val slope: Double)
|
||||
|
||||
companion object {
|
||||
val CODEC: Codec<BezierChainMoLangCurve> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
|
||||
PrimitiveCodec.STRING.fieldOf("name").forGetter { it.name },
|
||||
EXPRESSION_CODEC.fieldOf("input").forGetter { it.input },
|
||||
UnboundedMapCodec(PrimitiveCodec.DOUBLE, BezierChainNode.CODEC).fieldOf("nodes").forGetter { it.nodes }
|
||||
).apply(instance) { _, name, input, nodes -> BezierChainMoLangCurve(name, input, nodes) }
|
||||
}
|
||||
}
|
||||
|
||||
override val type: CurveType = CurveType.BEZIER_CHAIN
|
||||
override fun resolve(runtime: MoLangRuntime, inputValue: Double): Double {
|
||||
val position = inputValue.coerceIn(0.0, 1.0)
|
||||
val nodePair = nodePairs.last { position >= it.startTime }
|
||||
val curve = nodePair.curve
|
||||
val t = (position - nodePair.startTime) / (nodePair.endTime - nodePair.startTime)
|
||||
return curve.getY(t)
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
name = buffer.readString()
|
||||
input = MoLang.createParser(buffer.readString()).parseExpression()
|
||||
nodes = buffer.readMap({ buffer.readDouble() }, { BezierChainNode(0.0, 0.0).also { it.readFromBuffer(buffer) } })
|
||||
deriveNodePairs()
|
||||
}
|
||||
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeString(name)
|
||||
buffer.writeString(input.getString())
|
||||
buffer.writeMap(nodes, { pb, key -> pb.writeDouble(key) }, { pb, value -> value.writeToBuffer(pb) })
|
||||
}
|
||||
}
|
||||
|
||||
class BezierMoLangCurve(
|
||||
override var name: String = "variable",
|
||||
override var input: Expression = NumberExpression(0.5),
|
||||
|
||||
+82
-6
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.codec.CodecMapped
|
||||
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.util.math.hamiltonProduct
|
||||
import com.cobblemon.mod.common.util.set
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
@@ -26,6 +27,7 @@ import net.minecraft.util.math.RotationAxis
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import org.joml.AxisAngle4d
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
interface ParticleCameraMode : CodecMapped {
|
||||
companion object : ArbitrarilyMappedSerializableCompanion<ParticleCameraMode, ParticleCameraModeType>(
|
||||
@@ -54,6 +56,8 @@ interface ParticleCameraMode : CodecMapped {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -77,6 +81,8 @@ class RotateXYZCameraMode : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -109,6 +115,8 @@ class RotateYCameraMode : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -146,13 +154,15 @@ class LookAtXYZ : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
viewDirection: Vec3d
|
||||
): Quaternionf {
|
||||
val i = if (angle == 0F) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
|
||||
val rotation = Quaternionf(0F, 0F, 0F, 1F)
|
||||
val rotation = Quaternionf()
|
||||
rotation.hamiltonProduct(RotationAxis.POSITIVE_Y.rotationDegrees(-cameraYaw))
|
||||
rotation.hamiltonProduct(RotationAxis.POSITIVE_X.rotationDegrees(cameraPitch))
|
||||
rotation.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees(i))
|
||||
@@ -174,6 +184,8 @@ class LookAtY : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -203,6 +215,8 @@ class DirectionZ : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -236,6 +250,8 @@ class EmitterYZPlane : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -274,6 +290,8 @@ class EmitterXZPlane : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -311,6 +329,8 @@ class EmitterXYPlane : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -347,6 +367,8 @@ class DirectionY : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -380,6 +402,8 @@ class DirectionX : ParticleCameraMode {
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
@@ -398,6 +422,29 @@ class DirectionX : ParticleCameraMode {
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* You can think of this camera mode as the one that does the following:
|
||||
*
|
||||
* - Uses the direction of motion as the particle X axis
|
||||
* - Uses a vector parallel to the camera plane and perpendicular to the direction of motion as the particle Y axis.
|
||||
*
|
||||
* The second requirement is difficult to accomplish. The way it was eventually solved is by
|
||||
* thinking of the plane generated by two specific vectors. One is the camera to the particle,
|
||||
* the other is the camera to where the particle will be after it continues moving on its
|
||||
* trajectory for some amount of time.
|
||||
*
|
||||
* That plane slices right through the camera, and so the normal vector of that plane is parallel
|
||||
* lies on the camera plane (hard to explain that one). Since the plane includes the direction vector,
|
||||
* the normal vector of that plane is perpendicular to the direction vector. Since the direction
|
||||
* vector is being used for the X axis, and the normal vector is perpendicular, we can use that as
|
||||
* the Y axis safely.
|
||||
*
|
||||
* The chicanery of this look direction is all simply to calculate the correct Y axis, use it,
|
||||
* and then use the direction vector for the X axis.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since June 29th, 2023
|
||||
*/
|
||||
class LookAtDirection : ParticleCameraMode {
|
||||
override val type: ParticleCameraModeType = ParticleCameraModeType.LOOK_AT_DIRECTION
|
||||
companion object {
|
||||
@@ -408,21 +455,50 @@ class LookAtDirection : ParticleCameraMode {
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable shit so fewer instantiations during rendering.
|
||||
val viewDirectionF = Vector3f(0F, 0F, 0F)
|
||||
val particlePositionF = Vector3f(0F, 0F, 0F)
|
||||
val cameraPositionF = Vector3f(0F, 0F, 0F)
|
||||
val axisAngle = AxisAngle4d()
|
||||
|
||||
override fun getRotation(
|
||||
matrixWrapper: MatrixWrapper,
|
||||
prevAngle: Float,
|
||||
angle: Float,
|
||||
deltaTicks: Float,
|
||||
particlePosition: Vec3d,
|
||||
cameraPosition: Vec3d,
|
||||
cameraAngle: Quaternionf,
|
||||
cameraYaw: Float,
|
||||
cameraPitch: Float,
|
||||
viewDirection: Vec3d
|
||||
): Quaternionf {
|
||||
val i = if (angle == 0F) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
|
||||
val rotation = Quaternionf(0F, 0F, 0F, 1F)
|
||||
rotation.hamiltonProduct(RotationAxis.POSITIVE_Y.rotationDegrees((viewDirection.x * -cameraYaw).toFloat()))
|
||||
rotation.hamiltonProduct(RotationAxis.POSITIVE_X.rotationDegrees((viewDirection.y * cameraPitch).toFloat()))
|
||||
rotation.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees((viewDirection.z * i).toFloat()))
|
||||
viewDirectionF.set(viewDirection)
|
||||
particlePositionF.set(particlePosition)
|
||||
cameraPositionF.set(cameraPosition)
|
||||
|
||||
// Perform a rotation around the axis that should later become the particle Y axis
|
||||
Quaternionf().rotateTo(
|
||||
// Camera -> Particle
|
||||
particlePositionF.sub(cameraPositionF, Vector3f()),
|
||||
// Camera -> (Particle + Direction)
|
||||
particlePositionF.add(viewDirectionF, Vector3f()).sub(cameraPositionF)
|
||||
).get(axisAngle) // Extract the rotation into axis + angle, so we can pluck the axis out.
|
||||
|
||||
// Extract the axis of rotation, it is what we want the particle's local Y axis to be.
|
||||
val correctY = Vector3f(axisAngle.x.toFloat(), axisAngle.y.toFloat(), axisAngle.z.toFloat())
|
||||
// First make X look along the direction vector
|
||||
val rotation = Quaternionf().rotateTo(Vector3f(1F, 0F, 0F), viewDirectionF)
|
||||
// Now figure out what the local Y axis is
|
||||
val currentY = Vector3f(0F, 1F, 0F).rotate(rotation)
|
||||
// Move that local Y to the correct local Y
|
||||
// Pre-multiply is because the previous transform won't affect the correct Y, but vice versa absolutely would; we don't want that.
|
||||
rotation.premul(Quaternionf().rotateTo(currentY, correctY))
|
||||
|
||||
// Do the regular rotation around Z to spin the particle, same as all other modes.
|
||||
val particleAngle = if (angle == 0.0f) 0F else MathHelper.lerp(deltaTicks, prevAngle, angle)
|
||||
rotation.hamiltonProduct(RotationAxis.POSITIVE_Z.rotationDegrees(particleAngle))
|
||||
|
||||
return rotation
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -55,7 +56,9 @@ class OnceEmitterLifetime(var activeTime: Expression = 1.0.asExpression()) : Par
|
||||
override val type = ParticleEmitterLifetimeType.ONCE
|
||||
|
||||
override fun getAction(runtime: MoLangRuntime, started: Boolean, emitterAge: Double): ParticleEmitterAction {
|
||||
return if (emitterAge > runtime.resolveDouble(activeTime)) {
|
||||
val activeTime = runtime.resolve(activeTime)
|
||||
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
|
||||
return if (emitterAge > activeTime.asDouble()) {
|
||||
ParticleEmitterAction.STOP
|
||||
} else {
|
||||
ParticleEmitterAction.GO
|
||||
@@ -127,11 +130,13 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
|
||||
override val type = ParticleEmitterLifetimeType.LOOPING
|
||||
|
||||
override fun getAction(runtime: MoLangRuntime, started: Boolean, emitterAge: Double): ParticleEmitterAction {
|
||||
val activeTime = runtime.resolveDouble(activeTime)
|
||||
val activeTime = runtime.resolve(activeTime)
|
||||
val activeTimeValue = activeTime.asDouble()
|
||||
val sleepTime = runtime.resolveDouble(sleepTime)
|
||||
val interval = activeTime + sleepTime
|
||||
val interval = activeTimeValue + sleepTime
|
||||
val displacement = emitterAge % interval
|
||||
return if (displacement < activeTime) {
|
||||
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
|
||||
return if (displacement < activeTimeValue) {
|
||||
ParticleEmitterAction.GO
|
||||
} else {
|
||||
ParticleEmitterAction.RESET
|
||||
@@ -145,7 +150,6 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
|
||||
}
|
||||
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
val activeTimeString =
|
||||
buffer.writeString(activeTime.getString())
|
||||
buffer.writeString(sleepTime.getString())
|
||||
}
|
||||
|
||||
+24
-14
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.snowstorm
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.MoLang
|
||||
import com.bedrockk.molang.ast.NumberExpression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
@@ -17,9 +16,11 @@ import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.codec.CodecMapped
|
||||
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterRate.Companion.OVERFLOW_VARIABLE
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
@@ -49,12 +50,12 @@ enum class ParticleEmitterRateType {
|
||||
INSTANT
|
||||
}
|
||||
|
||||
class InstantParticleEmitterRate(var amount: Int = 1) : ParticleEmitterRate {
|
||||
class InstantParticleEmitterRate(var amount: Expression = "1".asExpression()) : ParticleEmitterRate {
|
||||
companion object {
|
||||
val CODEC: Codec<InstantParticleEmitterRate> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
|
||||
PrimitiveCodec.INT.fieldOf("amount").forGetter { it.amount }
|
||||
EXPRESSION_CODEC.fieldOf("amount").forGetter { it.amount }
|
||||
).apply(instance) { _, amount -> InstantParticleEmitterRate(amount) }
|
||||
}
|
||||
}
|
||||
@@ -65,17 +66,17 @@ class InstantParticleEmitterRate(var amount: Int = 1) : ParticleEmitterRate {
|
||||
if (started) {
|
||||
return 0
|
||||
} else {
|
||||
return amount
|
||||
return runtime.resolveInt(amount)
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
amount = buffer.readInt()
|
||||
amount = buffer.readString().asExpression()
|
||||
}
|
||||
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeInt(amount)
|
||||
buffer.writeString(amount.getString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,29 +100,38 @@ class SteadyParticleEmitterRate(
|
||||
override val type = ParticleEmitterRateType.STEADY
|
||||
|
||||
override fun getEmitCount(runtime: MoLangRuntime, started: Boolean, currentlyActive: Int): Int {
|
||||
// The emitting rates are all per second, but this runs every tick. Presents some difficulties.
|
||||
|
||||
val max = runtime.resolveDouble(maximum).toInt()
|
||||
val variables = runtime.environment.structs["variable"] as VariableStruct
|
||||
var currentOverflow = variables.map[OVERFLOW_VARIABLE]?.asDouble() ?: 0.0
|
||||
|
||||
/*
|
||||
* The strategy is that per tick it might be calculated to be 1.2 particles. We can't spawn a fifth
|
||||
* of a particle, so we consider the .2 to be overflow which will be added to the next total. If the
|
||||
* next tick we end up with 1.9, the overflow from last time will bring it to 2.1, so 2 particles with
|
||||
* 0.1 overflow for the next tick.
|
||||
*/
|
||||
|
||||
val currentOverflow = variables.map[OVERFLOW_VARIABLE]?.asDouble() ?: 0.0
|
||||
if (currentlyActive >= max) {
|
||||
return 0
|
||||
}
|
||||
|
||||
val perSecond = runtime.resolveDouble(rate)
|
||||
|
||||
val trySpawn = perSecond / 20.0 + currentOverflow
|
||||
|
||||
val intComponent = trySpawn.toInt()
|
||||
val doubleComponent = trySpawn - intComponent
|
||||
val intComponent = trySpawn.toInt() // round down
|
||||
val doubleComponent = trySpawn - intComponent // decimal portion (the overflow)
|
||||
|
||||
currentOverflow = doubleComponent
|
||||
|
||||
variables.map[OVERFLOW_VARIABLE] = DoubleValue(currentOverflow)
|
||||
variables.map[OVERFLOW_VARIABLE] = DoubleValue(doubleComponent)
|
||||
return min(intComponent, max - currentlyActive)
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
rate = MoLang.createParser(buffer.readString()).parseExpression()
|
||||
maximum = MoLang.createParser(buffer.readString()).parseExpression()
|
||||
rate = buffer.readString().asExpression()
|
||||
maximum = buffer.readString().asExpression()
|
||||
}
|
||||
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
|
||||
@@ -11,5 +11,6 @@ package com.cobblemon.mod.common.api.snowstorm
|
||||
enum class ParticleMaterial {
|
||||
ALPHA,
|
||||
OPAQUE,
|
||||
BLEND
|
||||
BLEND,
|
||||
ADD
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.snowstorm
|
||||
|
||||
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
|
||||
import com.mojang.blaze3d.platform.GlStateManager
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.minecraft.client.particle.ParticleTextureSheet
|
||||
import net.minecraft.client.render.*
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture
|
||||
import net.minecraft.client.texture.TextureManager
|
||||
|
||||
object ParticleMaterials {
|
||||
|
||||
val ALPHA = object : ParticleTextureSheet {
|
||||
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
|
||||
RenderSystem.disableBlend()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_CUTOUT }
|
||||
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
|
||||
|
||||
|
||||
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
|
||||
}
|
||||
|
||||
override fun draw(tessellator: Tessellator) {
|
||||
tessellator.draw()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ALPHA"
|
||||
}
|
||||
}
|
||||
|
||||
val ADD = object : ParticleTextureSheet {
|
||||
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
|
||||
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
|
||||
|
||||
RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
|
||||
}
|
||||
|
||||
override fun draw(tessellator: Tessellator) {
|
||||
tessellator.draw()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ADD"
|
||||
}
|
||||
}
|
||||
|
||||
val BLEND = object : ParticleTextureSheet {
|
||||
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
|
||||
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
|
||||
|
||||
RenderSystem.blendFunc(GlStateManager.SrcFactor.DST_COLOR, GlStateManager.DstFactor.ZERO)
|
||||
|
||||
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
|
||||
}
|
||||
|
||||
override fun draw(tessellator: Tessellator) {
|
||||
tessellator.draw()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BLEND"
|
||||
}
|
||||
}
|
||||
|
||||
val OPAQUE = object : ParticleTextureSheet {
|
||||
override fun begin(builder: BufferBuilder, textureManager: TextureManager) {
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.depthMask(true)
|
||||
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
|
||||
RenderSystem.setShaderTexture(0, SpriteAtlasTexture.PARTICLE_ATLAS_TEXTURE)
|
||||
|
||||
RenderSystem.blendFunc(GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ONE)
|
||||
|
||||
builder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT)
|
||||
}
|
||||
|
||||
override fun draw(tessellator: Tessellator) {
|
||||
tessellator.draw()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "OPAQUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,10 @@ import com.bedrockk.molang.ast.NumberExpression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.codec.CodecMapped
|
||||
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.math.geometry.transformDirection
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DynamicOps
|
||||
@@ -37,7 +39,7 @@ interface ParticleMotion : CodecMapped {
|
||||
}
|
||||
|
||||
val type: ParticleMotionType
|
||||
fun getInitialVelocity(runtime: MoLangRuntime, particlePos: Vec3d, emitterPos: Vec3d): Vec3d
|
||||
fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d): Vec3d
|
||||
fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d
|
||||
}
|
||||
|
||||
@@ -73,8 +75,8 @@ class DynamicParticleMotion(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getInitialVelocity(runtime: MoLangRuntime, particlePos: Vec3d, emitterPos: Vec3d): Vec3d {
|
||||
return direction.getDirectionVector(runtime, emitterPos, particlePos).multiply(runtime.resolveDouble(speed))
|
||||
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d): Vec3d {
|
||||
return direction.getDirectionVector(runtime, storm, emitterPos, particlePos).normalize().multiply(runtime.resolveDouble(speed))
|
||||
}
|
||||
|
||||
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d {
|
||||
@@ -82,7 +84,7 @@ class DynamicParticleMotion(
|
||||
runtime.resolveDouble(acceleration.first),
|
||||
runtime.resolveDouble(acceleration.second),
|
||||
runtime.resolveDouble(acceleration.third)
|
||||
).subtract(velocity.multiply(runtime.resolveDouble(drag) / 20)/* Drag is measured in blocks per tick per tick, I know it sounds dumb. */)
|
||||
).subtract(velocity.multiply(runtime.resolveDouble(drag)))
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
@@ -122,7 +124,7 @@ interface ParticleMotionDirection : CodecMapped {
|
||||
}
|
||||
}
|
||||
val type: ParticleMotionDirectionType
|
||||
fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d): Vec3d
|
||||
fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d
|
||||
}
|
||||
|
||||
class InwardsMotionDirection : ParticleMotionDirection {
|
||||
@@ -134,7 +136,13 @@ class InwardsMotionDirection : ParticleMotionDirection {
|
||||
}
|
||||
|
||||
override val type = ParticleMotionDirectionType.INWARDS
|
||||
override fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d) = emitterPos.subtract(particlePos).normalize()
|
||||
override fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
|
||||
return if (particlePos == emitterPos) {
|
||||
Vec3d(storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5)
|
||||
} else {
|
||||
particlePos.subtract(emitterPos)
|
||||
}.normalize()
|
||||
}
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {}
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {}
|
||||
@@ -149,8 +157,12 @@ class OutwardsMotionDirection : ParticleMotionDirection {
|
||||
}
|
||||
|
||||
override val type = ParticleMotionDirectionType.OUTWARDS
|
||||
override fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
|
||||
return particlePos.subtract(emitterPos).normalize()
|
||||
override fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
|
||||
return if (particlePos == emitterPos) {
|
||||
Vec3d(storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5)
|
||||
} else {
|
||||
particlePos.subtract(emitterPos)
|
||||
}.normalize()
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
@@ -178,12 +190,13 @@ class CustomMotionDirection(
|
||||
|
||||
override val type = ParticleMotionDirectionType.CUSTOM
|
||||
|
||||
override fun getDirectionVector(runtime: MoLangRuntime, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
|
||||
return Vec3d(
|
||||
override fun getDirectionVector(runtime: MoLangRuntime, storm: ParticleStorm, emitterPos: Vec3d, particlePos: Vec3d): Vec3d {
|
||||
val v = Vec3d(
|
||||
runtime.resolveDouble(direction.first),
|
||||
runtime.resolveDouble(direction.second),
|
||||
runtime.resolveDouble(direction.third)
|
||||
)
|
||||
return storm.matrixWrapper.matrix.transformDirection(v)
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
@@ -220,7 +233,7 @@ class StaticParticleMotion : ParticleMotion {
|
||||
@Transient
|
||||
override val type = ParticleMotionType.STATIC
|
||||
|
||||
override fun getInitialVelocity(runtime: MoLangRuntime, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
|
||||
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
|
||||
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d) = Vec3d.ZERO
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {}
|
||||
|
||||
@@ -34,6 +34,9 @@ class ParticleSpace(
|
||||
}
|
||||
}
|
||||
|
||||
val isLocalSpace: Boolean
|
||||
get() = localPosition || localRotation
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
localPosition = buffer.readBoolean()
|
||||
localRotation = buffer.readBoolean()
|
||||
|
||||
@@ -146,7 +146,7 @@ class AnimatedParticleUVMode(
|
||||
} else {
|
||||
val fps = runtime.resolveDouble(fps)
|
||||
val effectiveFrame = ((age * fps) % maxFrame).toInt()
|
||||
val frame = if (!loop && age * fps > maxFrame) {
|
||||
val frame = if (!loop && age * fps >= maxFrame) {
|
||||
maxFrame
|
||||
} else {
|
||||
effectiveFrame
|
||||
|
||||
+1
-5
@@ -9,17 +9,13 @@
|
||||
package com.cobblemon.mod.common.api.snowstorm
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.MoLang
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.codec.CodecMapped
|
||||
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.readVec3d
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveVec3d
|
||||
import com.cobblemon.mod.common.util.writeVec3d
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
@@ -70,7 +66,7 @@ class FromMotionViewDirection(var minSpeed: Double = 0.01) : ParticleViewDirecti
|
||||
}
|
||||
|
||||
override fun getDirection(runtime: MoLangRuntime, lastDirection: Vec3d, currentVelocity: Vec3d): Vec3d {
|
||||
return if (currentVelocity.length() / 20 >= minSpeed) {
|
||||
return if (currentVelocity.length() * 20 >= minSpeed) {
|
||||
currentVelocity.normalize()
|
||||
} else {
|
||||
lastDirection
|
||||
|
||||
@@ -10,21 +10,8 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.entity.Despawner
|
||||
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.FishingSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
|
||||
import com.cobblemon.mod.common.api.spawning.context.FishingSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SubmergedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.SurfaceSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.condition.*
|
||||
import com.cobblemon.mod.common.api.spawning.context.*
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.GroundedSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.LavafloorSpawningContextCalculator
|
||||
import com.cobblemon.mod.common.api.spawning.context.calculators.SeafloorSpawningContextCalculator
|
||||
@@ -34,6 +21,7 @@ import com.cobblemon.mod.common.api.spawning.context.calculators.SurfaceSpawning
|
||||
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import com.cobblemon.mod.common.api.spawning.fallingstar.FallingStarSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.preset.BasicSpawnDetailPreset
|
||||
@@ -87,6 +75,7 @@ object BestSpawner {
|
||||
val spawnerManagers = mutableListOf<SpawnerManager>(CobblemonWorldSpawnerManager)
|
||||
var defaultPokemonDespawner: Despawner<PokemonEntity> = CobblemonAgingDespawner(getAgeTicks = { it.ticksLived })
|
||||
lateinit var fishingSpawner: FishingSpawner
|
||||
lateinit var starSpawner: FallingStarSpawner
|
||||
|
||||
fun loadConfig() {
|
||||
LOGGER.info("Starting the Best Spawner...")
|
||||
@@ -96,6 +85,7 @@ object BestSpawner {
|
||||
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
|
||||
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
|
||||
SpawningCondition.register(FishingSpawningCondition.NAME, FishingSpawningCondition::class.java)
|
||||
SpawningCondition.register(FallingStarSpawningCondition.NAME, FallingStarSpawningCondition::class.java)
|
||||
|
||||
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
|
||||
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
|
||||
@@ -110,6 +100,7 @@ object BestSpawner {
|
||||
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "fishing", clazz = FishingSpawningContext::class.java, defaultCondition = FishingSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "fallingstar", clazz = FallingStarSpawningContext::class.java, defaultCondition = FallingStarSpawningCondition.NAME)
|
||||
|
||||
LOGGER.info("Loaded ${SpawningContext.contexts.size} spawning context types.")
|
||||
|
||||
@@ -125,5 +116,6 @@ object BestSpawner {
|
||||
fun onServerStarted() {
|
||||
spawnerManagers.forEach(SpawnerManager::onServerStarted)
|
||||
fishingSpawner = FishingSpawner()
|
||||
starSpawner = FallingStarSpawner()
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.FallingStarSpawningContext
|
||||
|
||||
class FallingStarSpawningCondition: SpawningCondition<FallingStarSpawningContext>() {
|
||||
override fun contextClass() = FallingStarSpawningContext::class.java
|
||||
|
||||
override fun fits(ctx: FallingStarSpawningContext): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "fallingstar"
|
||||
}
|
||||
}
|
||||
+31
@@ -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.api.spawning.context
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.fallingstar.FallingStarSpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
class FallingStarSpawningContext(
|
||||
cause: FallingStarSpawnCause,
|
||||
world: ServerWorld,
|
||||
pos: BlockPos,
|
||||
influences: MutableList<SpawningInfluence>
|
||||
) : TriggerSpawningContext(
|
||||
cause = cause,
|
||||
world = world,
|
||||
position = pos,
|
||||
light = world.getLightLevel(pos),
|
||||
skyLight = world.getLightLevel(pos.up()),
|
||||
canSeeSky = world.isSkyVisibleAllowingSea(pos),
|
||||
influences = influences
|
||||
) {
|
||||
|
||||
}
|
||||
+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.spawning.fallingstar
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnBucket
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnCause
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
class FallingStarSpawnCause(
|
||||
spawner: Spawner,
|
||||
bucket: SpawnBucket,
|
||||
entity: Entity?
|
||||
) : SpawnCause(spawner, bucket, entity)
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.fallingstar
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
|
||||
import com.cobblemon.mod.common.api.spawning.context.FallingStarSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TriggerSpawner
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
open class FallingStarSpawner(
|
||||
override val name: String = "fallingstar",
|
||||
pool: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
) : TriggerSpawner<FallingStarSpawnCause>(name, pool) {
|
||||
override fun parseContext(cause: FallingStarSpawnCause, world: ServerWorld, pos: BlockPos): FallingStarSpawningContext? {
|
||||
return FallingStarSpawningContext(cause, world, pos, influences)
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -32,11 +32,10 @@ open class NBTStoreAdapter(
|
||||
override fun save(file: File, serialized: NbtCompound) = NbtIo.writeCompressed(serialized, file)
|
||||
override fun <E, T : PokemonStore<E>> load(file: File, storeClass: Class<out T>, uuid: UUID): T? {
|
||||
val store = try {
|
||||
storeClass.getConstructor(UUID::class.java, UUID::class.java).newInstance(uuid, uuid)
|
||||
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
|
||||
} catch (exception: NoSuchMethodException) {
|
||||
storeClass.getConstructor(UUID::class.java).newInstance(uuid)
|
||||
}
|
||||
|
||||
return try {
|
||||
val nbt = NbtIo.readCompressed(file)
|
||||
store.loadFromNBT(nbt)
|
||||
|
||||
@@ -43,6 +43,8 @@ open class PlayerPartyStore(
|
||||
|
||||
private var secondsSinceFriendshipUpdate = 0
|
||||
|
||||
constructor(playerUUID: UUID): this(playerUUID, playerUUID)
|
||||
|
||||
override fun initialize() {
|
||||
super.initialize()
|
||||
observerUUIDs.add(playerUUID)
|
||||
|
||||
@@ -148,6 +148,8 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val HAS_FIRE_STONE_ORE_RARE = create("has_ore/ore_fire_stone_rare")
|
||||
@JvmField
|
||||
val HAS_FIRE_STONE_ORE_NETHER = create("has_ore/ore_fire_stone_nether")
|
||||
@JvmField
|
||||
val HAS_ICE_STONE_ORE = create("has_ore/ore_ice_stone_normal")
|
||||
@JvmField
|
||||
val HAS_ICE_STONE_ORE_RARE = create("has_ore/ore_ice_stone_rare")
|
||||
|
||||
@@ -45,6 +45,7 @@ object CobblemonBlockTags {
|
||||
@JvmField val SNOW_BLOCK = createTag("snow_block")
|
||||
@JvmField val ROOTS = createTag("roots")
|
||||
@JvmField val STANDING_SIGNS = createTag("standing_signs")
|
||||
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
|
||||
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
|
||||
@JvmField val WALL_SIGNS = createTag("wall_signs")
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.tags
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
@@ -21,11 +22,12 @@ import net.minecraft.registry.tag.TagKey
|
||||
*/
|
||||
@Suppress("unused", "HasPlatformType")
|
||||
object CobblemonItemTags {
|
||||
|
||||
@JvmField
|
||||
val ANCIENT_POKE_BALLS = create("ancient_poke_balls")
|
||||
@JvmField
|
||||
val APRICORN_LOGS = create("apricorn_logs")
|
||||
@JvmField
|
||||
val APRICORN_SEEDS = create("apricorn_seeds")
|
||||
val APRICORN_SPROUTS = create("apricorn_sprouts")
|
||||
@JvmField
|
||||
val APRICORNS = create("apricorns")
|
||||
/**
|
||||
@@ -40,6 +42,15 @@ object CobblemonItemTags {
|
||||
val BOATS = create("boats")
|
||||
@JvmField
|
||||
val COBBLEMON_SEEDS = create("cobblemon_seeds")
|
||||
/**
|
||||
* This tag is used for Fossil Machine natural materials
|
||||
*/
|
||||
@JvmField
|
||||
val COOKED_MEAT = create("cooked_meat")
|
||||
@JvmField
|
||||
val DAWN_STONE_ORES = create("dawn_stone_ores")
|
||||
@JvmField
|
||||
val DUSK_STONE_ORES = create("dusk_stone_ores")
|
||||
@JvmField
|
||||
val EVOLUTION_ITEMS = create("evolution_items")
|
||||
@JvmField
|
||||
@@ -47,27 +58,54 @@ object CobblemonItemTags {
|
||||
@JvmField
|
||||
val EXPERIENCE_CANDIES = create("experience_candies")
|
||||
@JvmField
|
||||
val FIRE_STONE_ORES = create("fire_stone_ores")
|
||||
@JvmField
|
||||
val FOSSILS = create("fossils")
|
||||
@JvmField
|
||||
val HANGING_SIGNS = create("hanging_signs")
|
||||
@JvmField
|
||||
val HERBS = create("herbs")
|
||||
@JvmField
|
||||
val ICE_STONE_ORES = create("ice_stone_ores")
|
||||
@JvmField
|
||||
val LEAF_STONE_ORES = create("leaf_stone_ores")
|
||||
@JvmField
|
||||
val MINT_LEAF = create("mint_leaf")
|
||||
@JvmField
|
||||
val MINT_SEEDS = create("mint_seeds")
|
||||
@JvmField
|
||||
val MINTS = create("mints")
|
||||
@JvmField
|
||||
val MOON_STONE_ORES = create("moon_stone_ores")
|
||||
@JvmField
|
||||
val MUTATED_BERRIES = create("mutated_berries")
|
||||
@JvmField
|
||||
val PLANTS = create("plants")
|
||||
@JvmField
|
||||
val POKEBALLS = create("poke_balls")
|
||||
val POKE_BALLS = create("poke_balls")
|
||||
@JvmField
|
||||
val PROTEIN_INGREDIENTS = create("protein_ingredients")
|
||||
/** See [COOKED_MEAT] */
|
||||
@JvmField
|
||||
val RAW_MEAT = create("raw_meat")
|
||||
@JvmField
|
||||
val SEEDS = create("seeds")
|
||||
@JvmField
|
||||
val SHINY_STONE_ORES = create("shiny_stone_ores")
|
||||
@JvmField
|
||||
val SIGNS = create("signs")
|
||||
@JvmField
|
||||
val SUN_STONE_ORES = create("sun_stone_ores")
|
||||
@JvmField
|
||||
val THUNDER_STONE_ORES = create("thunder_stone_ores")
|
||||
@JvmField
|
||||
val TUMBLESTONES = create("tumblestones")
|
||||
@JvmField
|
||||
val WATER_STONE_ORES = create("water_stone_ores")
|
||||
@JvmField
|
||||
val ZINC_INGREDIENTS = create("zinc_ingredients")
|
||||
|
||||
// Held Item Tags
|
||||
@JvmField
|
||||
val ANY_HELD_ITEM = create("held/is_held_item")
|
||||
@JvmField
|
||||
@@ -97,6 +135,15 @@ object CobblemonItemTags {
|
||||
@JvmField
|
||||
val CONSUMED_IN_WILD_BATTLE = create("held/consumed_in_wild_battle")
|
||||
|
||||
/**
|
||||
* Tag that flags items as being able to "create" [CobblemonItems.LEFTOVERS].
|
||||
*/
|
||||
@JvmField
|
||||
val LEAVES_LEFTOVERS = create("held/leaves_leftovers")
|
||||
|
||||
@JvmField
|
||||
val POTTERY_SHERDS = create("decorated_pot_sherds")
|
||||
|
||||
private fun create(path: String) = TagKey.of(RegistryKeys.ITEM, cobblemonResource(path))
|
||||
|
||||
}
|
||||
@@ -10,7 +10,11 @@ package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.*
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BasicContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.Effect
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.MissingContext
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
@@ -20,7 +24,11 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.text.*
|
||||
import com.cobblemon.mod.common.api.text.gold
|
||||
import com.cobblemon.mod.common.api.text.plus
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
|
||||
@@ -31,24 +39,46 @@ import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.battles.interpreter.ContextManager
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.DamageInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.DeprecatedInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.DeprecatedSplitInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.FaintInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.IgnoredInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.MoveInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.TurnInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.UnknownInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.UpkeepInstruction
|
||||
import com.cobblemon.mod.common.battles.interpreter.instructions.*
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMadeInvalidChoicePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattlePersistentStatusPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleQueueRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleSwitchPokemonPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.LastBattleCriticalHitsEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.runOnServer
|
||||
import com.cobblemon.mod.common.util.swap
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.collections.Iterator
|
||||
import kotlin.collections.MutableList
|
||||
import kotlin.collections.any
|
||||
import kotlin.collections.emptySet
|
||||
import kotlin.collections.filter
|
||||
import kotlin.collections.filterIsInstance
|
||||
import kotlin.collections.find
|
||||
import kotlin.collections.first
|
||||
import kotlin.collections.firstOrNull
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.getOrNull
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.map
|
||||
import kotlin.collections.mapNotNull
|
||||
import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.reduce
|
||||
import kotlin.collections.set
|
||||
import kotlin.collections.setOf
|
||||
import kotlin.collections.toMutableList
|
||||
import kotlin.collections.toTypedArray
|
||||
import kotlin.math.roundToInt
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
@@ -86,12 +116,13 @@ object ShowdownInterpreter {
|
||||
updateInstructionParser["upkeep"] = { _, _, _, _ -> UpkeepInstruction() }
|
||||
updateInstructionParser["faint"] = { battle, _, message, _ -> FaintInstruction(battle, message) }
|
||||
updateInstructionParser["move"] = { _, instructionSet, message, _ -> MoveInstruction(instructionSet, message) }
|
||||
splitInstructionParser["-damage"] = { _, targetActor, _, publicMessage, privateMessage, _ ->
|
||||
DamageInstruction(targetActor, publicMessage, privateMessage)
|
||||
updateInstructionParser["-activate"] = { _, instructionSet, message, _ -> ActivateInstruction(instructionSet, message) }
|
||||
updateInstructionParser["-ability"] = { _, instructionSet, message, _ -> AbilityInstruction(instructionSet, message) }
|
||||
updateInstructionParser["-miss"] = { battle, instructionSet, message, _ -> MissInstruction(battle, instructionSet, message) }
|
||||
splitInstructionParser["-damage"] = { _, targetActor, instructionSet, publicMessage, privateMessage, _ ->
|
||||
DamageInstruction(instructionSet, targetActor, publicMessage, privateMessage)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Note '-cureteam' is a legacy thing that is only used in generation 2 and 4 mods for heal bell and aromatherapy respectively as such we can just ignore that
|
||||
|
||||
updateInstructions["|win|"] = this::handleWinInstruction
|
||||
@@ -105,11 +136,9 @@ object ShowdownInterpreter {
|
||||
updateInstructions["|-fail|"] = this::handleFailInstruction
|
||||
updateInstructions["|-start|"] = this::handleStartInstructions
|
||||
updateInstructions["|-block|"] = this::handleBlockInstructions
|
||||
updateInstructions["|-activate|"] = this::handleActivateInstructions
|
||||
updateInstructions["|-curestatus|"] = this::handleCureStatusInstruction
|
||||
updateInstructions["|-fieldstart|"] = this::handleFieldStartInstructions
|
||||
updateInstructions["|-fieldend|"] = this::handleFieldEndInstructions
|
||||
updateInstructions["|-ability|"] = this::handleAbilityInstructions
|
||||
updateInstructions["|-endability|"] = this::handleEndAbilityInstruction
|
||||
updateInstructions["|-nothing"] = { battle, _, _ ->
|
||||
battle.dispatchGo { battle.broadcastChatMessage(battleLang("nothing")) }
|
||||
@@ -131,7 +160,6 @@ object ShowdownInterpreter {
|
||||
updateInstructions["|-invertboost|"] = this::handleInvertBoostInstruction
|
||||
updateInstructions["|-status|"] = this::handleStatusInstruction
|
||||
updateInstructions["|-end|"] = this::handleEndInstruction
|
||||
updateInstructions["|-miss|"] = this::handleMissInstruction
|
||||
updateInstructions["|-hitcount|"] = this::handleHitCountInstruction
|
||||
updateInstructions["|-item|"] = this::handleItemInstruction
|
||||
updateInstructions["|-enditem|"] = this::handleEndItemInstruction
|
||||
@@ -556,20 +584,6 @@ object ShowdownInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-miss|SOURCE|TARGET
|
||||
*
|
||||
* The move used by the SOURCE Pokémon missed (maybe absent) the TARGET Pokémon.
|
||||
*/
|
||||
private fun handleMissInstruction(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
battle.broadcastChatMessage(battleLang("missed").red())
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-immune|POKEMON
|
||||
@@ -625,7 +639,7 @@ object ShowdownInterpreter {
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val name = pokemon.getName()
|
||||
// Move may be null as it's not always given
|
||||
val moveName = message.moveAt(2)?.displayName ?: run { println(message.argumentAt(2)); "(Unrecognized: ${message.argumentAt(2)})".text() }
|
||||
val moveName = message.moveAt(2)?.displayName ?: run { "(Unrecognized: ${message.argumentAt(2)})".text() }
|
||||
|
||||
val lang = when (effectID) {
|
||||
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
|
||||
@@ -957,46 +971,6 @@ object ShowdownInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-activate|POKEMON|EFFECT
|
||||
*
|
||||
* A miscellaneous effect has activated.This is triggered whenever an effect could not be better described by one of the other minor messages.
|
||||
*/
|
||||
private fun handleActivateInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>){
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = pokemon.getName()
|
||||
val sourceName = message.getSourceBattlePokemon(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
val effect = message.effectAt(1) ?: return
|
||||
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
|
||||
broadcastOptionalAbility(battle, effect, pokemonName)
|
||||
|
||||
battle.dispatch{
|
||||
this.lastCauser[battle.battleId] = message
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
|
||||
val lang = when (effect.id) {
|
||||
// Includes a 3rd argument being the magnitude level as a number
|
||||
"magnitude" -> battleLang("activate.magnitude", message.argumentAt(2)?.toIntOrNull() ?: 1)
|
||||
// Includes spited move and the PP it was reduced by
|
||||
"spite", "eeriespell" -> battleLang("activate.spite", pokemonName, extraEffect, message.argumentAt(3)!!)
|
||||
// Don't need additional lang, announced elsewhere
|
||||
"toxicdebris", "shedskin" -> return@dispatch GO
|
||||
// Add activation to each Pokemon's history
|
||||
"destinybond" -> {
|
||||
battle.activePokemon.mapNotNull { it.battlePokemon?.uuid }.forEach { battle.minorBattleActions[it] = message }
|
||||
battleLang("activate.destinybond", pokemonName)
|
||||
}
|
||||
"focussash", "focusband" -> battleLang("activate.focusband", pokemonName, effect.typelessData)
|
||||
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
|
||||
"shadowforce", "hyperspacefury", "hyperspacehole" -> battleLang("activate.phantomforce", pokemonName)
|
||||
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
WaitDispatch(1F)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-fieldstart|CONDITION
|
||||
@@ -1054,54 +1028,6 @@ object ShowdownInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-ability|POKEMON|ABILITY|(from)EFFECT
|
||||
*
|
||||
* The ABILITY of the POKEMON has been changed due to a move/ability EFFECT.
|
||||
*
|
||||
* Format:
|
||||
* |-ability|POKEMON|ABILITY
|
||||
*
|
||||
* POKEMON has just switched-in, and its ability ABILITY is being announced to have a long-term effect.
|
||||
*/
|
||||
private fun handleAbilityInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = pokemon.getName()
|
||||
val effect = message.effectAt(1) ?: return
|
||||
val optionalEffect = message.effect()
|
||||
val optionalPokemon = message.getSourceBattlePokemon(battle)
|
||||
val optionalPokemonName = optionalPokemon?.getName()
|
||||
|
||||
// If there is an optional effect causing the activation, broadcast that instead of the standard effect
|
||||
if (optionalEffect != null) {
|
||||
broadcastAbility(battle, optionalEffect, pokemonName)
|
||||
} else {
|
||||
broadcastAbility(battle, effect, pokemonName)
|
||||
}
|
||||
|
||||
battle.dispatch {
|
||||
this.lastCauser[battle.battleId] = message
|
||||
|
||||
val lang = when (optionalEffect?.id) {
|
||||
"trace" -> optionalPokemonName?.let { battleLang("ability.trace", pokemonName, it, effect.typelessData) }
|
||||
"receiver", "powerofalchemy" -> optionalPokemonName?.let { battleLang("ability.receiver", it, effect.typelessData) } // Receiver and Power of Alchemy share the same text
|
||||
else -> when (effect.id) {
|
||||
"sturdy", "unnerve", "anticipation" -> battleLang("ability.${effect.id}", pokemonName) // Unique message
|
||||
"airlock", "cloudnine" -> battleLang("ability.airlock") // Cloud Nine shares the same text as Air Lock
|
||||
else -> null // Effect broadcasted by a succeeding instruction
|
||||
}
|
||||
}
|
||||
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
if (lang != null) {
|
||||
battle.broadcastChatMessage(lang)
|
||||
return@dispatch WaitDispatch(1F)
|
||||
}
|
||||
else return@dispatch GO
|
||||
}
|
||||
}
|
||||
|
||||
fun broadcastOptionalAbility(battle: PokemonBattle, effect: Effect?, pokemonName: MutableText) {
|
||||
if (effect != null && effect.type == Effect.Type.ABILITY)
|
||||
broadcastAbility(battle, effect, pokemonName)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.battles.dispatch
|
||||
|
||||
interface CauserInstruction
|
||||
@@ -9,9 +9,11 @@
|
||||
package com.cobblemon.mod.common.battles.dispatch
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import org.objectweb.asm.tree.analysis.Interpreter
|
||||
|
||||
class InstructionSet {
|
||||
val instructions: MutableList<InterpreterInstruction> = mutableListOf()
|
||||
|
||||
fun getSubsequentInstructions(instruction: InterpreterInstruction): List<InterpreterInstruction> {
|
||||
val index = instructions.indexOf(instruction)
|
||||
return instructions.subList(index + 1, instructions.size).toList()
|
||||
@@ -22,6 +24,31 @@ class InstructionSet {
|
||||
return instructions.subList(0, index).toList()
|
||||
}
|
||||
|
||||
inline fun <reified T> getMostRecentInstruction(comparedTo: InterpreterInstruction, predicate: (T) -> Boolean = { true }): T? {
|
||||
val index = instructions.indexOf(comparedTo)
|
||||
return instructions.subList(0, index).filterIsInstance<T>().lastOrNull(predicate)
|
||||
}
|
||||
|
||||
fun findInstructionsCausedBy(causerInstruction: CauserInstruction): List<InterpreterInstruction> {
|
||||
causerInstruction as InterpreterInstruction
|
||||
val thisCauseIndex = instructions.indexOf(causerInstruction)
|
||||
if (thisCauseIndex == instructions.size - 1) {
|
||||
return emptyList()
|
||||
}
|
||||
val nextCauseIndex = getNextInstruction<CauserInstruction>(causerInstruction)?.let { instructions.indexOf(it as InterpreterInstruction) }
|
||||
return instructions.subList(thisCauseIndex + 1, nextCauseIndex ?: instructions.size)
|
||||
}
|
||||
|
||||
inline fun <reified T> getNextInstruction(comparedTo: InterpreterInstruction, predicate: (T) -> Boolean = { true }): T? {
|
||||
val index = instructions.indexOf(comparedTo)
|
||||
if (instructions.last() == comparedTo) {
|
||||
return null
|
||||
}
|
||||
return instructions.subList(index + 1, instructions.size).filterIsInstance<T>().firstOrNull(predicate)
|
||||
}
|
||||
|
||||
fun getMostRecentCauser(comparedTo: InterpreterInstruction) = getMostRecentInstruction<CauserInstruction>(comparedTo)
|
||||
|
||||
fun execute(battle: PokemonBattle) {
|
||||
instructions.forEach { it(battle) }
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.CauserInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-ability|POKEMON|ABILITY|(from)EFFECT
|
||||
*
|
||||
* The ABILITY of the POKEMON has been changed due to a move/ability EFFECT.
|
||||
*
|
||||
* Format:
|
||||
* |-ability|POKEMON|ABILITY
|
||||
*
|
||||
* POKEMON has just switched-in, and its ability ABILITY is being announced to have a long-term effect.
|
||||
*/
|
||||
class AbilityInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : InterpreterInstruction, CauserInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = pokemon.getName()
|
||||
val effect = message.effectAt(1) ?: return
|
||||
val optionalEffect = message.effect()
|
||||
val optionalPokemon = message.getSourceBattlePokemon(battle)
|
||||
val optionalPokemonName = optionalPokemon?.getName()
|
||||
|
||||
// If there is an optional effect causing the activation, broadcast that instead of the standard effect
|
||||
if (optionalEffect != null) {
|
||||
ShowdownInterpreter.broadcastAbility(battle, optionalEffect, pokemonName)
|
||||
} else {
|
||||
ShowdownInterpreter.broadcastAbility(battle, effect, pokemonName)
|
||||
}
|
||||
|
||||
battle.dispatch {
|
||||
ShowdownInterpreter.lastCauser[battle.battleId] = message
|
||||
|
||||
val lang = when (optionalEffect?.id) {
|
||||
"trace" -> optionalPokemonName?.let { battleLang("ability.trace", pokemonName, it, effect.typelessData) }
|
||||
"receiver", "powerofalchemy" -> optionalPokemonName?.let { battleLang("ability.receiver", it, effect.typelessData) } // Receiver and Power of Alchemy share the same text
|
||||
else -> when (effect.id) {
|
||||
"sturdy", "unnerve", "anticipation" -> battleLang("ability.${effect.id}", pokemonName) // Unique message
|
||||
"airlock", "cloudnine" -> battleLang("ability.airlock") // Cloud Nine shares the same text as Air Lock
|
||||
else -> null // Effect broadcasted by a succeeding instruction
|
||||
}
|
||||
}
|
||||
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
if (lang != null) {
|
||||
battle.broadcastChatMessage(lang)
|
||||
return@dispatch WaitDispatch(1F)
|
||||
}
|
||||
else return@dispatch GO
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.CauserInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-activate|POKEMON|EFFECT
|
||||
*
|
||||
* A miscellaneous effect has activated.This is triggered whenever an effect could not be better described by one of the other minor messages.
|
||||
*/
|
||||
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : InterpreterInstruction, CauserInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val pokemonName = pokemon.getName()
|
||||
val sourceName = message.getSourceBattlePokemon(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
val effect = message.effectAt(1) ?: return
|
||||
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemonName)
|
||||
|
||||
battle.dispatch{
|
||||
ShowdownInterpreter.lastCauser[battle.battleId] = message
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
|
||||
val lang = when (effect.id) {
|
||||
// Includes a 3rd argument being the magnitude level as a number
|
||||
"magnitude" -> battleLang("activate.magnitude", message.argumentAt(2)?.toIntOrNull() ?: 1)
|
||||
// Includes spited move and the PP it was reduced by
|
||||
"spite", "eeriespell" -> battleLang("activate.spite", pokemonName, extraEffect, message.argumentAt(3)!!)
|
||||
// Don't need additional lang, announced elsewhere
|
||||
"toxicdebris", "shedskin" -> return@dispatch GO
|
||||
// Add activation to each Pokemon's history
|
||||
"destinybond" -> {
|
||||
battle.activePokemon.mapNotNull { it.battlePokemon?.uuid }.forEach { battle.minorBattleActions[it] = message }
|
||||
battleLang("activate.destinybond", pokemonName)
|
||||
}
|
||||
"focussash", "focusband" -> battleLang("activate.focusband", pokemonName, effect.typelessData)
|
||||
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
|
||||
"shadowforce", "hyperspacefury", "hyperspacehole" -> battleLang("activate.phantomforce", pokemonName)
|
||||
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
WaitDispatch(1F)
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
-7
@@ -15,24 +15,34 @@ import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
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
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.math.roundToInt
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class DamageInstruction(
|
||||
val instructionSet: InstructionSet,
|
||||
val actor: BattleActor,
|
||||
val publicMessage: BattleMessage,
|
||||
val privateMessage: BattleMessage
|
||||
) : InterpreterInstruction {
|
||||
var future = CompletableFuture.completedFuture(Unit)
|
||||
val battlePokemon = publicMessage.getBattlePokemon(0, actor.battle)
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val battlePokemon = publicMessage.getBattlePokemon(0, battle) ?: return
|
||||
if (privateMessage.optionalArgument("from")?.equals("recoil", true) == true) {
|
||||
battlePokemon ?: return
|
||||
val recoiling = privateMessage.optionalArgument("from")?.equals("recoil", true) == true
|
||||
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
|
||||
if (recoiling) {
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
if (RecoilEvolutionProgress.supports(pokemon)) {
|
||||
val newPercentage = privateMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toIntOrNull() ?: 0
|
||||
@@ -48,10 +58,34 @@ class DamageInstruction(
|
||||
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
|
||||
val effect = privateMessage.effect()
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val sourceName = privateMessage.getSourceBattlePokemon(battle)?.getName() ?: Text.literal("UNKOWN")
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, sourceName)
|
||||
val sourceName = privateMessage.getSourceBattlePokemon(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
var causedFaint = newHealth == "0"
|
||||
|
||||
battle.dispatch {
|
||||
if (lastCauser is MoveInstruction && recoiling) {
|
||||
UntilDispatch { "recoil" !in lastCauser.holds }
|
||||
} else {
|
||||
GO
|
||||
}
|
||||
}
|
||||
|
||||
battle.dispatch {
|
||||
if (!causedFaint) {
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, sourceName)
|
||||
val newHealthRatio: Float
|
||||
val remainingHealth = newHealth.split("/")[0].toInt()
|
||||
|
||||
@@ -70,8 +104,7 @@ class DamageInstruction(
|
||||
battle.broadcastChatMessage(lang.red())
|
||||
}
|
||||
|
||||
var causedFaint = false
|
||||
if (newHealth == "0") {
|
||||
if (causedFaint) {
|
||||
newHealthRatio = 0F
|
||||
battle.dispatch {
|
||||
battlePokemon.effectedPokemon.currentHealth = 0
|
||||
@@ -102,7 +135,9 @@ class DamageInstruction(
|
||||
battle.minorBattleActions[battlePokemon.uuid] = privateMessage
|
||||
|
||||
// If they faint from this damage then don't bother waiting for the damage to be applied.
|
||||
if (causedFaint) {
|
||||
if (lastCauser is MoveInstruction && lastCauser.actionEffect != null && !causedFaint) {
|
||||
UntilDispatch { lastCauser.future.isDone }
|
||||
} else if (causedFaint) {
|
||||
GO
|
||||
} else {
|
||||
WaitDispatch(1F)
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
|
||||
/**
|
||||
* Format:
|
||||
* |-miss|SOURCE|TARGET
|
||||
*
|
||||
* The move used by the SOURCE Pokémon missed (maybe absent) the TARGET Pokémon.
|
||||
*/
|
||||
class MissInstruction(val battle: PokemonBattle, val instructionSet: InstructionSet, val message: BattleMessage): InterpreterInstruction {
|
||||
val target = message.getBattlePokemon(1, battle)
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val pokemon = message.getBattlePokemon(0, battle) ?: return@dispatchGo
|
||||
battle.broadcastChatMessage(battleLang("missed").red())
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
-7
@@ -8,30 +8,55 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.Effect
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.CauserInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class MoveInstruction(
|
||||
val instructionSet: InstructionSet,
|
||||
val message: BattleMessage
|
||||
) : InterpreterInstruction {
|
||||
) : InterpreterInstruction, CauserInstruction {
|
||||
val effect = message.effectAt(1) ?: Effect.pure("", "")
|
||||
val move = Moves.getByNameOrDummy(effect.id)
|
||||
val actionEffect = move.actionEffect
|
||||
|
||||
var future = CompletableFuture.completedFuture(Unit)
|
||||
var holds = mutableSetOf<String>()
|
||||
|
||||
lateinit var userPokemon: BattlePokemon
|
||||
var targetPokemon: BattlePokemon? = null
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
val userPokemon = message.getBattlePokemon(0, battle) ?: return
|
||||
val targetPokemon = message.getBattlePokemon(2, battle)
|
||||
val effect = message.effectAt(1) ?: return
|
||||
userPokemon = message.getBattlePokemon(0, battle)!!
|
||||
targetPokemon = message.getBattlePokemon(2, battle)
|
||||
val targetPokemon = targetPokemon // So smart non-null casts can happen
|
||||
|
||||
val optionalEffect = message.effect()
|
||||
val move = Moves.getByNameOrDummy(effect.id)
|
||||
val pokemonName = userPokemon.getName()
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, optionalEffect, pokemonName)
|
||||
|
||||
battle.dispatch { UntilDispatch { instructionSet.getMostRecentInstruction<MoveInstruction>(this)?.future?.isDone != false } }
|
||||
|
||||
battle.dispatchGo {
|
||||
battle.dispatch {
|
||||
ShowdownInterpreter.lastCauser[battle.battleId] = message
|
||||
|
||||
userPokemon.effectedPokemon.let { pokemon ->
|
||||
@@ -50,8 +75,50 @@ class MoveInstruction(
|
||||
battleLang("used_move", pokemonName, move.displayName)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
battle.majorBattleActions[userPokemon.uuid] = message
|
||||
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
userPokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
targetPokemon?.effectedPokemon?.entity?.let { TargetsProvider(it) }?.let(providers::add)
|
||||
val runtime = MoLangRuntime().also {
|
||||
battle.addQueryFunctions(it.environment.getQueryStruct()).addStandardFunctions()
|
||||
}
|
||||
|
||||
actionEffect ?: return@dispatch GO
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers
|
||||
)
|
||||
|
||||
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
|
||||
val missedTargets = subsequentInstructions.filterIsInstance<MissInstruction>().mapNotNull { it.target }
|
||||
|
||||
runtime.environment.getQueryStruct().addFunction("missed") { params ->
|
||||
if (params.params.size == 0) {
|
||||
return@addFunction DoubleValue(missedTargets.isNotEmpty())
|
||||
} else {
|
||||
val entityUUID = params.getString(0)
|
||||
return@addFunction DoubleValue(missedTargets.any { it.entity?.uuidAsString == entityUUID })
|
||||
}
|
||||
}
|
||||
|
||||
val hurtTargets = subsequentInstructions.filterIsInstance<DamageInstruction>().mapNotNull { it.battlePokemon }
|
||||
runtime.environment.getQueryStruct().addFunction("hurt") { params ->
|
||||
if (params.params.size == 0) {
|
||||
return@addFunction DoubleValue(hurtTargets.isNotEmpty())
|
||||
} else {
|
||||
val entityUUID = params.getString(0)
|
||||
return@addFunction DoubleValue(hurtTargets.any { it.entity?.uuidAsString == entityUUID })
|
||||
}
|
||||
}
|
||||
|
||||
runtime.environment.getQueryStruct().addFunction("move") { move.struct }
|
||||
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
future.thenApply { holds.clear() }
|
||||
return@dispatch UntilDispatch { "effects" !in holds }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.block.entity.DyniteOreBlockEntity
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.BlockWithEntity
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityTicker
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
class DyniteOreBlock(settings: Settings) : BlockWithEntity(settings) {
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return DyniteOreBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun getRenderType(state: BlockState?) = BlockRenderType.MODEL
|
||||
|
||||
override fun <T : BlockEntity> getTicker(
|
||||
world: World,
|
||||
state: BlockState,
|
||||
type: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
return checkType(type, CobblemonBlockEntities.DYNITE_ORE, DyniteOreBlockEntity.TICKER::tick)
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -6,12 +6,12 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.fossilmachine
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockBlock
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilCompartmentBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilAnalyzerBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import net.minecraft.block.Block
|
||||
@@ -30,7 +30,7 @@ import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
class FossilCompartmentBlock(properties: Settings) : MultiblockBlock(properties), InventoryProvider {
|
||||
class FossilAnalyzerBlock(properties: Settings) : MultiblockBlock(properties), InventoryProvider {
|
||||
init {
|
||||
defaultState = defaultState
|
||||
.with(HorizontalFacingBlock.FACING, Direction.NORTH)
|
||||
@@ -38,14 +38,14 @@ class FossilCompartmentBlock(properties: Settings) : MultiblockBlock(properties)
|
||||
}
|
||||
|
||||
override fun createMultiBlockEntity(pos: BlockPos, state: BlockState): FossilMultiblockEntity {
|
||||
return FossilCompartmentBlockEntity(pos, state, FossilMultiblockBuilder(pos))
|
||||
return FossilAnalyzerBlockEntity(pos, state, FossilMultiblockBuilder(pos))
|
||||
}
|
||||
|
||||
override fun <T : BlockEntity?> getTicker(
|
||||
world: World?,
|
||||
state: BlockState?,
|
||||
type: BlockEntityType<T>?
|
||||
): BlockEntityTicker<T>? = checkType(type, CobblemonBlockEntities.FOSSIL_COMPARTMENT, FossilMultiblockStructure.TICKER::tick)
|
||||
): BlockEntityTicker<T>? = checkType(type, CobblemonBlockEntities.FOSSIL_ANALYZER, FossilMultiblockStructure.TICKER::tick)
|
||||
|
||||
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
|
||||
return defaultState.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing)
|
||||
@@ -56,9 +56,9 @@ class FossilCompartmentBlock(properties: Settings) : MultiblockBlock(properties)
|
||||
world: WorldAccess,
|
||||
pos: BlockPos
|
||||
): SidedInventory {
|
||||
val compartmentEntity = world.getBlockEntity(pos) as FossilCompartmentBlockEntity
|
||||
val analyzerEntity = world.getBlockEntity(pos) as FossilAnalyzerBlockEntity
|
||||
|
||||
return compartmentEntity.inv
|
||||
return analyzerEntity.inv
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
/**
|
||||
* Used for blocks that grow similarly to Amethyst.
|
||||
*
|
||||
* @param settings Block settings
|
||||
* @param stage The stage of growth of the block
|
||||
* @param height The height of this block's hitbox
|
||||
* @param nextStage The next stage of this block's growth. Null if final stage
|
||||
*
|
||||
* @author whatsy
|
||||
*/
|
||||
abstract class GrowableStoneBlock(
|
||||
settings: Settings,
|
||||
val stage: Int,
|
||||
val height: Int,
|
||||
xzOffset: Int,
|
||||
val nextStage: Block?
|
||||
) : FacingBlock(settings) {
|
||||
|
||||
private val upShape: VoxelShape = Block.createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
0.0,
|
||||
xzOffset.toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
height.toDouble(),
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
private val downShape: VoxelShape = createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
(16 - height).toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
16.0,
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
private val northShape: VoxelShape = createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
(16 - height).toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
16.0)
|
||||
|
||||
private val southShape: VoxelShape = createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
0.0,
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
height.toDouble())
|
||||
|
||||
private val eastShape: VoxelShape = createCuboidShape(
|
||||
0.0,
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
height.toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
private val westShape: VoxelShape = createCuboidShape(
|
||||
(16 - height).toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
16.0,
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(FACING, Direction.DOWN)
|
||||
}
|
||||
|
||||
abstract fun canGrow(pos: BlockPos, world: BlockView): Boolean
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun hasRandomTicks(state: BlockState?) = stage < MAX_STAGE
|
||||
override fun randomTick(state: BlockState, world: ServerWorld, pos: BlockPos, random: Random) {
|
||||
if (world.random.nextInt(5) == 0 && canGrow(pos, world)) {
|
||||
val block = nextStage
|
||||
|
||||
if (block != null) {
|
||||
val newState = block.defaultState.with(FACING, state.get(FACING)) as BlockState
|
||||
world.setBlockState(pos, newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
|
||||
var blockState = defaultState
|
||||
val worldView = ctx.world
|
||||
val blockPos = ctx.blockPos
|
||||
blockState = blockState.with(FACING, ctx.side) as BlockState
|
||||
if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
val direction = state.get(FACING) as Direction
|
||||
val blockState = world.getBlockState(pos.offset(direction.opposite))
|
||||
return blockState.isSideSolidFullSquare(world, pos, direction)
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState? {
|
||||
return if (direction == state.get(FACING).opposite && !state.canPlaceAt(world, pos)) Blocks.AIR.defaultState
|
||||
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
val direction = state.get(FACING) as Direction
|
||||
return when (direction) {
|
||||
Direction.NORTH -> northShape
|
||||
Direction.SOUTH -> southShape
|
||||
Direction.EAST -> eastShape
|
||||
Direction.WEST -> westShape
|
||||
Direction.DOWN -> downShape
|
||||
Direction.UP -> upShape
|
||||
else -> upShape
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val STAGE_2 = 2
|
||||
const val STAGE_3 = 3
|
||||
|
||||
const val MAX_STAGE = STAGE_3
|
||||
const val MIN_STAGE = STAGE_0
|
||||
}
|
||||
}
|
||||
+28
-7
@@ -6,19 +6,21 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.fossilmachine
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockBlock
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.HorizontalFacingBlock
|
||||
import net.minecraft.block.ShapeContext
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.IntProperty
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
@@ -26,7 +28,7 @@ import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
|
||||
class FossilMonitorBlock(properties: Settings) : MultiblockBlock(properties) {
|
||||
class MonitorBlock(properties: Settings) : MultiblockBlock(properties) {
|
||||
init {
|
||||
defaultState = defaultState.with(HorizontalFacingBlock.FACING, Direction.NORTH)
|
||||
}
|
||||
@@ -46,7 +48,7 @@ class FossilMonitorBlock(properties: Settings) : MultiblockBlock(properties) {
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(HorizontalFacingBlock.FACING)
|
||||
builder.add(PROGRESS)
|
||||
builder.add(SCREEN)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@@ -76,10 +78,14 @@ class FossilMonitorBlock(properties: Settings) : MultiblockBlock(properties) {
|
||||
): VoxelShape {
|
||||
return HITBOX
|
||||
}
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(state: BlockState?, world: BlockView?, pos: BlockPos?, type: NavigationType?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
//0 is off
|
||||
val PROGRESS = IntProperty.of("progress", 0, 9)
|
||||
val SCREEN = EnumProperty.of("screen", MonitorScreen::class.java)
|
||||
val HITBOX = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0625, 0.0, 0.0625, 0.9375, 0.375, 0.9375),
|
||||
VoxelShapes.cuboid(0.0625, 0.875, 0.0625, 0.9375, 1.0, 0.9375),
|
||||
@@ -88,4 +94,19 @@ class FossilMonitorBlock(properties: Settings) : MultiblockBlock(properties) {
|
||||
VoxelShapes.cuboid(0.0625, 0.375, 0.0625, 0.1875, 0.875, 0.9375)
|
||||
)
|
||||
}
|
||||
}
|
||||
enum class MonitorScreen : StringIdentifiable {
|
||||
OFF,
|
||||
BLUE_PROGRESS_1,
|
||||
BLUE_PROGRESS_2,
|
||||
BLUE_PROGRESS_3,
|
||||
BLUE_PROGRESS_4,
|
||||
BLUE_PROGRESS_5,
|
||||
BLUE_PROGRESS_6,
|
||||
BLUE_PROGRESS_7,
|
||||
BLUE_PROGRESS_8,
|
||||
BLUE_PROGRESS_9,
|
||||
GREEN_PROGRESS_9;
|
||||
|
||||
override fun asString(): String = this.name.lowercase()
|
||||
}
|
||||
}
|
||||
+35
-33
@@ -6,13 +6,13 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.fossilmachine
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockBlock
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilTubeBlockEntity
|
||||
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 net.minecraft.block.*
|
||||
import net.minecraft.entity.LivingEntity
|
||||
@@ -22,6 +22,7 @@ import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.util.ActionResult
|
||||
@@ -39,17 +40,19 @@ import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
|
||||
class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), InventoryProvider {
|
||||
class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties), InventoryProvider {
|
||||
|
||||
init {
|
||||
defaultState = defaultState
|
||||
.with(HorizontalFacingBlock.FACING, Direction.NORTH)
|
||||
.with(PART, TubePart.BOTTOM)
|
||||
.with(PART, TankPart.BOTTOM)
|
||||
.with(TRIGGERED, false)
|
||||
.with(ON, false)
|
||||
}
|
||||
|
||||
|
||||
fun getPositionOfOtherPart(state: BlockState, pos: BlockPos): BlockPos {
|
||||
return if (state.get(PART) == TubePart.BOTTOM) {
|
||||
return if (state.get(PART) == TankPart.BOTTOM) {
|
||||
pos.up()
|
||||
} else {
|
||||
pos.down()
|
||||
@@ -64,12 +67,12 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
}
|
||||
}
|
||||
|
||||
private fun isBase(state: BlockState): Boolean = state.get(PART) == TubePart.BOTTOM
|
||||
private fun isBase(state: BlockState): Boolean = state.get(PART) == TankPart.BOTTOM
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
super.onBreak(world, pos, state, player)
|
||||
val otherPart = world.getBlockState(getPositionOfOtherPart(state, pos))
|
||||
if (otherPart.block is FossilTubeBlock) {
|
||||
if (otherPart.block is RestorationTankBlock) {
|
||||
world.setBlockState(getPositionOfOtherPart(state, pos), Blocks.AIR.defaultState, 35)
|
||||
world.syncWorldEvent(player, 2001, getPositionOfOtherPart(state, pos), getRawIdFromState(otherPart))
|
||||
}
|
||||
@@ -83,7 +86,7 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
itemStack: ItemStack?
|
||||
) {
|
||||
//Place the full block before we call to super to validate the multiblock
|
||||
world.setBlockState(pos.up(), state.with(PART, TubePart.TOP) as BlockState, 3)
|
||||
world.setBlockState(pos.up(), state.with(PART, TankPart.TOP) as BlockState, 3)
|
||||
world.updateNeighbors(pos, Blocks.AIR)
|
||||
state.updateNeighbors(world, pos, 3)
|
||||
super.onPlaced(world, pos, state, placer, itemStack)
|
||||
@@ -91,20 +94,20 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||
if(state.get(PART) == TubePart.TOP) {
|
||||
// TubeTop isn't reliably up to date on clients, need to look at the bottom half
|
||||
val tubeBottomPos = pos.down()
|
||||
val tubeBottomState = world.getBlockState(tubeBottomPos)
|
||||
if(tubeBottomState.block.equals(CobblemonBlocks.FOSSIL_TUBE.asBlock()) && tubeBottomState.get(PART) == TubePart.BOTTOM) {
|
||||
return super.onUse(tubeBottomState, world, tubeBottomPos, player, hand, hit)
|
||||
if(state.get(PART) == TankPart.TOP) {
|
||||
// TankTop isn't reliably up to date on clients, need to look at the bottom half
|
||||
val tankBottomPos = pos.down()
|
||||
val tankBottomState = world.getBlockState(tankBottomPos)
|
||||
if(tankBottomState.block.equals(CobblemonBlocks.RESTORATION_TANK.asBlock()) && tankBottomState.get(PART) == TankPart.BOTTOM) {
|
||||
return super.onUse(tankBottomState, world, tankBottomPos, player, hand, hit)
|
||||
}
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit)
|
||||
}
|
||||
|
||||
override fun createMultiBlockEntity(pos: BlockPos, state: BlockState): FossilMultiblockEntity {
|
||||
return if (state.get(PART) == TubePart.BOTTOM) {
|
||||
FossilTubeBlockEntity(pos, state, FossilMultiblockBuilder(pos))
|
||||
return if (state.get(PART) == TankPart.BOTTOM) {
|
||||
RestorationTankBlockEntity(pos, state, FossilMultiblockBuilder(pos))
|
||||
} else {
|
||||
FossilMultiblockEntity(pos, state, FossilMultiblockBuilder(pos))
|
||||
}
|
||||
@@ -116,7 +119,7 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
if (world.getBlockState(abovePosition).canReplace(blockPlaceContext) && !world.isOutOfHeightLimit(abovePosition)) {
|
||||
return defaultState
|
||||
.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing)
|
||||
.with(PART, TubePart.BOTTOM)
|
||||
.with(PART, TankPart.BOTTOM)
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -125,13 +128,13 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
val blockPos = pos.down()
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
return if (state.get(PART) == TubePart.BOTTOM) blockState.isSideSolidFullSquare(world, blockPos, Direction.UP) else blockState.isOf(this)
|
||||
return if (state.get(PART) == TankPart.BOTTOM) true else blockState.isOf(this)
|
||||
}
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(HorizontalFacingBlock.FACING)
|
||||
builder.add(PART)
|
||||
builder.add(TRIGGERED)
|
||||
|
||||
builder.add(ON)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@@ -145,8 +148,8 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
if(world == null || pos == null) {
|
||||
return 0
|
||||
}
|
||||
val tubeEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
val multiBlockEntity = tubeEntity.multiblockStructure
|
||||
val tankEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
val multiBlockEntity = tankEntity.multiblockStructure
|
||||
if(multiBlockEntity != null) {
|
||||
return multiBlockEntity.getComparatorOutput(state, world, pos)
|
||||
}
|
||||
@@ -175,10 +178,8 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
if(world == null || pos == null) {
|
||||
return
|
||||
}
|
||||
val tubeEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
if (tubeEntity.multiblockStructure != null) {
|
||||
tubeEntity.multiblockStructure!!.onTriggerEvent(state, world, pos, random)
|
||||
}
|
||||
val tankEntity = world.getBlockEntity(pos) as? MultiblockEntity
|
||||
tankEntity?.multiblockStructure!!.onTriggerEvent(state, world, pos, random)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(
|
||||
@@ -187,7 +188,7 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
pos: BlockPos?,
|
||||
context: ShapeContext?
|
||||
): VoxelShape {
|
||||
return if (state.get(PART) == TubePart.TOP) {
|
||||
return if (state.get(PART) == TankPart.TOP) {
|
||||
var shape = VoxelShapes.cuboid(0.0625, 0.0, 0.0625, 0.9375, 0.8125, 0.9375)
|
||||
shape = VoxelShapes.union(shape, VoxelShapes.cuboid(0.0, 0.8125, 0.0, 1.0, 1.0, 1.0))
|
||||
shape
|
||||
@@ -202,21 +203,22 @@ class FossilTubeBlock(properties: Settings) : MultiblockBlock(properties), Inven
|
||||
world: WorldAccess,
|
||||
pos: BlockPos
|
||||
): SidedInventory {
|
||||
val tubeEntity =
|
||||
(if (state.get(PART) == TubePart.TOP) world.getBlockEntity(pos.down())
|
||||
else world.getBlockEntity(pos)) as FossilTubeBlockEntity
|
||||
val tankEntity =
|
||||
(if (state.get(PART) == TankPart.TOP) world.getBlockEntity(pos.down())
|
||||
else world.getBlockEntity(pos)) as RestorationTankBlockEntity
|
||||
|
||||
return tubeEntity.inv
|
||||
return tankEntity.inv
|
||||
}
|
||||
|
||||
enum class TubePart(private val label: String) : StringIdentifiable {
|
||||
enum class TankPart(private val label: String) : StringIdentifiable {
|
||||
TOP("top"),
|
||||
BOTTOM("bottom");
|
||||
override fun asString() = label
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PART = EnumProperty.of("part", TubePart::class.java)
|
||||
val PART = EnumProperty.of("part", TankPart::class.java)
|
||||
val TRIGGERED = Properties.TRIGGERED
|
||||
val ON = BooleanProperty.of("on")
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
@@ -20,125 +21,15 @@ import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
|
||||
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
class TumblestoneBlock(
|
||||
settings: Settings,
|
||||
val stage: Int,
|
||||
val height: Int,
|
||||
stage: Int,
|
||||
height: Int,
|
||||
xzOffset: Int,
|
||||
val nextStage: Block?
|
||||
) : FacingBlock(settings) {
|
||||
|
||||
private val upShape: VoxelShape = Block.createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
0.0,
|
||||
xzOffset.toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
height.toDouble(),
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
private val downShape: VoxelShape = createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
(16 - height).toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
16.0,
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
private val northShape: VoxelShape = createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
(16 - height).toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
16.0)
|
||||
|
||||
private val southShape: VoxelShape = createCuboidShape(
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
0.0,
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
height.toDouble())
|
||||
|
||||
private val eastShape: VoxelShape = createCuboidShape(
|
||||
0.0,
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
height.toDouble(),
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
private val westShape: VoxelShape = createCuboidShape(
|
||||
(16 - height).toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
xzOffset.toDouble(),
|
||||
16.0,
|
||||
(16 - xzOffset).toDouble(),
|
||||
(16 - xzOffset).toDouble())
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(FACING, Direction.DOWN)
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun hasRandomTicks(state: BlockState?) = stage < MAX_STAGE
|
||||
override fun randomTick(state: BlockState, world: ServerWorld, pos: BlockPos, random: Random) {
|
||||
if (world.random.nextInt(5) == 0 && canGrow(pos, world)) {
|
||||
val block = nextStage
|
||||
|
||||
if (block != null) {
|
||||
val newState = block.defaultState.with(FACING, state.get(FACING)) as BlockState
|
||||
world.setBlockState(pos, newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
|
||||
var blockState = defaultState
|
||||
val worldView = ctx.world
|
||||
val blockPos = ctx.blockPos
|
||||
blockState = blockState.with(FACING, ctx.side) as BlockState
|
||||
if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
val direction = state.get(FACING) as Direction
|
||||
val blockState = world.getBlockState(pos.offset(direction.opposite))
|
||||
return blockState.isSideSolidFullSquare(world, pos, direction)
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState? {
|
||||
return if (direction == state.get(FACING).opposite && !state.canPlaceAt(world, pos)) Blocks.AIR.defaultState
|
||||
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
val direction = state.get(FACING) as Direction
|
||||
return when (direction) {
|
||||
Direction.NORTH -> northShape
|
||||
Direction.SOUTH -> southShape
|
||||
Direction.EAST -> eastShape
|
||||
Direction.WEST -> westShape
|
||||
Direction.DOWN -> downShape
|
||||
Direction.UP -> upShape
|
||||
else -> upShape
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValidGrowthBlock(block: Block): Boolean {
|
||||
return block == Blocks.MAGMA_BLOCK || block == Blocks.LAVA
|
||||
}
|
||||
|
||||
private fun canGrow(pos: BlockPos, world: BlockView): Boolean {
|
||||
nextStage: Block?
|
||||
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
|
||||
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
|
||||
if (stage == MAX_STAGE) return false
|
||||
val iterator: Iterator<BlockPos> =
|
||||
BlockPos.iterate(pos.add(-1, -1, -1), pos.add(1, 1, 1))
|
||||
@@ -147,18 +38,9 @@ class TumblestoneBlock(
|
||||
var blockPos: BlockPos
|
||||
do { if (!iterator.hasNext()) { return false }
|
||||
blockPos = iterator.next()
|
||||
} while (!isValidGrowthBlock(world.getBlockState(blockPos).block))
|
||||
} while (!world.getBlockState(blockPos).isIn(CobblemonBlockTags.TUMBLESTONE_HEAT_SOURCE))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STAGE_0 = 0
|
||||
const val STAGE_1 = 1
|
||||
const val STAGE_2 = 2
|
||||
const val STAGE_3 = 3
|
||||
|
||||
const val MAX_STAGE = STAGE_3
|
||||
const val MIN_STAGE = STAGE_0
|
||||
}
|
||||
}
|
||||
@@ -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.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityTicker
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
class DyniteOreBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DYNITE_ORE, pos, state) {
|
||||
|
||||
companion object {
|
||||
internal val TICKER = BlockEntityTicker<DyniteOreBlockEntity> { world, pos, state, blockEntity ->
|
||||
if (world.isClient) return@BlockEntityTicker
|
||||
// idk if i'll actually need this but i already made it
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+12
-12
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity.fossil
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
@@ -20,14 +20,14 @@ import net.minecraft.item.ItemStack
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
|
||||
class FossilCompartmentBlockEntity(
|
||||
class FossilAnalyzerBlockEntity(
|
||||
pos: BlockPos, state: BlockState,
|
||||
multiblockBuilder: MultiblockStructureBuilder
|
||||
): FossilMultiblockEntity(pos, state, multiblockBuilder, CobblemonBlockEntities.FOSSIL_COMPARTMENT) {
|
||||
): FossilMultiblockEntity(pos, state, multiblockBuilder, CobblemonBlockEntities.FOSSIL_ANALYZER) {
|
||||
|
||||
val inv = FossilCompartmentInventory(this)
|
||||
val inv = FossilAnalyzerInventory(this)
|
||||
|
||||
class FossilCompartmentInventory(val compartmentEntity: FossilCompartmentBlockEntity) : SidedInventory {
|
||||
class FossilAnalyzerInventory(val analyzerEntity: FossilAnalyzerBlockEntity) : SidedInventory {
|
||||
override fun clear() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
@@ -53,17 +53,17 @@ class FossilCompartmentBlockEntity(
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack) {
|
||||
if (compartmentEntity.multiblockStructure != null) {
|
||||
val struct = compartmentEntity.multiblockStructure as FossilMultiblockStructure
|
||||
compartmentEntity.world?.let {
|
||||
if (analyzerEntity.multiblockStructure != null) {
|
||||
val struct = analyzerEntity.multiblockStructure as FossilMultiblockStructure
|
||||
analyzerEntity.world?.let {
|
||||
struct.insertFossil(stack, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
if (compartmentEntity.world != null) {
|
||||
compartmentEntity.multiblockStructure?.markDirty(compartmentEntity.world!!)
|
||||
if (analyzerEntity.world != null) {
|
||||
analyzerEntity.multiblockStructure?.markDirty(analyzerEntity.world!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ class FossilCompartmentBlockEntity(
|
||||
}
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if (compartmentEntity.multiblockStructure is FossilMultiblockStructure) {
|
||||
val structure = compartmentEntity.multiblockStructure as FossilMultiblockStructure
|
||||
if (analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
|
||||
val structure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
|
||||
return stack?.let { Fossils.isFossilIngredient(it) } == true
|
||||
&& structure.fossilInventory.size < Cobblemon.config.maxInsertedFossilItems
|
||||
&& structure.isRunning() == false && structure.resultingFossil == null
|
||||
+18
-2
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity.fossil
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
|
||||
@@ -47,9 +47,25 @@ open class FossilMultiblockEntity(
|
||||
}
|
||||
}
|
||||
|
||||
override fun markRemoved() {
|
||||
super.markRemoved()
|
||||
if(this.multiblockStructure != null && world != null) {
|
||||
this.multiblockStructure!!.markRemoved(world!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound) {
|
||||
val oldMultiblockStructure = this.multiblockStructure as? FossilMultiblockStructure
|
||||
multiblockStructure = if (nbt.contains(DataKeys.MULTIBLOCK_STORAGE)) {
|
||||
FossilMultiblockStructure.fromNbt(nbt.getCompound(DataKeys.MULTIBLOCK_STORAGE))
|
||||
if(oldMultiblockStructure?.fossilState != null) {
|
||||
// Copy the fossilState's previous animation time to the new instance
|
||||
// Otherwise the fetus animation gets interrupted on every block update
|
||||
val animAge = oldMultiblockStructure.fossilState.peekAge() // If someone knows a better way to fetch the age, please do.
|
||||
val partialTicks = oldMultiblockStructure.fossilState.getPartialTicks()
|
||||
FossilMultiblockStructure.fromNbt(nbt.getCompound(DataKeys.MULTIBLOCK_STORAGE), animAge, partialTicks )
|
||||
} else {
|
||||
FossilMultiblockStructure.fromNbt(nbt.getCompound(DataKeys.MULTIBLOCK_STORAGE))
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.api.fossil.NaturalMaterials
|
||||
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.util.collection.DefaultedList
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
|
||||
class RestorationTankBlockEntity(
|
||||
pos: BlockPos, state: BlockState,
|
||||
multiblockBuilder: MultiblockStructureBuilder
|
||||
) : FossilMultiblockEntity(pos, state, multiblockBuilder, CobblemonBlockEntities.RESTORATION_TANK) {
|
||||
val inv = RestorationTankInventory(this)
|
||||
|
||||
override fun writeNbt(nbt: NbtCompound) {
|
||||
super.writeNbt(nbt)
|
||||
Inventories.writeNbt(nbt, inv.items)
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound) {
|
||||
super.readNbt(nbt)
|
||||
Inventories.readNbt(nbt, inv.items)
|
||||
}
|
||||
|
||||
class RestorationTankInventory(val tankEntity: RestorationTankBlockEntity) : SidedInventory {
|
||||
|
||||
val items: DefaultedList<ItemStack> = DefaultedList.ofSize(8, ItemStack.EMPTY)
|
||||
|
||||
|
||||
override fun clear() {
|
||||
for (i in items.indices) {
|
||||
items[i] = ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return 8
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return items.all { it == ItemStack.EMPTY }
|
||||
}
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
if(slot == -1 || slot >= size()) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
return items[slot]
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
if(slot < 0 || slot >= size()) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
val result = items[slot].split(amount)
|
||||
if(items[slot].count == 0) {
|
||||
items[slot] = ItemStack.EMPTY
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
return removeStack(slot, 1)
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack) {
|
||||
val struct = tankEntity.multiblockStructure as? FossilMultiblockStructure
|
||||
tankEntity.world?.let {
|
||||
struct?.insertOrganicMaterial(stack, it)
|
||||
val returnIdentifier = NaturalMaterials.getReturnItem((stack))
|
||||
if(returnIdentifier != null ) {
|
||||
// Store the return item
|
||||
val returnItem = Registries.ITEM.get(returnIdentifier)
|
||||
storeReturnItem(returnItem, stack.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeReturnItem(returnItem: Item, count: Int) {
|
||||
val destStack = items.withIndex().firstOrNull {
|
||||
it.value == ItemStack.EMPTY || (it.value.count < it.value.maxCount && it.value.item == returnItem)
|
||||
}
|
||||
if (destStack != null) {
|
||||
if(destStack.value == ItemStack.EMPTY) {
|
||||
items[destStack.index] = ItemStack(returnItem, 1)
|
||||
} else {
|
||||
destStack.value.increment(count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
tankEntity.world?.let {
|
||||
tankEntity.multiblockStructure?.markDirty(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getAvailableSlots(side: Direction?): IntArray {
|
||||
return if(side == Direction.DOWN) intArrayOf(0,1,2,3,4,5,6,7) else intArrayOf(-1)
|
||||
}
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if (tankEntity.multiblockStructure is FossilMultiblockStructure) {
|
||||
if(dir != Direction.DOWN) {
|
||||
val structure = tankEntity.multiblockStructure as FossilMultiblockStructure
|
||||
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
|
||||
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
|
||||
&& structure.createdPokemon == null
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return false
|
||||
if(canUtilize) {
|
||||
// See if there's room
|
||||
for (i in items.indices ) {
|
||||
if(items[i] == ItemStack.EMPTY || (items[i].count < items[i].maxCount
|
||||
&& items[i].item == Registries.ITEM.get(returnItem))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
return dir == Direction.DOWN && slot >= 0 && slot < size()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity.fossil
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
|
||||
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.fossil.FossilState
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
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.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
|
||||
class FossilTubeBlockEntity(
|
||||
pos: BlockPos, state: BlockState,
|
||||
multiblockBuilder: MultiblockStructureBuilder
|
||||
) : FossilMultiblockEntity(pos, state, multiblockBuilder, CobblemonBlockEntities.FOSSIL_TUBE) {
|
||||
val inv = FossilTubeInventory(this)
|
||||
|
||||
class FossilTubeInventory(val tubeEntity: FossilTubeBlockEntity) : 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 {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack) {
|
||||
if (tubeEntity.multiblockStructure != null) {
|
||||
val struct = tubeEntity.multiblockStructure as FossilMultiblockStructure
|
||||
tubeEntity.world?.let {
|
||||
struct.insertOrganicMaterial(stack, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
if (tubeEntity.world != null) {
|
||||
tubeEntity.multiblockStructure?.markDirty(tubeEntity.world!!)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
if (tubeEntity.multiblockStructure is FossilMultiblockStructure) {
|
||||
val structure = tubeEntity.multiblockStructure as FossilMultiblockStructure
|
||||
return stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
|
||||
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
|
||||
&& structure.createdPokemon == null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+50
-53
@@ -13,11 +13,8 @@ import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
|
||||
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilTubeBlockEntity
|
||||
import com.cobblemon.mod.common.block.fossilmachine.FossilCompartmentBlock
|
||||
import com.cobblemon.mod.common.block.fossilmachine.FossilTubeBlock
|
||||
import com.cobblemon.mod.common.block.RestorationTankBlock
|
||||
import com.cobblemon.mod.common.api.multiblock.condition.BlockRelativeCondition
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilCompartmentBlockEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsList
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
@@ -53,13 +50,13 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
||||
|
||||
override val conditions = listOf(
|
||||
BlockRelativeCondition(
|
||||
FOSSIL_COMPARTMENT_PRED,
|
||||
FOSSIL_MONITOR_PRED,
|
||||
FOSSIL_ANALYZER_PRED,
|
||||
MONITOR_PRED,
|
||||
arrayOf(Direction.UP)
|
||||
),
|
||||
BlockRelativeCondition(
|
||||
FOSSIL_COMPARTMENT_PRED,
|
||||
FOSSIL_TUBE_PRED,
|
||||
FOSSIL_ANALYZER_PRED,
|
||||
RESTORATION_TANK_PRED,
|
||||
arrayOf(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST)
|
||||
)
|
||||
)
|
||||
@@ -72,68 +69,68 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
||||
The process of identifying the constituent blocks is a bit of a clusterfuck so here's an explanation
|
||||
|
||||
1. We find all of the monitors in the boundingBox
|
||||
2. We map those positions to a new list representing valid compartment positions
|
||||
(This is where it starts getting complicated) We want valid compartment positions, but we
|
||||
need to remember the index in the monitor position list that each compartment is valid for.
|
||||
So basically we take the monitor positions, check if they have a compartment underneath.
|
||||
If they do, set the element in the NEW list to be the position of the compartment.
|
||||
If they dont, set the element in the new list to be null
|
||||
3. Do a similar process for tubes.
|
||||
(Make a copy of the compartment positions, set them to the tube position if theres a tube, null if not)
|
||||
2. We map those positions to a new list representing valid analyzer positions
|
||||
(This is where it starts getting complicated) We want valid analyzer positions, but we
|
||||
need to remember the index in the monitor position list that each analyzer is valid for.
|
||||
So basically we take the monitor positions, check if they have a analyzer underneath.
|
||||
If they do, set the element in the NEW list to be the position of the analyzer.
|
||||
If they don't, set the element in the new list to be null
|
||||
3. Do a similar process for tanks.
|
||||
(Make a copy of the analyzer positions, set them to the tank position if there's a tank, null if not)
|
||||
|
||||
What this ends up doing is giving us a list at the end filled with either nulls or BlockPositions
|
||||
of tubes that have a valid structure. The index of any tube in the list corresponds to the index
|
||||
of the Monitor/Compartment in their respective lists. So if you have a fossilTubePositions list of
|
||||
[null BlockPos null], the fossilCompartment BlockPos is fossilCompPositions[1] and the monitor BlockPos
|
||||
is fossilMonitorPositions[1]
|
||||
of tanks that have a valid structure. The index of any tank in the list corresponds to the index
|
||||
of the Monitor/Analyzer in their respective lists. So if you have a fossilTankPositions list of
|
||||
[null BlockPos null], the fossilAnalyzer BlockPos is fossilCompPositions[1] and the monitor BlockPos
|
||||
is monitorPositions[1]
|
||||
|
||||
- Apion
|
||||
*/
|
||||
var fossilMonitorPositions = blocks.filter {
|
||||
FOSSIL_MONITOR_PRED.test(world, it)
|
||||
var monitorPositions = blocks.filter {
|
||||
MONITOR_PRED.test(world, it)
|
||||
}
|
||||
|
||||
var fossilCompPositions = fossilMonitorPositions.map {
|
||||
if (FOSSIL_COMPARTMENT_PRED.test(world, it.down())) it.down()
|
||||
var fossilAnalyzerPositions = monitorPositions.map {
|
||||
if (FOSSIL_ANALYZER_PRED.test(world, it.down())) it.down()
|
||||
else null
|
||||
}
|
||||
|
||||
var fossilTubePositions = fossilCompPositions.map { compPosition ->
|
||||
if (compPosition == null) {
|
||||
var restorationTankPositions = fossilAnalyzerPositions.map { analyzerPosition ->
|
||||
if (analyzerPosition == null) {
|
||||
return@map null
|
||||
}
|
||||
dirsToCheck.forEach {
|
||||
if (FOSSIL_TUBE_PRED.test(world, compPosition.offset(it))) {
|
||||
return@map compPosition.offset(it)
|
||||
if (RESTORATION_TANK_PRED.test(world, analyzerPosition.offset(it))) {
|
||||
return@map analyzerPosition.offset(it)
|
||||
}
|
||||
}
|
||||
return@map null
|
||||
}
|
||||
|
||||
val fossilTubeIndex = fossilTubePositions.indexOfFirst {
|
||||
val fossilTankIndex = restorationTankPositions.indexOfFirst {
|
||||
it != null
|
||||
}
|
||||
if (fossilTubeIndex == -1) {
|
||||
if (fossilTankIndex == -1) {
|
||||
Cobblemon.LOGGER.error("FossilMultiblockBuilder form called on invalid structure! This should never happen!")
|
||||
return
|
||||
}
|
||||
val fossilMonitorPos = fossilMonitorPositions[fossilTubeIndex]
|
||||
val fossilCompPos = fossilCompPositions[fossilTubeIndex]!!
|
||||
val fossilTubePos = fossilTubePositions[fossilTubeIndex]!!
|
||||
val monitorEntity = world.getBlockEntity(fossilMonitorPos) as MultiblockEntity
|
||||
val compEntity = world.getBlockEntity(fossilCompPos) as MultiblockEntity
|
||||
val tubeBaseEntity = world.getBlockEntity(fossilTubePos) as MultiblockEntity
|
||||
val tubeTopEntity = world.getBlockEntity(fossilTubePos.up()) as MultiblockEntity
|
||||
val structure = FossilMultiblockStructure(fossilMonitorPos, fossilCompPos, fossilTubePos)
|
||||
val monitorPos = monitorPositions[fossilTankIndex]
|
||||
val fossilAnalyzerPos = fossilAnalyzerPositions[fossilTankIndex]!!
|
||||
val restorationTankPos = restorationTankPositions[fossilTankIndex]!!
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as MultiblockEntity
|
||||
val analyzerEntity = world.getBlockEntity(fossilAnalyzerPos) as MultiblockEntity
|
||||
val tankBaseEntity = world.getBlockEntity(restorationTankPos) as MultiblockEntity
|
||||
val tankTopEntity = world.getBlockEntity(restorationTankPos.up()) as MultiblockEntity
|
||||
val structure = FossilMultiblockStructure(monitorPos, fossilAnalyzerPos, restorationTankPos)
|
||||
|
||||
structure.tubeConnectorDirection = dirsToCheck.filter {
|
||||
val adjPos = fossilTubePos.offset(it)
|
||||
return@filter adjPos == fossilCompPos
|
||||
structure.tankConnectorDirection = dirsToCheck.filter {
|
||||
val adjPos = restorationTankPos.offset(it)
|
||||
return@filter adjPos == fossilAnalyzerPos
|
||||
}.first()
|
||||
|
||||
compEntity.multiblockStructure = structure
|
||||
tubeBaseEntity.multiblockStructure = structure
|
||||
tubeTopEntity.multiblockStructure = structure
|
||||
analyzerEntity.multiblockStructure = structure
|
||||
tankBaseEntity.multiblockStructure = structure
|
||||
tankTopEntity.multiblockStructure = structure
|
||||
monitorEntity.multiblockStructure = structure
|
||||
structure.syncToClient(world)
|
||||
structure.markDirty(world)
|
||||
@@ -141,9 +138,9 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
||||
world.playSound(null, centerPos, CobblemonSounds.FOSSIL_MACHINE_ASSEMBLE, SoundCategory.BLOCKS)
|
||||
|
||||
//Set these to null so the builders can be freed
|
||||
compEntity.multiblockBuilder = null
|
||||
tubeBaseEntity.multiblockBuilder = null
|
||||
tubeTopEntity.multiblockBuilder = null
|
||||
analyzerEntity.multiblockBuilder = null
|
||||
tankBaseEntity.multiblockBuilder = null
|
||||
tankTopEntity.multiblockBuilder = null
|
||||
monitorEntity.multiblockBuilder = null
|
||||
}
|
||||
|
||||
@@ -153,18 +150,18 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
||||
nbt.putBoolean(DataKeys.FORMED, false)
|
||||
return@run nbt
|
||||
}
|
||||
val FOSSIL_MONITOR_PRED = BlockPredicate.Builder.create().blocks(CobblemonBlocks.FOSSIL_MONITOR)
|
||||
val MONITOR_PRED = BlockPredicate.Builder.create().blocks(CobblemonBlocks.MONITOR)
|
||||
.nbt(NBT_TO_CHECK)
|
||||
.build()
|
||||
val FOSSIL_COMPARTMENT_PRED = BlockPredicate.Builder.create()
|
||||
.blocks(CobblemonBlocks.FOSSIL_COMPARTMENT)
|
||||
val FOSSIL_ANALYZER_PRED = BlockPredicate.Builder.create()
|
||||
.blocks(CobblemonBlocks.FOSSIL_ANALYZER)
|
||||
.nbt(NBT_TO_CHECK)
|
||||
.build()
|
||||
|
||||
val FOSSIL_TUBE_PRED = BlockPredicate.Builder.create()
|
||||
val RESTORATION_TANK_PRED = BlockPredicate.Builder.create()
|
||||
.nbt(NBT_TO_CHECK)
|
||||
.blocks(CobblemonBlocks.FOSSIL_TUBE)
|
||||
.state(StatePredicate.Builder.create().exactMatch(FossilTubeBlock.PART, FossilTubeBlock.TubePart.BOTTOM).build())
|
||||
.blocks(CobblemonBlocks.RESTORATION_TANK)
|
||||
.state(StatePredicate.Builder.create().exactMatch(RestorationTankBlock.PART, RestorationTankBlock.TankPart.BOTTOM).build())
|
||||
.build()
|
||||
//lol thanks mojang for not allowing nbt puts to be chained
|
||||
}
|
||||
|
||||
+144
-75
@@ -17,22 +17,22 @@ import com.cobblemon.mod.common.api.fossil.NaturalMaterials
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockEntity
|
||||
import com.cobblemon.mod.common.api.multiblock.MultiblockStructure
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.entity.fossil.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.fossilmachine.FossilCompartmentBlock
|
||||
import com.cobblemon.mod.common.block.fossilmachine.FossilMonitorBlock
|
||||
import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.FossilAnalyzerBlock
|
||||
import com.cobblemon.mod.common.block.MonitorBlock
|
||||
import com.cobblemon.mod.common.block.RestorationTankBlock
|
||||
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.fossil.FossilState
|
||||
import com.cobblemon.mod.common.client.sound.CancellableSoundController
|
||||
import com.cobblemon.mod.common.client.sound.CancellableSoundInstance
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.HorizontalFacingBlock
|
||||
import net.minecraft.block.entity.BlockEntityTicker
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.entity.EntityPose
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
@@ -54,15 +54,18 @@ import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.world.World
|
||||
import java.util.UUID
|
||||
import kotlin.math.ceil
|
||||
|
||||
class FossilMultiblockStructure (
|
||||
val monitorPos: BlockPos,
|
||||
val compartmentPos: BlockPos,
|
||||
val tubeBasePos: BlockPos
|
||||
val analyzerPos: BlockPos,
|
||||
val tankBasePos: BlockPos,
|
||||
animAge: Int = -1,
|
||||
animPartialTicks: Float = 0F
|
||||
) : MultiblockStructure {
|
||||
|
||||
override val controllerBlockPos = compartmentPos
|
||||
override val controllerBlockPos = analyzerPos
|
||||
|
||||
|
||||
// TODO: API method for this
|
||||
@@ -78,10 +81,10 @@ class FossilMultiblockStructure (
|
||||
private var lastInteraction: Long = 0
|
||||
private var machineStartTime: Long = 0
|
||||
private var protectionTime: Int = -1
|
||||
private var fossilOwner: PlayerEntity? = null
|
||||
val fossilState = FossilState()
|
||||
private var fossilOwnerUUID: UUID? = null
|
||||
val fossilState = FossilState(animAge, animPartialTicks)
|
||||
var fossilInventory: MutableList<ItemStack> = mutableListOf<ItemStack>()
|
||||
var tubeConnectorDirection: Direction? = null
|
||||
var tankConnectorDirection: Direction? = null
|
||||
|
||||
//Only updated clientside
|
||||
var fillLevel = 0
|
||||
@@ -97,7 +100,7 @@ class FossilMultiblockStructure (
|
||||
|
||||
if (this.createdPokemon != null) {
|
||||
|
||||
if (!stack.isIn(CobblemonItemTags.POKEBALLS) || stack.item !is PokeBallItem) {
|
||||
if (!stack.isIn(CobblemonItemTags.POKE_BALLS) || stack.item !is PokeBallItem) {
|
||||
return ActionResult.PASS
|
||||
}
|
||||
|
||||
@@ -105,8 +108,12 @@ class FossilMultiblockStructure (
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
|
||||
if (this.fossilOwner != null && player != this.fossilOwner) {
|
||||
player.sendMessage(lang("fossilmachine.protected", this.fossilOwner!!.name), true)
|
||||
if (this.fossilOwnerUUID != null && player.uuid != this.fossilOwnerUUID) {
|
||||
var ownerName : String = "UNKNOWN_USER" // TODO: lang agnostic fallback
|
||||
server()?.userCache?.getByUuid(this.fossilOwnerUUID)?.orElse(null)?.name?.let {
|
||||
ownerName = it
|
||||
}
|
||||
player.sendMessage(lang("fossilmachine.protected", ownerName), true)
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
@@ -123,8 +130,14 @@ class FossilMultiblockStructure (
|
||||
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
CobblemonCriteria.RESURRECT_POKEMON.trigger(player, createdPokemon!!)
|
||||
|
||||
// Turn the monitor off
|
||||
val monitorState = world.getBlockState(monitorPos)
|
||||
if(!monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
|
||||
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, MonitorBlock.MonitorScreen.OFF))
|
||||
}
|
||||
|
||||
this.createdPokemon = null
|
||||
this.fossilOwner = null
|
||||
this.fossilOwnerUUID = null
|
||||
this.protectionTime = -1
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
@@ -147,7 +160,7 @@ class FossilMultiblockStructure (
|
||||
// remove last fossil in the fossil machine stack when grabbed out of the machine
|
||||
this.fossilInventory.removeAt(fossilInventory.size - 1)
|
||||
if(!world.isClient) {
|
||||
world.playSound(null, compartmentPos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_FOSSIL, SoundCategory.BLOCKS)
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
@@ -166,11 +179,11 @@ class FossilMultiblockStructure (
|
||||
stack?.decrement(1)
|
||||
}
|
||||
|
||||
fossilOwner = player
|
||||
fossilOwnerUUID = player.uuid
|
||||
fossilInventory.add(copyFossilStack)
|
||||
if(!world.isClient) {
|
||||
this.updateFossilType(world)
|
||||
world.playSound(null, compartmentPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
}
|
||||
@@ -191,16 +204,18 @@ class FossilMultiblockStructure (
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
|
||||
// pure client instances dont know what a valid fossil is so this is my janky workaround
|
||||
if (stack.isIn(CobblemonItemTags.FOSSILS)) return ActionResult.SUCCESS
|
||||
|
||||
return ActionResult.PASS
|
||||
}
|
||||
|
||||
public fun spawn(world: World, pos: BlockPos, directionToBehind: Direction, pokemon: Pokemon) : Boolean {
|
||||
val world = world ?: return false
|
||||
val entity = PokemonEntity(world, pokemon = pokemon)
|
||||
entity.calculateDimensions()
|
||||
val width = entity.boundingBox.xLength
|
||||
|
||||
val idealPlace = pos.add(directionToBehind.vector.multiply(ceil(width).toInt() + 1))
|
||||
val idealPlace = pos.add(directionToBehind.vector.multiply(ceil(width / 2.0).toInt() + 1))
|
||||
var box = entity.getDimensions(EntityPose.STANDING).getBoxAt(idealPlace.toCenterPos().subtract(0.0, 0.5, 0.0))
|
||||
|
||||
for (i in 0..5) {
|
||||
@@ -269,7 +284,7 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
return Math.max(15 - timeRemaining * 15 / TIME_TO_TAKE, 1)
|
||||
}
|
||||
if(tubeBasePos == pos || tubeBasePos.up() == pos) {
|
||||
if(tankBasePos == pos || tankBasePos.up() == pos) {
|
||||
return organicMaterialInside * 15 / MATERIAL_TO_START
|
||||
}
|
||||
return 0
|
||||
@@ -284,9 +299,9 @@ class FossilMultiblockStructure (
|
||||
if(success) {
|
||||
this.fossilState.growthState = "Taken"
|
||||
this.createdPokemon = null
|
||||
this.fossilOwner = null
|
||||
this.fossilOwnerUUID = null
|
||||
this.protectionTime = -1
|
||||
world.playSound(null, tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS)
|
||||
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS)
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
@@ -297,22 +312,21 @@ class FossilMultiblockStructure (
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as MultiblockEntity
|
||||
val compartmentEntity = world.getBlockEntity(compartmentPos) as MultiblockEntity
|
||||
val tubeBaseEntity = world.getBlockEntity(tubeBasePos) as MultiblockEntity
|
||||
val tubeTopEntity = world.getBlockEntity(tubeBasePos.up()) as MultiblockEntity
|
||||
val state = world.getBlockState(monitorEntity.pos)
|
||||
val analyzerEntity = world.getBlockEntity(analyzerPos) as MultiblockEntity
|
||||
val tankBaseEntity = world.getBlockEntity(tankBasePos) as MultiblockEntity
|
||||
val tankTopEntity = world.getBlockEntity(tankBasePos.up()) as MultiblockEntity
|
||||
val state = world.getBlockState(tankBaseEntity.pos)
|
||||
val direction = state.get(HorizontalFacingBlock.FACING).getOpposite()
|
||||
val wildPokemon: Pokemon? = this.createdPokemon
|
||||
|
||||
monitorEntity.multiblockStructure = null
|
||||
compartmentEntity.multiblockStructure = null
|
||||
tubeBaseEntity.multiblockStructure = null
|
||||
tubeTopEntity.multiblockStructure = null
|
||||
analyzerEntity.multiblockStructure = null
|
||||
tankBaseEntity.multiblockStructure = null
|
||||
tankTopEntity.multiblockStructure = null
|
||||
monitorEntity.masterBlockPos = null
|
||||
compartmentEntity.masterBlockPos = null
|
||||
tubeBaseEntity.masterBlockPos = null
|
||||
tubeTopEntity.masterBlockPos = null
|
||||
|
||||
analyzerEntity.masterBlockPos = null
|
||||
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) {
|
||||
@@ -321,10 +335,17 @@ class FossilMultiblockStructure (
|
||||
ItemScatterer.spawn(world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), stack)
|
||||
}
|
||||
}
|
||||
if(tankBaseEntity is RestorationTankBlockEntity) {
|
||||
tankBaseEntity.inv.items.forEach {
|
||||
val stack = ItemStack(it.item, 1)
|
||||
ItemScatterer.spawn(world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), stack)
|
||||
}
|
||||
tankBaseEntity.inv.clear()
|
||||
}
|
||||
|
||||
|
||||
// if the machine is broken while the pokemon is done then spawn the pokemon at the location and make it a wild pokemon
|
||||
if (this.fossilState.growthState == "Fully Grown" && wildPokemon != null) {
|
||||
if (wildPokemon != null) {
|
||||
//world.createExplosion(this.createdPokemon?.entity, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), 5F, World.ExplosionSourceType.TNT)
|
||||
|
||||
// instantiate the pokemon as a new entity and spawn it at the location of the machine
|
||||
@@ -334,29 +355,39 @@ class FossilMultiblockStructure (
|
||||
this.spawn(world, pos, direction, wildPokemon)
|
||||
|
||||
}
|
||||
|
||||
MinecraftClient.getInstance().soundManager.stopSounds(CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP.id, SoundCategory.BLOCKS)
|
||||
|
||||
this.protectionTime = -1
|
||||
this.updateFossilType(world)
|
||||
this.stopMachine(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
}
|
||||
|
||||
override fun markRemoved(world: World) {
|
||||
if(world.isClient) {
|
||||
CancellableSoundController.stopSound(this.tankBasePos, CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP.id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(world: World) {
|
||||
if (protectionTime > 0) protectionTime--
|
||||
if (protectionTime == 0) {
|
||||
protectionTime = -1
|
||||
this.fossilOwner = null
|
||||
world.playSound(null, tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_UNPROTECTED, SoundCategory.BLOCKS)
|
||||
this.fossilOwnerUUID = null
|
||||
this.updateProgress(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_UNPROTECTED, SoundCategory.BLOCKS)
|
||||
}
|
||||
|
||||
if (this.createdPokemon != null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isRunning() && (world.time - this.machineStartTime) % 160L == 0L) {
|
||||
world.playSound(null, this.tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
if (world.isClient && this.isRunning() && (world.time - this.machineStartTime) % 160L == 0L) {
|
||||
if(world.isClient) {
|
||||
CancellableSoundController.playSound(CancellableSoundInstance(CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP,
|
||||
tankBasePos, true, 1.0f, 1.0f, ))
|
||||
}
|
||||
}
|
||||
|
||||
if (this.timeRemaining == -1 && this.organicMaterialInside >= MATERIAL_TO_START && this.resultingFossil != null) {
|
||||
@@ -375,14 +406,13 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
if (this.timeRemaining == 0) {
|
||||
world.playSound(null, tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_FINISHED, SoundCategory.BLOCKS)
|
||||
MinecraftClient.getInstance().soundManager.stopSounds(CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP.id, SoundCategory.BLOCKS)
|
||||
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_FINISHED, SoundCategory.BLOCKS)
|
||||
fossilInventory.clear()
|
||||
|
||||
this.resultingFossil?.let {
|
||||
this.createdPokemon = it.result.create()
|
||||
}
|
||||
if(this.fossilOwner != null) {
|
||||
if(this.fossilOwnerUUID != null) {
|
||||
protectionTime = PROTECTION_TIME
|
||||
}
|
||||
|
||||
@@ -391,20 +421,23 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
override fun syncToClient(world: World) {
|
||||
val tubeBaseEntity = world.getBlockEntity(tubeBasePos) as MultiblockEntity
|
||||
val compartmentEntity = world.getBlockEntity(controllerBlockPos) as MultiblockEntity
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as MultiblockEntity
|
||||
val tankBaseEntity = world.getBlockEntity(tankBasePos) as? MultiblockEntity
|
||||
val analyzerEntity = world.getBlockEntity(controllerBlockPos) as? MultiblockEntity
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as? MultiblockEntity
|
||||
|
||||
world.updateListeners(tubeBasePos, tubeBaseEntity.cachedState, tubeBaseEntity.cachedState, Block.NOTIFY_LISTENERS)
|
||||
world.updateListeners(compartmentPos, compartmentEntity.cachedState, compartmentEntity.cachedState, Block.NOTIFY_LISTENERS)
|
||||
world.updateListeners(monitorPos, monitorEntity.cachedState, monitorEntity.cachedState, Block.NOTIFY_LISTENERS)
|
||||
if(tankBaseEntity != null)
|
||||
world.updateListeners(tankBasePos, tankBaseEntity.cachedState, tankBaseEntity.cachedState, Block.NOTIFY_LISTENERS)
|
||||
if(analyzerEntity != null)
|
||||
world.updateListeners(analyzerPos, analyzerEntity.cachedState, analyzerEntity.cachedState, Block.NOTIFY_LISTENERS)
|
||||
if(monitorEntity != null)
|
||||
world.updateListeners(monitorPos, monitorEntity.cachedState, monitorEntity.cachedState, Block.NOTIFY_LISTENERS)
|
||||
}
|
||||
|
||||
override fun markDirty(world: World) {
|
||||
val entities = listOf(
|
||||
world.getBlockEntity(compartmentPos),
|
||||
world.getBlockEntity(tubeBasePos),
|
||||
world.getBlockEntity(tubeBasePos.up()),
|
||||
world.getBlockEntity(analyzerPos),
|
||||
world.getBlockEntity(tankBasePos),
|
||||
world.getBlockEntity(tankBasePos.up()),
|
||||
world.getBlockEntity(monitorPos)
|
||||
)
|
||||
entities.forEach {
|
||||
@@ -416,8 +449,11 @@ class FossilMultiblockStructure (
|
||||
this.timeRemaining = TIME_TO_TAKE
|
||||
this.machineStartTime = world.time
|
||||
|
||||
world.playSound(null, tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_ACTIVATE, SoundCategory.BLOCKS)
|
||||
world.playSound(null, tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP, SoundCategory.BLOCKS)
|
||||
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_ACTIVATE, SoundCategory.BLOCKS)
|
||||
if(world.isClient) {
|
||||
CancellableSoundController.playSound(CancellableSoundInstance(CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP,
|
||||
tankBasePos, true, 1.0f, 1.0f, ))
|
||||
}
|
||||
|
||||
this.updateOnStatus(world)
|
||||
this.updateProgress(world)
|
||||
@@ -432,6 +468,10 @@ class FossilMultiblockStructure (
|
||||
|
||||
fossilInventory.clear()
|
||||
|
||||
if(world.isClient) {
|
||||
CancellableSoundController.stopSound(tankBasePos, CobblemonSounds.FOSSIL_MACHINE_ACTIVE_LOOP.id)
|
||||
}
|
||||
|
||||
this.updateOnStatus(world)
|
||||
this.updateProgress(world)
|
||||
this.syncToClient(world)
|
||||
@@ -439,14 +479,38 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
fun updateOnStatus(world: World) {
|
||||
val compartmentState = world.getBlockState(compartmentPos)
|
||||
world.setBlockState(compartmentPos, compartmentState.with(FossilCompartmentBlock.ON, timeRemaining >= 0))
|
||||
val upperTankPos = tankBasePos.up()
|
||||
val analyzerState = world.getBlockState(analyzerPos)
|
||||
val tankState = world.getBlockState(tankBasePos.up())
|
||||
world.setBlockState(analyzerPos, analyzerState.with(FossilAnalyzerBlock.ON, timeRemaining >= 0))
|
||||
world.setBlockState(upperTankPos, tankState.with(RestorationTankBlock.ON, timeRemaining >= 0))
|
||||
}
|
||||
|
||||
fun updateProgress(world: World) {
|
||||
val progress = if (timeRemaining <= 0) 0 else ((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE) + 1
|
||||
val screenID = if(protectionTime > 0F ) {
|
||||
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
|
||||
} else if (timeRemaining <= 0) {
|
||||
MonitorBlock.MonitorScreen.OFF
|
||||
} else {
|
||||
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
|
||||
}
|
||||
val monitorState = world.getBlockState(monitorPos)
|
||||
world.setBlockState(monitorPos, monitorState.with(FossilMonitorBlock.PROGRESS, progress))
|
||||
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
|
||||
}
|
||||
|
||||
fun getProgressScreen(progress:Int) : MonitorBlock.MonitorScreen {
|
||||
return when (progress) {
|
||||
0 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_1
|
||||
1 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_2
|
||||
2 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_3
|
||||
3 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_4
|
||||
4 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_5
|
||||
5 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_6
|
||||
6 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_7
|
||||
7 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_8
|
||||
8 -> MonitorBlock.MonitorScreen.BLUE_PROGRESS_9
|
||||
else -> MonitorBlock.MonitorScreen.OFF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -491,11 +555,11 @@ class FossilMultiblockStructure (
|
||||
organicMaterialInside += natureValue
|
||||
}
|
||||
if (this.organicMaterialInside >= MATERIAL_TO_START) {
|
||||
world.playSound(null, this.tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_DNA_FULL, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
world.playSound(null, this.tankBasePos, CobblemonSounds.FOSSIL_MACHINE_DNA_FULL, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
} else if (world.time - this.lastInteraction < 10) {
|
||||
world.playSound(null, this.tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_INSERT_DNA_SMALL, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
world.playSound(null, this.tankBasePos, CobblemonSounds.FOSSIL_MACHINE_INSERT_DNA_SMALL, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
} else {
|
||||
world.playSound(null, this.tubeBasePos, CobblemonSounds.FOSSIL_MACHINE_INSERT_DNA, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
world.playSound(null, this.tankBasePos, CobblemonSounds.FOSSIL_MACHINE_INSERT_DNA, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
}
|
||||
this.markDirty(world)
|
||||
if (oldFillStage != (organicMaterialInside / 8)) {
|
||||
@@ -514,7 +578,7 @@ class FossilMultiblockStructure (
|
||||
|
||||
//add fossil to the stack in the Compartment
|
||||
this.fossilInventory.add(stack)
|
||||
world.playSound(null, compartmentPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
|
||||
|
||||
this.updateFossilType(world)
|
||||
this.markDirty(world)
|
||||
@@ -527,16 +591,19 @@ class FossilMultiblockStructure (
|
||||
override fun writeToNbt(): NbtCompound {
|
||||
val result = NbtCompound()
|
||||
result.put(DataKeys.MONITOR_POS, NbtHelper.fromBlockPos(monitorPos))
|
||||
result.put(DataKeys.COMPARTMENT_POS, NbtHelper.fromBlockPos(compartmentPos))
|
||||
result.put(DataKeys.TUBE_BASE_POS, NbtHelper.fromBlockPos(tubeBasePos))
|
||||
result.put(DataKeys.ANALYZER_POS, NbtHelper.fromBlockPos(analyzerPos))
|
||||
result.put(DataKeys.TANK_BASE_POS, NbtHelper.fromBlockPos(tankBasePos))
|
||||
result.putInt(DataKeys.TIME_LEFT, timeRemaining)
|
||||
result.putInt(DataKeys.PROTECTED_TIME_LEFT, protectionTime)
|
||||
if(fossilOwnerUUID != null)
|
||||
result.putUuid(DataKeys.FOSSIL_OWNER, fossilOwnerUUID)
|
||||
result.putInt(DataKeys.ORGANIC_MATERIAL, organicMaterialInside)
|
||||
val fossilInv = NbtList()
|
||||
fossilInventory.forEach{ item ->
|
||||
fossilInv.add(item.writeNbt(NbtCompound()))
|
||||
}
|
||||
result.put(DataKeys.FOSSIL_INVENTORY, fossilInv)
|
||||
result.putString(DataKeys.CONNECTOR_DIRECTION, tubeConnectorDirection?.toString())
|
||||
result.putString(DataKeys.CONNECTOR_DIRECTION, tankConnectorDirection?.toString())
|
||||
|
||||
if (this.resultingFossil != null) {
|
||||
result.putString(DataKeys.INSERTED_FOSSIL, this.resultingFossil!!.asString())
|
||||
@@ -562,14 +629,16 @@ class FossilMultiblockStructure (
|
||||
const val TIME_PER_STAGE = TIME_TO_TAKE / 8
|
||||
const val PROTECTION_TIME = 6000
|
||||
|
||||
fun fromNbt(nbt: NbtCompound): FossilMultiblockStructure {
|
||||
fun fromNbt(nbt: NbtCompound, animAge: Int = -1, partialTicks: Float = 0f): FossilMultiblockStructure {
|
||||
val monitorPos = NbtHelper.toBlockPos(nbt.getCompound(DataKeys.MONITOR_POS))
|
||||
val compartmentPos = NbtHelper.toBlockPos(nbt.getCompound(DataKeys.COMPARTMENT_POS))
|
||||
val tubeBasePos = NbtHelper.toBlockPos(nbt.getCompound(DataKeys.TUBE_BASE_POS))
|
||||
val compartmentPos = NbtHelper.toBlockPos(nbt.getCompound(DataKeys.ANALYZER_POS))
|
||||
val tankBasePos = NbtHelper.toBlockPos(nbt.getCompound(DataKeys.TANK_BASE_POS))
|
||||
|
||||
val result = FossilMultiblockStructure(monitorPos, compartmentPos, tubeBasePos)
|
||||
val result = FossilMultiblockStructure(monitorPos, compartmentPos, tankBasePos, animAge, partialTicks)
|
||||
result.organicMaterialInside = nbt.getInt(DataKeys.ORGANIC_MATERIAL)
|
||||
result.timeRemaining = nbt.getInt(DataKeys.TIME_LEFT)
|
||||
result.protectionTime = if(nbt.contains(DataKeys.PROTECTED_TIME_LEFT)) nbt.getInt(DataKeys.PROTECTED_TIME_LEFT) else -1
|
||||
result.fossilOwnerUUID = if(nbt.contains(DataKeys.FOSSIL_OWNER)) nbt.getUuid(DataKeys.FOSSIL_OWNER) else null
|
||||
|
||||
val fossilInv = (nbt.get(DataKeys.FOSSIL_INVENTORY) as NbtList)
|
||||
val actualFossilList = mutableListOf<ItemStack>()
|
||||
@@ -577,7 +646,7 @@ class FossilMultiblockStructure (
|
||||
actualFossilList.add(ItemStack.fromNbt(it as NbtCompound))
|
||||
}
|
||||
result.fossilInventory = actualFossilList
|
||||
result.tubeConnectorDirection = Direction.byName(nbt.getString(DataKeys.CONNECTOR_DIRECTION))
|
||||
result.tankConnectorDirection = Direction.byName(nbt.getString(DataKeys.CONNECTOR_DIRECTION))
|
||||
|
||||
if (nbt.contains(DataKeys.INSERTED_FOSSIL)) {
|
||||
val id = Identifier(nbt.getString(DataKeys.INSERTED_FOSSIL))
|
||||
|
||||
+28
-28
@@ -21,44 +21,44 @@ import net.minecraft.util.Identifier
|
||||
object CobblemonBakingOverrides {
|
||||
val models = mutableListOf<BakingOverride>()
|
||||
|
||||
val FOSSIL_FLUID_BUBBLING = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_bubbling"),
|
||||
cobblemonModel("fossil_fluid_bubbling", "none")
|
||||
val RESTORATION_TANK_FLUID_BUBBLING = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_bubbling"),
|
||||
cobblemonModel("restoration_tank_fluid_bubbling", "none")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_1 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_1"),
|
||||
cobblemonModel("fossil_fluid_chunked", "1")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_1 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_1"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "1")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_2 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_2"),
|
||||
cobblemonModel("fossil_fluid_chunked", "2")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_2 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_2"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "2")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_3 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_3"),
|
||||
cobblemonModel("fossil_fluid_chunked", "3")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_3 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_3"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "3")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_4 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_4"),
|
||||
cobblemonModel("fossil_fluid_chunked", "4")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_4 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_4"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "4")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_5 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_5"),
|
||||
cobblemonModel("fossil_fluid_chunked", "5")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_5 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_5"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "5")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_6 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_6"),
|
||||
cobblemonModel("fossil_fluid_chunked", "6")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_6 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_6"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "6")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_7 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_7"),
|
||||
cobblemonModel("fossil_fluid_chunked", "7")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_7 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_7"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "7")
|
||||
)
|
||||
val FOSSIL_FLUID_CHUNKED_8 = registerOverride(
|
||||
cobblemonResource("block/fossil/fossil_fluid_chunked_8"),
|
||||
cobblemonModel("fossil_fluid_chunked", "8")
|
||||
val RESTORATION_TANK_FLUID_CHUNKED_8 = registerOverride(
|
||||
cobblemonResource("block/restoration_tank_fluid_chunked_8"),
|
||||
cobblemonModel("restoration_tank_fluid_chunked", "8")
|
||||
)
|
||||
val RESTORATION_TANK_CONNECTOR = registerOverride(
|
||||
cobblemonResource("block/fossil/restoration_tank_connector"),
|
||||
cobblemonResource("block/restoration_tank_connector"),
|
||||
cobblemonModel("restoration_tank_connector", "none")
|
||||
)
|
||||
|
||||
|
||||
@@ -23,11 +23,12 @@ import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
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.FossilCompartmentRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.FossilTubeRenderer
|
||||
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.HealingMachineRenderer
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.entity.FallingStarRenderer
|
||||
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
|
||||
@@ -45,6 +46,7 @@ import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
|
||||
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
|
||||
@@ -162,7 +164,7 @@ object CobblemonClient {
|
||||
|
||||
this.implementation.registerBlockRenderType(
|
||||
RenderLayer.getCutout(),
|
||||
CobblemonBlocks.FOSSIL_COMPARTMENT,
|
||||
CobblemonBlocks.FOSSIL_ANALYZER,
|
||||
CobblemonBlocks.APRICORN_DOOR,
|
||||
CobblemonBlocks.APRICORN_TRAPDOOR,
|
||||
CobblemonBlocks.APRICORN_SIGN,
|
||||
@@ -202,7 +204,7 @@ object CobblemonClient {
|
||||
CobblemonBlocks.REVIVAL_HERB,
|
||||
*CobblemonBlocks.berries().values.toTypedArray(),
|
||||
CobblemonBlocks.POTTED_PEP_UP_FLOWER,
|
||||
CobblemonBlocks.FOSSIL_TUBE,
|
||||
CobblemonBlocks.RESTORATION_TANK,
|
||||
CobblemonBlocks.SMALL_BUDDING_TUMBLESTONE,
|
||||
CobblemonBlocks.MEDIUM_BUDDING_TUMBLESTONE,
|
||||
CobblemonBlocks.LARGE_BUDDING_TUMBLESTONE,
|
||||
@@ -242,8 +244,8 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.BERRY, ::BerryBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.SIGN, ::SignBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HANGING_SIGN, ::HangingSignBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_COMPARTMENT, ::FossilCompartmentRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_TUBE, ::FossilTubeRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_ANALYZER, ::FossilAnalyzerRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -255,6 +257,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 Falling Star renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.FALLING_STAR_ENTITY) { ctx -> FallingStarRenderer(ctx) }
|
||||
LOGGER.info("Registering PokeRod Bobber renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.POKE_BOBBER) { ctx -> PokeBobberEntityRenderer(ctx) }
|
||||
}
|
||||
@@ -270,6 +274,7 @@ object CobblemonClient {
|
||||
PokeBallModelRepository.reload(resourceManager)
|
||||
BerryModelRepository.reload(resourceManager)
|
||||
FossilModelRepository.reload(resourceManager)
|
||||
MiscModelRepository.reload(resourceManager)
|
||||
LOGGER.info("Loaded assets")
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -8,7 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.entity
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.entity.EntitySideDelegate
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.reactive.SettableObservable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
@@ -37,6 +40,11 @@ class EmptyPokeBallClientDelegate : PokeBallPoseableState(), EntitySideDelegate<
|
||||
this.currentEntity = entity
|
||||
age = entity.age
|
||||
initSubscriptions()
|
||||
this.runtime.environment.getQueryStruct().addFunctions(mapOf(
|
||||
"pokeball_type" to java.util.function.Function {
|
||||
return@Function DoubleValue(currentEntity.pokeBall.name.toString())
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
override fun tick(entity: EmptyPokeBallEntity) {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.entity
|
||||
|
||||
import com.cobblemon.mod.common.api.entity.EntitySideDelegate
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
|
||||
|
||||
class GenericBedrockClientDelegate : EntitySideDelegate<GenericBedrockEntity>, PoseableEntityState<GenericBedrockEntity>() {
|
||||
lateinit var currentEntity: GenericBedrockEntity
|
||||
override val schedulingTracker
|
||||
get() = getEntity().schedulingTracker
|
||||
override fun getEntity() = currentEntity
|
||||
|
||||
override fun initialize(entity: GenericBedrockEntity) {
|
||||
super.initialize(entity)
|
||||
this.currentEntity = entity
|
||||
this.age = entity.age
|
||||
this.currentModel = GenericBedrockEntityModelRepository.getPoser(entity.category, entity.aspects)
|
||||
|
||||
val model = currentModel!!
|
||||
model.context.put(RenderContext.ENTITY, entity)
|
||||
currentModel!!.updateLocators(this)
|
||||
updateLocatorPosition(entity.pos)
|
||||
|
||||
val currentPoseType = entity.getCurrentPoseType()
|
||||
val pose = this.currentModel!!.poses.values.firstOrNull { currentPoseType in it.poseTypes && (it.condition == null || it.condition.invoke(entity)) }
|
||||
if (pose != null) {
|
||||
doLater { setPose(pose.poseName) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(entity: GenericBedrockEntity) {
|
||||
super.tick(entity)
|
||||
updateLocatorPosition(entity.pos)
|
||||
incrementAge(entity)
|
||||
}
|
||||
|
||||
override fun updatePartialTicks(partialTicks: Float) {
|
||||
this.currentPartialTicks = partialTicks
|
||||
}
|
||||
}
|
||||
+142
-9
@@ -8,25 +8,48 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.entity
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnClient
|
||||
import com.cobblemon.mod.common.api.scheduling.lerpOnClient
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatefulAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
|
||||
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer.Companion.ease
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.MovingSoundInstance
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import java.lang.Float.min
|
||||
import kotlin.math.abs
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.data.TrackedData
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideDelegate {
|
||||
companion object {
|
||||
const val BEAM_SHRINK_TIME = 0.8F
|
||||
const val BEAM_SHRINK_TIME = 0.4F
|
||||
const val BEAM_EXTEND_TIME = 0.2F
|
||||
const val POKEBALL_AIR_TIME = 0.5F
|
||||
}
|
||||
|
||||
override val schedulingTracker: SchedulingTracker
|
||||
@@ -43,10 +66,21 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
|
||||
var beamStartTime = System.currentTimeMillis()
|
||||
var ballStartTime = System.currentTimeMillis()
|
||||
var ballDone = false
|
||||
var ballOffset = 0f
|
||||
var ballRotOffset = 0f
|
||||
var sendOutPosition: Vec3d? = null
|
||||
var sendOutOffset: Vec3d? = null
|
||||
var playedSendOutSound: Boolean = false
|
||||
var playedThrowingSound: Boolean = false
|
||||
|
||||
val secondsSinceBeamEffectStarted: Float
|
||||
get() = (System.currentTimeMillis() - beamStartTime) / 1000F
|
||||
|
||||
val secondsSinceBallThrown: Float
|
||||
get() = (System.currentTimeMillis() - ballStartTime) / 1000F
|
||||
|
||||
private var cryAnimation: StatefulAnimation<PokemonEntity, *>? = null
|
||||
|
||||
override fun onTrackedDataSet(data: TrackedData<*>) {
|
||||
@@ -61,7 +95,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
if (isDying) {
|
||||
val model = (currentModel ?: return) as PokemonPoseableModel
|
||||
val animation = try {
|
||||
model.getFaintAnimation(currentEntity, this)
|
||||
model.getAnimation(this, "faint", runtime)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
@@ -76,21 +110,95 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
0 -> { /* Do nothing */ }
|
||||
1 -> {
|
||||
// Scaling up out of pokeball
|
||||
playedSendOutSound = false
|
||||
entityScaleModifier = 0F
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
ballStartTime = System.currentTimeMillis()
|
||||
currentEntity.isInvisible = true
|
||||
afterOnClient(seconds = BEAM_EXTEND_TIME) {
|
||||
lerpOnClient(BEAM_SHRINK_TIME) { entityScaleModifier = it }
|
||||
currentEntity.isInvisible = false
|
||||
ballDone = false
|
||||
var soundPos = currentEntity.pos
|
||||
currentEntity.pokemon.getOwnerUUID()?.let{
|
||||
currentEntity.world.getPlayerByUuid(it)?.let {
|
||||
val offset = it.pos.subtract(currentEntity.pos.add(0.0, 2.0 - (ballOffset.toDouble()/10f), 0.0)).normalize().multiply(-ease(ballOffset.toDouble()))
|
||||
with(it.pos.subtract(currentEntity.pos)) {
|
||||
var newOffset = offset.multiply(2.0)
|
||||
val distance = it.pos.distanceTo(currentEntity.pos)
|
||||
newOffset = newOffset.multiply((distance / 10.0) * 5)
|
||||
soundPos = currentEntity.pos.add(newOffset)
|
||||
}
|
||||
it.swingHand(it.activeHand ?: Hand.MAIN_HAND)
|
||||
}
|
||||
}
|
||||
val client = MinecraftClient.getInstance()
|
||||
val sound = MovingSoundInstance(SoundEvent.of(CobblemonSounds.POKE_BALL_TRAIL.id), SoundCategory.PLAYERS, { sendOutPosition?.add(sendOutOffset) }, 0.1f, 1f, false, 20, 0)
|
||||
if(!playedThrowingSound){
|
||||
client.soundManager.play(sound)
|
||||
playedThrowingSound = true
|
||||
}
|
||||
lerpOnClient(POKEBALL_AIR_TIME) { ballOffset = it }
|
||||
ballRotOffset = ((Math.random()) * currentEntity.world.random.nextBetween(-15, 15)).toFloat()
|
||||
|
||||
currentEntity.after(seconds = POKEBALL_AIR_TIME){
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
ballDone = true
|
||||
if (client.soundManager.get(CobblemonSounds.POKE_BALL_OPEN.id) != null && !playedSendOutSound) {
|
||||
currentEntity.owner?.let {
|
||||
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
|
||||
playedSendOutSound = true
|
||||
/// create end rod particles in a 0.1 radius around the soundPos with a count of 50 and a random velocity of 0.1
|
||||
sendOutPosition?.let{
|
||||
val newPos = it.add(sendOutOffset)
|
||||
val ballType = currentEntity.pokemon.caughtBall.name.path.toLowerCase().replace("_","")
|
||||
val mode = if(currentEntity.isBattling) "battle" else "casual"
|
||||
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
|
||||
sendflash?.let { effect ->
|
||||
val wrapper = MatrixWrapper()
|
||||
val matrix = MatrixStack()
|
||||
matrix.translate(newPos.x, newPos.y, newPos.z)
|
||||
wrapper.updateMatrix(matrix.peek().positionMatrix)
|
||||
val world = MinecraftClient.getInstance().world ?: return@let
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
|
||||
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
|
||||
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
|
||||
afterOnClient(seconds = 0.01667f) {
|
||||
ballsparks?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
ballsendsparkle?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
currentEntity.after(seconds = 0.4f) {
|
||||
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
|
||||
ballsparkle?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
currentEntity.after(seconds = BEAM_EXTEND_TIME) {
|
||||
lerpOnClient(BEAM_SHRINK_TIME) { entityScaleModifier = it }
|
||||
currentEntity.isInvisible = false
|
||||
currentEntity.after(seconds = POKEBALL_AIR_TIME*2){
|
||||
ballOffset = 0f
|
||||
ballRotOffset = 0f
|
||||
sendOutPosition = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Scaling down into pokeball
|
||||
entityScaleModifier = 1F
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
ballOffset = 0f
|
||||
ballRotOffset = 0f
|
||||
sendOutPosition = null
|
||||
afterOnClient(seconds = BEAM_EXTEND_TIME) {
|
||||
val afterPoint2 = System.currentTimeMillis()
|
||||
lerpOnClient(BEAM_SHRINK_TIME) {
|
||||
lerpOnClient(BEAM_SHRINK_TIME) {
|
||||
entityScaleModifier = (1 - it)
|
||||
}
|
||||
}
|
||||
@@ -118,6 +226,27 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
override fun initialize(entity: PokemonEntity) {
|
||||
this.currentEntity = entity
|
||||
this.age = entity.age
|
||||
|
||||
this.runtime.environment.getQueryStruct().addFunctions(mapOf(
|
||||
"in_battle" to java.util.function.Function {
|
||||
return@Function DoubleValue(currentEntity.isBattling)
|
||||
},
|
||||
"shiny" to java.util.function.Function {
|
||||
return@Function DoubleValue(currentEntity.pokemon.shiny)
|
||||
},
|
||||
"form" to java.util.function.Function {
|
||||
return@Function StringValue(currentEntity.pokemon.form.name)
|
||||
},
|
||||
"width" to java.util.function.Function {
|
||||
return@Function DoubleValue(currentEntity.boundingBox.xLength)
|
||||
},
|
||||
"height" to java.util.function.Function {
|
||||
return@Function DoubleValue(currentEntity.boundingBox.yLength)
|
||||
},
|
||||
"weight" to java.util.function.Function {
|
||||
return@Function DoubleValue(currentEntity.pokemon.species.weight.toDouble())
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
override fun tick(entity: PokemonEntity) {
|
||||
@@ -144,12 +273,16 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
fun cry() {
|
||||
val model = currentModel ?: return
|
||||
if (model is PokemonPoseableModel) {
|
||||
if (cryAnimation != null && cryAnimation in statefulAnimations) {
|
||||
if (cryAnimation != null && (cryAnimation in statefulAnimations || cryAnimation == primaryAnimation)) {
|
||||
return
|
||||
}
|
||||
|
||||
val animation = model.cryAnimation(currentEntity, this) ?: return
|
||||
statefulAnimations.add(animation)
|
||||
if (animation is PrimaryAnimation) {
|
||||
addPrimaryAnimation(animation)
|
||||
} else {
|
||||
statefulAnimations.add(animation)
|
||||
}
|
||||
cryAnimation = animation
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -138,7 +138,8 @@ abstract class BattleGimmickButton(gimmick: ShowdownMoveset.Gimmick, val x: Floa
|
||||
pp = gimmickTemplate?.pp ?: moveTemplate.pp,
|
||||
priority = gimmickTemplate?.priority ?: moveTemplate.priority,
|
||||
critRatio = gimmickTemplate?.critRatio ?: moveTemplate.critRatio,
|
||||
effectChances = gimmickTemplate?.effectChances ?: moveTemplate.effectChances
|
||||
effectChances = gimmickTemplate?.effectChances ?: moveTemplate.effectChances,
|
||||
actionEffect = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -9,7 +9,9 @@
|
||||
package com.cobblemon.mod.common.client.gui.interact.wheel
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.tooltip.Tooltip
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
@@ -21,6 +23,7 @@ import org.joml.Vector4f
|
||||
class InteractWheelButton(
|
||||
private val iconResource: Identifier?,
|
||||
private val buttonResource: Identifier,
|
||||
private val tooltipText: String?,
|
||||
x: Int,
|
||||
y: Int,
|
||||
private val isEnabled: Boolean,
|
||||
@@ -50,6 +53,12 @@ class InteractWheelButton(
|
||||
alpha = if (isEnabled) 1f else 0.4f
|
||||
)
|
||||
|
||||
if(isHovered(mouseX.toFloat(), mouseY.toFloat())){
|
||||
tooltipText?.let {
|
||||
context.drawTooltip(MinecraftClient.getInstance().textRenderer, Text.translatable(it), mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
if (iconResource != null) {
|
||||
val (iconX, iconY) = getIconPosition()
|
||||
val colour = this.colour() ?: Vector3f(1F, 1F, 1F)
|
||||
@@ -86,4 +95,8 @@ class InteractWheelButton(
|
||||
return mouseX in xMin..xMax && mouseY in yMin..yMax
|
||||
}
|
||||
|
||||
override fun getTooltip(): Tooltip? {
|
||||
tooltipText?.let { return Tooltip.of(Text.translatable(it)) } ?: return super.getTooltip()
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user