Compare commits

..
Author SHA1 Message Date
Sterrezo a602df2fb8 Removed a missing sound from sounds.json 2025-09-27 12:11:09 +02:00
Sterrezo f4d64eb05b Merge branch 'main' into sterr/bugfixes 2025-09-27 12:08:56 +02:00
Hiroku ce3f703de2 Merge branch 'matrix-reloaded' into 'main'
Allow reload of some registries

See merge request cable-mc/cobblemon!1878
2025-09-27 08:31:10 +00:00
Hiroku 8ed68742df Merge branch 'exp-actionbar-fix' into 'main'
Fixes EXP gained actionbar not using the AddExperienceResult.

See merge request cable-mc/cobblemon!1875
2025-09-27 05:53:33 +00:00
Landon 16aa930735 Merge branch 'boat-tweaks' into 'main'
Boat tweaks

See merge request cable-mc/cobblemon!1879
2025-09-26 03:41:03 +00:00
Landon Wills 55733e8ad5 Add offset setting for determining surface level 2025-09-25 22:52:18 -04:00
Landon Wills 67591efdad Change sprint modifier expression, adjust acceleration formula 2025-09-25 22:26:15 -04:00
Paul 5d300c8157 Change how reloadability is checked for /reload, maybe this will fix server stuff 2025-09-26 11:46:25 +10:00
Test c77198a845 rm unused imports 2025-09-25 20:00:02 -04:00
Test bd32c07cc1 fix ride stat percentage 2025-09-25 19:59:23 -04:00
Hiroku 99cfc4f147 Merge branch 'stat' into 'main'
🏇🏻

Closes #1687 and #1684

See merge request cable-mc/cobblemon!1870
2025-09-25 12:38:18 +00:00
Paul 89281a1f6d Experimental reload support for various datapack folders 2025-09-25 21:31:42 +10:00
Paul ad044fb05c Aprijuice back to flat boost mechanics, UI corrections 2025-09-25 20:38:00 +10:00
Polymeta 7088ce3828 Merge branch 'fix-scanner-sensitivity' into 'main'
Fix the scanner sensitivity I broke a few hours ago.

See merge request cable-mc/cobblemon!1876
2025-09-25 06:08:39 +00:00
JPAKx4 fc6a712906 Fix the scanner sensitivity I broke a few hours ago. 2025-09-24 21:51:41 -05:00
Neovitalism 1d0397c399 Update Pokemon.kt 2025-09-24 21:13:14 -05:00
Polymeta 8ad443a4c5 Merge branch 'riding-behaviour-tweaks-four' into 'main'
Quick riding tweaks

See merge request cable-mc/cobblemon!1873
2025-09-24 21:31:07 +00:00
Jackson BowesandPolymeta 0d1f9a0093 Quick riding tweaks 2025-09-24 21:31:07 +00:00
Polymeta 7587344cb4 Merge branch 'NavigationAndWanderTargetImprovements' into 'main'
NavigationAndWanderTargetImprovements

Closes #1693

See merge request cable-mc/cobblemon!1872
2025-09-24 21:24:48 +00:00
Polymeta f0926d7524 Merge branch 'scan-ride-crash' into 'main'
Fix crash when you zoom in using a pokedex and begin rolling

See merge request cable-mc/cobblemon!1874
2025-09-24 21:16:53 +00:00
JPAKx4 7c3b17d841 Fix crash when you zoom in using a pokedex and begin rolling 2025-09-24 14:27:11 -05:00
Polymeta 263e3cd79f Merge branch 'caring-farmer' into 'main'
Tag Support for Interactions

See merge request cable-mc/cobblemon!1871
2025-09-23 06:37:15 +00:00
JazzMcNade 2ba8062915 clean up 2025-09-23 01:36:14 -05:00
JazzMcNade edc298bf64 Fixed water walkers targeting lava surfaces and vice versa 2025-09-23 01:32:57 -05:00
JazzMcNade aa1158fcfe Fixed null exception , fixed water walkers being unable to rise up in water. 2025-09-23 01:01:48 -05:00
JazzMcNade ab40ae81c4 Merge branch 'main' into NavigationAndWanderTargetImprovements 2025-09-22 20:37:21 -05:00
dhi_holo b5df1878ed Update furfrou.json
Now can use shears from anything
2025-09-22 21:30:43 +10:00
dhi_holo 800965cb40 Tag!
Added support for the Brushes and Fertilizer item tags in the Interactions (brushes interaction tested with More Brushes and a small datapack to confirm correct behaviours)
2025-09-22 21:20:43 +10:00
Polymeta b00f1d9352 fix typo in pokemon interactions
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-22 10:53:31 +01:00
Polymeta a588cfadf8 Merge branch 'riding-scrape' into 'main'
Riding scrape

See merge request cable-mc/cobblemon!1869
2025-09-22 09:29:38 +00:00
Polymeta 2eb4062f67 [skip-ci] re-add client config for consistency
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-22 10:23:14 +01:00
Polymeta 43321d8872 licenses
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-09-22 10:07:19 +01:00
Hiroku a5ea584b75 Merge branch 'held-item-datapack' into 'main'
Implemented user-driven held item effects

See merge request cable-mc/cobblemon!1811
2025-09-22 08:10:13 +00:00
CraterandHiroku 6a9edc2e67 Implemented user-driven held item effects 2025-09-22 08:10:13 +00:00
Test 12377deff7 Merge branch 'dynlight-refactor' into 'main'
Number pushing and Dynlight betterments

See merge request cable-mc/cobblemon!1864
2025-09-22 07:47:42 +00:00
Test 583377d39b Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into stat 2025-09-22 01:31:33 -04:00
Test 2c1da8df06 license 2025-09-22 01:31:21 -04:00
Paul 737ddd973b Tiny cleanup stuff dw about it [skip-ci] 2025-09-22 15:16:25 +10:00
Test d749b04261 fix cyclizar mount idle 2025-09-22 05:14:50 +00:00
Test 93deb3a669 fix stat bar 2025-09-22 00:33:30 -04:00
Test dca45a95e9 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into stat
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/RidingBehaviourSettings.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/air/GliderBehaviour.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/black_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/blue_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/green_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/pink_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/red_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/white_aprijuice.json
#	common/src/main/resources/data/cobblemon/recipe/campfire_pot/yellow_aprijuice.json
2025-09-21 21:42:19 -04:00
Test 0412f16460 ride stat 2025-09-21 20:20:45 -04:00
Landon Wills a62e5a6571 Revert Golurk changes 2025-09-21 18:54:27 -04:00
Landon Wills 0ec337ee6c Scrape riding updates 2025-09-21 18:20:32 -04:00
Carmendar 76df0a429d changelog 2025-09-21 16:58:50 -05:00
Carmendar 08ea9378de Dewgong ready to scrape. 2025-09-21 16:57:07 -05:00
Carmendar c2bd21ab96 Merge branch 'main' into implement_pokemon 2025-09-21 11:42:32 -05:00
Paul 5db8ccfad4 Fixed various things about aprijuice ride boosts 2025-09-21 22:39:55 +10:00
Polymeta aa206d386a Merge branch 'stop-the-disco-spin' into 'main'
Fixed Pokémon spinning a bunch when re-calculating a path to a moving target, closes #1703

Closes #1703

See merge request cable-mc/cobblemon!1868
2025-09-21 10:11:11 +00:00
Paul a235af6c01 Fixed fliers taking fall damage upon landing without a passenger, fixed rarely flying pokemon joining a herd and launching into space 2025-09-21 19:24:23 +10:00
Carmendar a666a74a32 Slaking jump seat fix. 2025-09-21 03:22:51 -05:00
Carmendar c285120ff4 Merge branch 'main' into implement_pokemon 2025-09-21 03:17:33 -05:00
Paul dbd2890120 oop 2025-09-21 18:09:17 +10:00
Carmendar a5db46cdc5 Rhyhorn line ready for scrape & ride overlays for Slaking. 2025-09-21 01:17:58 -05:00
Carmendar 765cf9b96a Send in the bulls. (Tauros is ready to scrape.) 2025-09-21 01:12:38 -05:00
Paul efe7952869 Fixed Pokémon spinning a bunch when re-calculating a path to a moving target, closes #1703 2025-09-21 14:29:30 +10:00
Carmendar 9e434de285 Implemented spicy chicken legs animations. 2025-09-20 22:11:22 -05:00
Carmendar 712fe2c250 Merge branch 'main' into implement_pokemon 2025-09-20 22:09:16 -05:00
Hiroku 5db5a2ed35 Merge branch 'root-bone-animations-in-riding' into 'main'
Root bone animations in riding

See merge request cable-mc/cobblemon!1866
2025-09-21 02:15:02 +00:00
Paul 49c96e6fc8 Merge remote-tracking branch 'origin/main' 2025-09-21 10:46:21 +10:00
Paul d56c9a3f5a put da aprijuice recipes back 2025-09-21 10:42:53 +10:00
JazzMcNade 6c4c973ed8 Fixed large mobs unable to climb blocks. Enabled wandering for seafloor, water surface, and lava surface walkers. 2025-09-20 18:10:55 -05:00
Carmendar a117303147 Merge branch 'main' into implement_pokemon 2025-09-20 17:50:10 -05:00
Landon dbf248a508 Merge branch 'riding-behaviour-tweaks-03' into 'main'
ride tweaks extended cut

See merge request cable-mc/cobblemon!1865
2025-09-20 17:40:28 +00:00
Jackson BowesandLandon 56d75b14fa ride tweaks extended cut 2025-09-20 17:40:27 +00:00
Paul 87311e2d7d Added default ride pose and use that instead of letting minecraft animate them 2025-09-20 22:03:44 +10:00
Paul bd63fb686d Support marking some seats as making the hands busy 2025-09-20 17:00:35 +10:00
Carmendar 0f9a422d44 no more garchomp quirks while mount flying. 2025-09-20 00:52:41 -05:00
Paul 32d18b47f9 Positional scaling fix for root-based ride player animation 2025-09-20 15:46:11 +10:00
Paul c618bf95fa Merge remote-tracking branch 'origin/main' into root-bone-animations-in-riding
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/client/HumanoidModelMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/player/MountedPlayerRenderer.kt
#	common/src/main/resources/mixins.cobblemon-common.json
2025-09-20 14:46:37 +10:00
Nadja Reitzenstein a7bd70e334 Make only held Pokedexes cast light 2025-09-19 13:10:12 +02:00
GenotypeXD-Dev d12bdc3745 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2025-09-18 21:53:03 -05:00
GenotypeXD-Dev 08c8623951 Flygon Size Fix 2025-09-18 21:38:51 -05:00
Nadja Reitzenstein d2ff7e3748 Make Charmander glow 2025-09-18 17:48:30 +02:00
Nadja Reitzenstein ea6452287b Refactor dynamic light support to be future-proof 2025-09-18 17:48:28 +02:00
Nadja Reitzenstein b1f7731563 Update dependencies all around 2025-09-18 17:47:39 +02:00
Paul be5ee8a6a3 Potentially got root bone animation working for players on pokes 2025-05-16 22:22:05 +10:00
Sterrezo 21e5ac6b96 Removed licenses from particle texture folders 2025-04-28 17:21:37 +02:00
Sterrezo d67eca46e6 Merge branch 'main' into sterr/bugfixes 2025-04-28 17:19:11 +02:00
Sterrezo ebbd92fb7a Even less redundancy 2024-12-06 19:15:37 +01:00
Sterrezo 60c4423ab7 Fixed the overlap but without redundancy this time 2024-12-06 18:14:40 +01:00
Sterrezo 82dbbc7f9f Accidental import 2024-12-06 14:49:50 +01:00
Sterrezo 534307df87 Fixed the overlap but efficiently this time 2024-12-06 14:47:57 +01:00
Sterrezo 6c4738fe82 Fixed the overlap but properly this time 2024-12-05 13:36:05 +01:00
Sterrezo 6dcddb612e Adjusted portrait space detection in the Pokedex to prevent overlap
Closes #1055
2024-12-04 17:14:45 +01:00
367 changed files with 43741 additions and 5135 deletions
+3
View File
@@ -15,6 +15,9 @@
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
### Fixes
- Fixed Pokémon with a moving walk target frequently spinning in place as they re-calculate their path.
### MoLang & Datapacks
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
+24
View File
@@ -283,6 +283,15 @@
- Revavroom
- Bouffalant
- Lickilicky
- Blaziken
- Rhyhorn
- Rhydon
- Rhyperior
- Tauros
- Tauros - Paldea Aqua
- Tauros - Paldea Blaze
- Tauros - Paldea Combat
- Dewgong
### Model updates for the following Pokémon
- Cleffa
@@ -456,6 +465,7 @@
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
- Roseli Berry Trees now naturally generate in their preferred biomes.
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -669,6 +679,20 @@
- Added `q.pokemon.is_busy` Returns whether the Pokémon is busy.
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
- Fixed `background` field and added `textColor` field for dialogues.
- Added support to reload some data registries:
- MoLang scripts
- Callbacks
- Spawn Detail Presets
- Spawn Pools
- Spawn Rules
- Cosmetic Items
- Dialogues
- Fossils
- Natural Materials
- Action Effects
- Mechanics
- Unlockable Wallpapers
- Starter Data
### Particles
Added new/updated particles for the following moves:
+7
View File
@@ -31,6 +31,7 @@ repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
maven(url = "https://maven.gegy.dev")
mavenLocal()
}
@@ -44,6 +45,12 @@ dependencies {
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
isTransitive = false
}
// LambDynamicLights is handled differently because we need the Mojang-mappings version of it.
modCompileOnly(libs.lambDynamicLights) {
capabilities {
requireCapability("dev.lambdaurora.lambdynamiclights:api-mojmap")
}
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
@@ -1,102 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.compat;
import com.cobblemon.mod.common.CobblemonEntities;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import dev.lambdaurora.lambdynlights.LambDynLights;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import net.minecraft.world.entity.player.Player;
// 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(
LambDynamicLightsCompat::resolvedPokemonLightLevel,
pokemon -> false
)
);
// Shouldered Pokémon
DynamicLightHandlers.registerDynamicLightHandler(
EntityType.PLAYER,
DynamicLightHandler.makeHandler(
LambDynamicLightsCompat::resolvedPlayerLightLevel,
player -> false
)
);
}
private static int resolvedPokemonLightLevel(PokemonEntity pokemon) {
var underwater = !pokemon.level().getFluidState(BlockPos.containing(pokemon.getX(), pokemon.getEyeY(), pokemon.getZ())).isEmpty();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int resolvedPlayerLightLevel(Player player) {
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
final int leftShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
final int rightShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
final int itemLightLevel = customHeldItemLightLevel(player.getMainHandItem(), player.getOffhandItem());
return Math.max(itemLightLevel, Math.max(leftShoulderLightLevel, rightShoulderLightLevel));
}
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 int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 :LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int customHeldItemLightLevel(ItemStack mainhand, ItemStack offhand) {
return (mainhand.getItem() instanceof PokedexItem || offhand.getItem() instanceof PokedexItem) ? 13 : 0;
}
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -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.compat.lambdynamiclights;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public abstract class CustomLuminance {
protected 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());
}
protected static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -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.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import dev.lambdaurora.lambdynlights.api.DynamicLightsContext;
import dev.lambdaurora.lambdynlights.api.DynamicLightsInitializer;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
public static final EntityLuminance.Type POKEMON_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
PokemonLuminance.INSTANCE
);
public static final EntityLuminance.Type PLAYER_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
PlayerLuminance.INSTANCE
);
@Override
public void onInitializeDynamicLights(DynamicLightsContext context) {
Cobblemon.LOGGER.info("Lamb Dynamic Lights compatibility enabled");
context.entityLightSourceManager().onRegisterEvent().register(ctx -> {
ctx.register(EntityType.PLAYER, PlayerLuminance.INSTANCE);
});
}
@SuppressWarnings({"removal", "UnstableApiUsage"})
@Override
public void onInitializeDynamicLights(ItemLightSourceManager itemLightSourceManager) {}
}
@@ -0,0 +1,72 @@
/*
* 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.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
public final class PlayerLuminance extends CustomLuminance implements EntityLuminance {
public static final PlayerLuminance INSTANCE = new PlayerLuminance();
private static final TagKey<Item> POKEDEX = TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokedex"));
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.PLAYER_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Entity entity) {
if (entity instanceof Player player) {
final int shoulderLightLevel = shoulderLuminance(itemLightSourceManager, player);
final int itemLightLevel = player.isHolding(stack -> stack.is(POKEDEX)) ? 13 : 0;
return Math.max(shoulderLightLevel, itemLightLevel);
} else {
return 0;
}
}
private @Range(from = 0L, to = 15L) static int shoulderLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Player player) {
final var leftShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft());
final var rightShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight());
if (leftShoulder == null && rightShoulder == null) {
return 0;
}
final int leftShoulderLightLevel = extractShoulderLightLevel(leftShoulder, itemLightSourceManager, player.isUnderWater());
final int rightShoulderLightLevel = extractShoulderLightLevel(rightShoulder, itemLightSourceManager, player.isUnderWater());
return Math.max(leftShoulderLightLevel, rightShoulderLightLevel);
}
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, @NotNull ItemLightSourceManager itemLightSourceManager, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
}
@@ -0,0 +1,45 @@
/*
* 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.lambdynamiclights;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
public final class PokemonLuminance extends CustomLuminance implements EntityLuminance {
public static final PokemonLuminance INSTANCE = new PokemonLuminance();
private PokemonLuminance() {}
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.POKEMON_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(
@NotNull ItemLightSourceManager itemLightSourceManager,
@NotNull Entity entity
) {
if (entity instanceof PokemonEntity pokemon) {
boolean underwater = pokemon.isUnderWater();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
} else {
return 0;
}
}
}
@@ -31,14 +31,15 @@ public class AbstractClientPlayerMixin {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
cir.setReturnValue(usageContext.getFovMultiplier());
}
} else {
// Only modify fov through riding if the pokedex is not open
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
//Return custom fov mult
cir.setReturnValue(ride.rideFovMult());
// Return custom fov mult for riding
cir.setReturnValue(ride.rideFovMult());
}
}
}
}
@@ -0,0 +1,46 @@
/*
* 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.client;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.model.AgeableListModel;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.Model;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* This mixin adds to the head of the renderToBuffer function and allows us to detect if it is a HumanoidModel and
* is being rendered as part of a player in the world who is riding a PokemonEntity. If so we need to obey any
* root animations that have been applied earlier.
*
* @author Hiroku
* @since May 16, 2025
*/
@Mixin(AgeableListModel.class)
public class AgeableListModelMixin {
@Inject(method = "renderToBuffer", at = @At("HEAD"))
private void cobblemon$renderToBuffer(
PoseStack poseStack,
VertexConsumer vertexConsumer,
int packedLight,
int packedOverlay,
int colour,
CallbackInfo ci
) {
Model model = (Model) (Object) this;
if (model instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
MountedPlayerRenderer.INSTANCE.animateRoot(poseStack);
}
}
}
@@ -19,20 +19,17 @@ import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
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.HashMap;
@Mixin(HumanoidModel.class)
public class HumanoidModelMixin {
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
@Shadow @Final
public ModelPart head;
@Shadow @Final
public ModelPart hat;
@Shadow @Final
public ModelPart rightArm;
@Shadow @Final
public ModelPart leftArm;
@@ -40,6 +37,8 @@ public class HumanoidModelMixin {
public ModelPart rightLeg;
@Shadow @Final
public ModelPart leftLeg;
@Shadow @Final
public ModelPart body;
@Inject(method = "setupAnim*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V", shift = At.Shift.BEFORE))
public void cobblemon$animateRiders(
@@ -51,19 +50,23 @@ public class HumanoidModelMixin {
float headPitch,
CallbackInfo ci
) {
if (relevantPartsByName.isEmpty()) {
relevantPartsByName.put("arm_left", leftArm);
relevantPartsByName.put("arm_right", rightArm);
relevantPartsByName.put("leg_left", leftLeg);
relevantPartsByName.put("leg_right", rightLeg);
relevantPartsByName.put("head", head);
MountedPlayerRenderer.shouldApplyRootAnimation = false;
if (MountedPlayerRenderer.relevantPartsByName.size() <= 1) {
MountedPlayerRenderer.relevantPartsByName.put("arm_left", leftArm);
MountedPlayerRenderer.relevantPartsByName.put("arm_right", rightArm);
MountedPlayerRenderer.relevantPartsByName.put("leg_left", leftLeg);
MountedPlayerRenderer.relevantPartsByName.put("leg_right", rightLeg);
MountedPlayerRenderer.relevantPartsByName.put("head", head);
MountedPlayerRenderer.relevantPartsByName.put("hat", hat);
MountedPlayerRenderer.relevantPartsByName.put("torso", body);
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
return behaviour.shouldRotateRiderHead(settings, state, pokemonEntity);
});
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
);
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
@@ -76,7 +79,8 @@ public class HumanoidModelMixin {
if (!(entity instanceof AbstractClientPlayer player)) return;
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, player, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
MountedPlayerRenderer.shouldApplyRootAnimation = true;
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -174,6 +174,16 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
return this.cobblemon$isDoubleJumping;
}
@Inject(method = "isHandsBusy", at = @At("HEAD"), cancellable = true)
private void cobblemon$isHandsBusy(CallbackInfoReturnable<Boolean> cir) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
cir.setReturnValue(seat.getHandsBusy());
}
}
@Override
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
@@ -117,6 +117,8 @@ public abstract class MouseHandlerMixin {
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
boolean returnValue = true;
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -127,7 +129,7 @@ public abstract class MouseHandlerMixin {
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
return false;
returnValue = false;
}
// Clamp player rotation if riding and the vehicle demands it
@@ -143,35 +145,33 @@ public abstract class MouseHandlerMixin {
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return true;
return returnValue;
}
//Send mouse input to be interpreted into rotation
//deltas by the ride controller
// Send mouse input to be interpreted into rotation
// deltas by the ride controller
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
//Perform Rotation using mouse influenced rotation deltas.
// Perform Rotation using mouse influenced rotation deltas.
controllable.getOrientationController().rotate(
(float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z
);
//Gather and apply the current rotation deltas
// Gather and apply the current rotation deltas
var angRot = cobblemon$getAngularVelocity(movementTime);
//Apply smoothing if requested by the controller.
//This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing())
{
// Apply smoothing if requested by the controller.
// This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing()) {
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
}
//Otherwise simply apply the smoothing
else
{
// Otherwise simply apply the smoothing
else {
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
}
return false;
@@ -26,10 +26,10 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(PokemonItemComponent.PACKET_CODEC)
.build())
val HELD_ITEM_REP: DataComponentType<HeldItemCapableComponent> = DataComponentType.builder<HeldItemCapableComponent>()
.persistent(HeldItemCapableComponent.CODEC)
.networkSynchronized(HeldItemCapableComponent.PACKET_CODEC)
.build()
val HELD_ITEM_EFFECT: DataComponentType<HeldItemEffectComponent> = create("held_item_effect", DataComponentType.builder<HeldItemEffectComponent>()
.persistent(HeldItemEffectComponent.CODEC)
.networkSynchronized(HeldItemEffectComponent.PACKET_CODEC)
.build())
val BAIT: DataComponentType<RodBaitComponent> = create("bait", DataComponentType.builder<RodBaitComponent>()
.persistent(RodBaitComponent.CODEC)
@@ -86,6 +86,7 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.ai.CobblemonAttackTargetData
import com.cobblemon.mod.common.api.ai.CobblemonAvoidTargetData
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
@@ -58,7 +57,6 @@ object CobblemonMemories {
/** how many are following me rn? */
val HERD_SIZE = register<Int>("herd_size") // Don't bother saving it, we'll try to keep count roughly.
val ATTACK_TARGET_DATA = register<CobblemonAttackTargetData>("attack_target_data") // This is used to store additional information about the attack target, such as when to give up pursuit.
val AVOID_TARGET_DATA = register<CobblemonAvoidTargetData>("avoid_target_data") // This is used to store additional information about the avoid target, such as how fast to move.
val WANDER_CONTROL = register<CobblemonWanderControl>("wander_control")
val DISABLE_WALK_TO_WANTED_ITEM = register<Boolean>("disable_walk_to_wanted_item")
val TIME_TRYING_TO_REACH_WANTED_ITEM = register<Int>("time_trying_to_wanted_item")
@@ -9,8 +9,8 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
@@ -20,7 +20,6 @@ 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.cooking.ToggleCookingPotLidHandler
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.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
@@ -65,8 +64,8 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
@@ -171,11 +170,11 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
@@ -224,11 +223,11 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
@@ -390,7 +389,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler))
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -20,8 +20,6 @@ import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.resolve
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.memory.MemoryModuleType
/**
* This is used in the AI system to store optional additional information about the attack target,
@@ -43,21 +41,15 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
class CobblemonAttackTargetData(
/** An expression that returns false when the target is no longer worth attacking. */
var shouldContinue: Expression = "true".asExpression(),
var dealtHitsUntilDisengage: Int = -1, // -1 means no limit
var disengageAction: (entity: LivingEntity) -> Unit = ::defaultDisengage
var hitsUntilDisengage: Int = -1, // -1 means no limit
var disengageAction: (entity: Entity) -> Unit = {}
) {
companion object {
fun defaultDisengage(entity: LivingEntity) {
entity.brain.eraseMemory(MemoryModuleType.ATTACK_TARGET)
entity.brain.updateActivityFromSchedule(entity.level().dayTime, entity.level().gameTime)
}
}
val struct = ObjectValue<CobblemonAttackTargetData>(this).also {
it.addFunction("dealt_hits_until_disengage") { DoubleValue(dealtHitsUntilDisengage.toDouble()) }
it.addFunction("set_dealt_hits_until_disengage") { params ->
it.addFunction("hits_until_disengage") { DoubleValue(hitsUntilDisengage.toDouble()) }
it.addFunction("set_hits_until_disengage") { params ->
val value = params.getDouble(0)
dealtHitsUntilDisengage = value.toInt()
DoubleValue(dealtHitsUntilDisengage.toDouble())
hitsUntilDisengage = value.toInt()
DoubleValue(hitsUntilDisengage.toDouble())
}
it.addFunction("set_disengage_action") { params ->
val script = params.getString(0).asIdentifierDefaultingNamespace()
@@ -72,15 +64,8 @@ class CobblemonAttackTargetData(
}
DoubleValue.ONE
}
it.addFunction("reset") {
shouldContinue = "true".asExpression()
dealtHitsUntilDisengage = -1
disengageAction = ::defaultDisengage
DoubleValue.ONE
}
it.addFunction("unset_disengage_action") { params ->
disengageAction = ::defaultDisengage
disengageAction = {}
}
}
}
@@ -1,23 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.util.getDoubleOrNull
class CobblemonAvoidTargetData {
var walkSpeed: Float? = null
@Transient
val struct = ObjectValue<CobblemonAvoidTargetData>(this).also {
it.addFunction("walk_speed") { DoubleValue(walkSpeed) }
it.addFunction("set_walk_speed") { walkSpeed = it.getDoubleOrNull(0)?.toFloat() }
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.util.getBooleanOrNull
import com.cobblemon.mod.common.util.getDoubleOrNull
import kotlin.math.sqrt
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.PathfinderMob
/**
* Used to maintain some settings around how the entity should wander this tick. The idea is you
@@ -29,8 +30,7 @@ class CobblemonWanderControl(
var allowWater: Boolean = false,
var allowAir: Boolean = false,
var pathCooldownTicks: Int = 20 * 5, // Used to mitigate the cost of pathfinding.
var speedMultiplier: Float = 1F,
var wanderChance: Float? = null
var wanderSpeed: Float = 0.35F,
) {
class WanderCenter(val x: Number, val y: Number, val z: Number, val minRange: Float, val maxRange: Float)
@@ -46,7 +46,7 @@ class CobblemonWanderControl(
.addFunction("allow_water") { if (allowWater) 1.0 else 0.0 }
.addFunction("allow_air") { if (allowAir) 1.0 else 0.0 }
.addFunction("path_cooldown_ticks") { pathCooldownTicks.toDouble() }
.addFunction("speed_multiplier") { speedMultiplier }
.addFunction("wander_speed") { wanderSpeed }
.addFunction("set_center") { params ->
if (params.params.size < 3) {
throw IllegalArgumentException("set_center requires at least 3 parameters: x, y, z")
@@ -69,19 +69,15 @@ class CobblemonWanderControl(
.addFunction("set_allow_water") { params -> allowWater = params.getBooleanOrNull(0) ?: false }
.addFunction("set_allow_air") { params -> allowAir = params.getBooleanOrNull(0) ?: false }
.addFunction("set_path_cooldown_ticks") { params -> pathCooldownTicks = params.getDoubleOrNull(0)?.toInt() ?: (20 * 5) }
.addFunction("set_speed_multiplier") { params -> speedMultiplier = params.getDoubleOrNull(0)?.toFloat() ?: 0.35F }
.addFunction("set_wander_chance") { params ->
wanderChance = params.getDoubleOrNull(0)?.toFloat()
}
.addFunction("set_wander_speed") { params -> wanderSpeed = params.getDoubleOrNull(0)?.toFloat() ?: 0.35F }
.addFunction("reset") {
center = null
allowLand = false
allowWater = false
allowAir = false
pathCooldownTicks = 20 * 5
speedMultiplier = 1F
wanderSpeed = 0.35F
maxAttempts = 4
wanderChance = null
}
fun isSuitable(position: BlockPos): Boolean {
@@ -53,7 +53,7 @@ class FindAirTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.getIsSubmerged()) {
if (!entity.getIsSubmerged() || entity.isInLava) {
return@Trigger false
}
@@ -57,7 +57,7 @@ class GoToLandTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.isInLiquid) {
if (!entity.isInWater) {
return@Trigger false
}
@@ -1,87 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.EntityCallbacks
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.Behavior
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.ai.sensing.SensorType
/**
* Frequently checks a diff on the visibility of entities around the given entity so EntityCallbacks can be triggered.
*
* @author Hiroku
* @since August 7th, 2025
*/
class NotifyAboutEntityVisibilityTaskConfig : SingleTaskConfig {
val ticksBetweenChecks: ExpressionOrEntityVariable = Either.left("20".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(ticksBetweenChecks).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is MoLangScriptingEntity) {
return null
}
val ticksBetweenChecks = ticksBetweenChecks.resolveInt(behaviourConfigurationContext.runtime)
behaviourConfigurationContext.addMemories(MemoryModuleType.NEAREST_LIVING_ENTITIES)
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
return object : Behavior<LivingEntity>(mapOf(MemoryModuleType.NEAREST_LIVING_ENTITIES to MemoryStatus.VALUE_PRESENT), Int.MAX_VALUE) {
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long) = true
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity) = true
// Buffer of known entities
val knownEntities = mutableListOf<LivingEntity>()
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
if (gameTime % ticksBetweenChecks != 0L) {
return
}
val visibleEntities = owner.getBrain().getMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES)
.orElse(emptyList<LivingEntity>())
.filter { it.isAlive }
val newEntities = visibleEntities.filterNot { it in knownEntities }
val removedEntities = knownEntities.filterNot { it in visibleEntities }
newEntities.forEach {
entity.callbacks.process(
type = EntityCallbacks.SAW_ENTITY,
functions = mapOf("other_entity" to { _ -> it.asMostSpecificMoLangValue() })
)
}
removedEntities.forEach {
entity.callbacks.process(
type = EntityCallbacks.LOST_ENTITY,
functions = mapOf("other_entity" to { _ -> it.asMostSpecificMoLangValue() })
)
}
knownEntities.clear()
knownEntities.addAll(visibleEntities)
}
}
}
}
@@ -120,7 +120,6 @@ interface TaskConfig {
cobblemonResource("target_entity") to TargetEntityTaskConfig::class.java,
cobblemonResource("memory_aspect") to MemoryAspectTaskConfig::class.java,
cobblemonResource("activity_change") to ActivityChangeTaskConfig::class.java,
cobblemonResource("notify_about_entity_visibility") to NotifyAboutEntityVisibilityTaskConfig::class.java,
)
}
@@ -14,6 +14,8 @@ import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.OmniPathingEntity
import com.cobblemon.mod.common.entity.ai.CobblemonRandomSurfacePos
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
@@ -26,6 +28,7 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BehaviorUtils
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
@@ -41,24 +44,27 @@ class WanderTaskConfig : SingleTaskConfig {
}
val condition = booleanVariable(WANDER, "wanders", true).asExpressible()
val wanderChance = numberVariable(WANDER, "wander_chance", 1/(20 * 6F)).asExpressible()
val wanderChance = numberVariable(WANDER, "wander_chance", 1 / (20 * 6F)).asExpressible()
val horizontalRange = numberVariable(WANDER, "horizontal_wander_range", 10).asExpressible()
val verticalRange = numberVariable(WANDER, "vertical_wander_range", 5).asExpressible()
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val avoidTargetingAir: ExpressionOrEntityVariable = Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
val speedMultiplier =
numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val avoidTargetingAir: ExpressionOrEntityVariable =
Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
val minimumHeight: ExpressionOrEntityVariable = Either.left("0".asExpression()) // Height off the ground
val maximumHeight: ExpressionOrEntityVariable = Either.left("-1".asExpression()) // Height off the ground
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
wanderChance,
horizontalRange,
verticalRange,
speedMultiplier,
avoidTargetingAir,
minimumHeight,
maximumHeight
).asVariables()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) =
listOf(
condition,
wanderChance,
horizontalRange,
verticalRange,
speedMultiplier,
avoidTargetingAir,
minimumHeight,
maximumHeight
).asVariables()
private fun applyHeightConstraints(
pos: BlockPos,
@@ -77,7 +83,7 @@ class WanderTaskConfig : SingleTaskConfig {
(1..MAX_LOOK_DOWN_DISTANCE).firstOrNull {
val newPos = pos.below(it)
if (!world.getBlockState(newPos).isAir) {
return@firstOrNull true
return@firstOrNull true
} else if (newPos.y <= world.minBuildHeight) {
// Don't adjust downward into the void (Mostly if we're in the end)
// This doesn't stop fliers from wandering off end islands,
@@ -126,18 +132,17 @@ class WanderTaskConfig : SingleTaskConfig {
it.registered(CobblemonMemories.WANDER_CONTROL)
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControl ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || entity.isUnderWater) {
if (entity !is PathfinderMob || (entity.isUnderWater && !(entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())))) {
return@Trigger false
}
val wanderControl = it.tryGet(wanderControl).orElse(null) ?: CobblemonWanderControl()
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean(mainThreadRuntime)
val wanderChance = wanderControl.wanderChance ?: mainThreadRuntime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
return@Trigger false
}
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
val minimumHeight = minimumHeight.resolveInt(mainThreadRuntime)
val maximumHeight = maximumHeight.resolveInt(mainThreadRuntime)
@@ -157,14 +162,28 @@ class WanderTaskConfig : SingleTaskConfig {
maximumHeight,
minimumHeight
)
} else if (entity.isUnderWater && entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())) {
BehaviorUtils.getRandomSwimmablePos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} else if (entity is OmniPathingEntity && (entity.canWalkOnWater() || entity.canWalkOnLava())) {
CobblemonRandomSurfacePos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} else {
LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} ?: continue
if (targetVec == null) continue
pos = applyHeightConstraints(
pos = BlockPos.containing(targetVec),
minimumHeight = minimumHeight,
@@ -180,10 +199,19 @@ class WanderTaskConfig : SingleTaskConfig {
walkTarget.set(
CobblemonWalkTarget(
pos = pos,
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime) * wanderControl.speedMultiplier,
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
completionRange = 0,
nodeTypeFilter = { nodeType -> nodeType !in listOf(PathType.WATER, PathType.WATER_BORDER) },
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
nodeTypeFilter = { nodeType ->
entity.canBreatheUnderwater() || nodeType !in listOf(
PathType.WATER,
PathType.WATER_BORDER
)
},
destinationNodeTypeFilter = { nodeType ->
!avoidsTargetingAir || nodeType !in listOf(
PathType.OPEN
)
}
)
)
lookTarget.set(BlockPosTracker(pos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
@@ -78,7 +78,7 @@ class WaterWanderTaskConfig : SingleTaskConfig {
}
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val wanderChance = wanderControl.wanderChance ?: mainThreadRuntime.resolveFloat(wanderChanceExpression)
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
return@Trigger false
}
@@ -104,7 +104,7 @@ class WaterWanderTaskConfig : SingleTaskConfig {
CobblemonWalkTarget(
pos = pos,
nodeTypeFilter = { it == PathType.WATER || it == PathType.WATER_BORDER },
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime) * wanderControl.speedMultiplier,
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
completionRange = 0
)
)
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.cooking
import net.minecraft.ChatFormatting
import net.minecraft.util.StringRepresentable
/**
@@ -18,16 +17,16 @@ import net.minecraft.util.StringRepresentable
* @author Licious
* @since November 28th, 2022
*/
enum class Flavour(val colour: Int, val chatFormatting: ChatFormatting): StringRepresentable {
SPICY(0x00FF00, ChatFormatting.RED),
DRY(0xFF0000, ChatFormatting.DARK_AQUA),
SWEET(0x0000FF, ChatFormatting.LIGHT_PURPLE),
BITTER(0xFFFF00, ChatFormatting.GREEN),
SOUR(0xFF00FF, ChatFormatting.YELLOW),
MILD(0xFF00FF, ChatFormatting.DARK_PURPLE);
enum class Flavour(val colour: Int): StringRepresentable {
SPICY(0xFA795E),
DRY(0x65AFF3),
SWEET(0xFF82CF),
BITTER(0x77C96A),
SOUR(0xF3D465),
MILD(0x9D99BF);
override fun getSerializedName() = name
companion object {
val CODEC = StringRepresentable.fromEnum<Flavour> { Flavour.entries.toTypedArray() }
}
}
}
@@ -18,14 +18,13 @@ import net.minecraft.resources.ResourceLocation
* @since August 5th, 2022
*/
interface DataProvider {
/**
* Registers a [DataRegistry] to listen for updates.
* The updates will automatically happen on the correct sides based on [DataRegistry.type].
*
* @param registry The [DataRegistry] being registered.
*/
fun <T : DataRegistry> register(registry: T): T
fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T
/**
* Attempts to find a [DataRegistry] with the given [ResourceLocation].
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.item
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import java.io.File
object HeldItems : DataRegistry {
override val id = cobblemonResource("held_items")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<HeldItems>()
override fun sync(player: ServerPlayer) {}
// val heldItems = PrioritizedList<BagItemLike>()
internal val heldItemsScripts = mutableMapOf<String, String>() // showdown item ID to JavaScript
val showdownItems = hashSetOf<String>()
override fun reload(manager: ResourceManager) {
heldItemsScripts.clear()
ShowdownService.service.resetRegistryData("heldItem")
manager.listResources("held_items") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
val js = reader.readText()
heldItemsScripts[resolvedIdentifier.path] = js
}
}
}
ShowdownService.service.sendRegistryData(heldItemsScripts, "heldItem")
val itemsJson = ShowdownService.service.getRegistryData("heldItem")
for (i in 0 until itemsJson.size()) {
val jsItem = itemsJson[i].asJsonObject
showdownItems += jsItem.get("id").asString
}
CobblemonHeldItemManager.load()
this.observable.emit(this)
}
}
@@ -12,6 +12,7 @@ import com.bedrockk.molang.runtime.MoLangEnvironment
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.struct.ArrayStruct
import com.bedrockk.molang.runtime.struct.MoStruct
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
@@ -24,8 +25,6 @@ import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
import com.cobblemon.mod.common.Environment
import com.cobblemon.mod.common.api.ai.CobblemonBlockPosTracker
import com.cobblemon.mod.common.api.ai.CobblemonAttackTargetData
import com.cobblemon.mod.common.api.ai.CobblemonAvoidTargetData
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
@@ -349,7 +348,10 @@ object MoLangFunctions {
val b = format.parse(dateB)
DoubleValue(a.after(b))
},
"create_simple_party_provider" to java.util.function.Function { SimplePartyProvider().struct },
"create_simple_party_provider" to java.util.function.Function { params ->
val partyProvider = SimplePartyProvider()
return@Function partyProvider.struct
},
"create_pickup_item" to java.util.function.Function { params ->
val item = params.getOrNull<MoValue>(0)?.asString()?.let(ObtainableItemCondition::parseFromString)
val pickupPriority = params.getIntOrNull(1) ?: 0
@@ -362,7 +364,6 @@ object MoLangFunctions {
{ worldHolder ->
val world = worldHolder.value()
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("id") { StringValue(world.dimension().location().toString()) }
map.put("is_time_of_day") { params ->
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
?: return@put DoubleValue.ZERO
@@ -806,13 +807,6 @@ object MoLangFunctions {
val source = DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolder(DamageTypes.GENERIC).get())
entity.hurt(source, amount.toFloat())
}
map.put("damage_source") { params ->
val registry = entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE)
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()?.let(registry::getHolder)
?: registry.getHolder(DamageTypes.GENERIC)
val damageSource = DamageSource(type.get(), entity)
return@put damageSource.asMoLangValue()
}
map.put("distance_to_owner") {
if (entity !is TamableAnimal) {
return@put DoubleValue.ZERO
@@ -936,10 +930,6 @@ object MoLangFunctions {
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger) }
map.put("distance_to_entity") { params ->
val otherEntity = params.get<MoValue>(0) as? ObjectValue<Entity> ?: return@put DoubleValue.ZERO
return@put DoubleValue(entity.distanceTo(otherEntity.obj))
}
map.put("distance_to_pos") { params ->
val x = params.getDouble(0)
val y = params.getDouble(1)
@@ -1073,19 +1063,7 @@ object MoLangFunctions {
}
)
val damageSourceFunctions: MutableList<(DamageSource) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ source ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("direct_entity") { source.directEntity?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO }
map.put("entity") { source.entity?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO }
map.put("is_food_exhaustion") { DoubleValue(source.foodExhaustion) }
map.put("is_direct") { DoubleValue(source.isDirect) }
map.put("type") { StringValue(source.typeHolder().unwrapKey().get().location().toString()) }
return@mutableListOf map
}
)
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_player") { _ -> DoubleValue(entity is Player) }
@@ -1127,23 +1105,6 @@ object MoLangFunctions {
val amount = params.getDouble(0)
entity.heal(amount.toFloat())
}
map.put("reset_activity") { _ ->
entity.brain.updateActivityFromSchedule(entity.level().dayTime, entity.level().gameTime)
DoubleValue.ONE
}
map.put("activity") { _ ->
return@put entity.brain.activeNonCoreActivity.map { StringValue(it.name) }.orElse(null) ?: DoubleValue.ZERO
}
map.put("set_activity") { params ->
val activityId = params.getString(0).asIdentifierDefaultingNamespace()
val activity = BuiltInRegistries.ACTIVITY.get(activityId)
if (activity != null) {
entity.brain.setActiveActivityIfPossible(activity)
return@put DoubleValue.ONE
} else {
return@put DoubleValue.ZERO
}
}
map.put("is_looking_at") { params ->
val targetEntity = params.get<MoValue>(0)
val maxDistance = params.getDoubleOrNull(1)?.toFloat() ?: 2.0F
@@ -1170,26 +1131,6 @@ object MoLangFunctions {
val brain = entity.brain
brain.setMemoryWithExpiry(MemoryModuleType.LOOK_TARGET, CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()), duration.toLong())
}
map.put("is_doing_activity") { params ->
val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") }
val activities = identifiers.mapNotNull(BuiltInRegistries.ACTIVITY::get)
if (activities.isNotEmpty()) {
return@put DoubleValue(activities.any(entity.brain::isActive))
} else {
return@put DoubleValue.ZERO
}
}
map.put("get_attack_target_data_memory") {
val value = entity.brain.getMemorySafely(CobblemonMemories.ATTACK_TARGET_DATA).orElse(null)
?: CobblemonAttackTargetData()
if (
!entity.brain.checkMemory(CobblemonMemories.ATTACK_TARGET_DATA, MemoryStatus.VALUE_PRESENT)
&& entity.brain.checkMemory(CobblemonMemories.ATTACK_TARGET_DATA, MemoryStatus.REGISTERED)
) {
entity.brain.setMemory(CobblemonMemories.ATTACK_TARGET_DATA, value)
}
return@put value.struct
}
map.put("get_wander_control_memory") {
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
?: CobblemonWanderControl()
@@ -1201,17 +1142,6 @@ object MoLangFunctions {
}
return@put value.struct
}
map.put("get_avoid_target_data_memory") {
val value = entity.brain.getMemorySafely(CobblemonMemories.AVOID_TARGET_DATA).orElse(null)
?: CobblemonAvoidTargetData()
if (
!entity.brain.checkMemory(CobblemonMemories.AVOID_TARGET_DATA, MemoryStatus.VALUE_PRESENT)
&& entity.brain.checkMemory(CobblemonMemories.AVOID_TARGET_DATA, MemoryStatus.REGISTERED)
) {
entity.brain.setMemory(CobblemonMemories.AVOID_TARGET_DATA, value)
}
return@put value.struct
}
map.put("get_position_memory") { params ->
val id = params.getString(0).asIdentifierDefaultingNamespace()
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
@@ -1238,30 +1168,6 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("get_entity_memory") { params ->
val id = params.getString(0).asIdentifierDefaultingNamespace()
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
if (entity.brain.checkMemory(memoryType, MemoryStatus.VALUE_PRESENT)) {
val memory = entity.brain.getMemory(memoryType).get()
return@put when (memory) {
is Entity -> memory.asMostSpecificMoLangValue()
else -> DoubleValue.ZERO
}
} else {
return@put DoubleValue.ZERO
}
}
map.put("set_entity_memory") { params ->
val memory = params.getString(0).asIdentifierDefaultingNamespace().let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<LivingEntity>
val entityValue = (params.get<MoValue>(1) as? ObjectValue<*>)?.obj as? LivingEntity ?: return@put DoubleValue.ZERO
val expiry = params.getIntOrNull(2) ?: -1
if (expiry != -1) {
entity.brain.setMemoryWithExpiry(memory, entityValue, expiry.toLong())
} else {
entity.brain.setMemory(memory, entityValue)
}
return@put DoubleValue.ONE
}
map.put("set_uuid_memory") { params ->
val memory = params.getString(0).asIdentifierDefaultingNamespace().let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
val uuid = params.getString(1).asUUID ?: return@put DoubleValue.ZERO
@@ -1308,18 +1214,6 @@ object MoLangFunctions {
return@put DoubleValue.ZERO
}
}
map.put("is_visible_entity") { params ->
val otherEntity = params.get<ObjectValue<*>>(0)?.obj as? LivingEntity ?: return@put DoubleValue.ZERO
val visibleMobs = entity.brain.getMemorySafely(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).orElse(null)
?: return@put DoubleValue.ZERO
return@put DoubleValue(visibleMobs.contains(otherEntity))
}
map.put("is_nearby_entity") { params ->
val otherEntity = params.get<ObjectValue<*>>(0)?.obj as? LivingEntity ?: return@put DoubleValue.ZERO
val nearbyMobs = entity.brain.getMemorySafely(MemoryModuleType.NEAREST_LIVING_ENTITIES).orElse(null)
?: return@put DoubleValue.ZERO
return@put DoubleValue(nearbyMobs.contains(otherEntity))
}
if (entity is PathfinderMob) {
map.put("walk_to") { params ->
val x = params.getDouble(0)
@@ -1358,6 +1252,15 @@ object MoLangFunctions {
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
map.put("battles") { ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }.mapIndexed { index, value -> "$index" to value }.toMap()) }
map.put("stop_battles") { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
map.put("is_doing_activity") { params ->
val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") }
val activities = identifiers.mapNotNull { identifier -> BuiltInRegistries.ACTIVITY.get(identifier) }
if (activities.isNotEmpty()) {
return@put DoubleValue(activities.any { activity -> npc.brain.isActive(activity) })
} else {
return@put DoubleValue.ZERO
}
}
map.put("run_script_on_client") { params ->
val world = npc.level()
if (world is ServerLevel) {
@@ -2568,15 +2471,6 @@ object MoLangFunctions {
return value
}
fun DamageSource.asMoLangValue(): ObjectValue<DamageSource> {
val value = ObjectValue(
obj = this,
stringify = { it.toString() }
)
value.addFunctions(damageSourceFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
return value
}
/**
* Different functions exist depending on the entity type, this tries to make the struct that's most specific to the type.
*/
@@ -15,6 +15,7 @@ import net.minecraft.world.entity.LivingEntity
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.abs
import kotlin.math.asin
import kotlin.math.sign
@@ -113,7 +114,7 @@ open class OrientationController(val entity: LivingEntity) {
fun applyGlobalPitch(deltaPitchDegrees: Float) = updateOrientation { original ->
val currQuat = Quaternionf().setFromUnnormalized(original)
val horzLeftVector = Vector3f(0.0f, this.upVector.y.sign, 0.0f).cross(this.forwardVector)
val horzLeftVector = Vector3f(0.0f, -abs(this.upVector.y.sign), 0.0f).cross(this.forwardVector)
// Avoid NaN issue when normalizing and the forwardVector and upVector are equal
val pitchAxis = if (horzLeftVector.lengthSquared() < 0.01) this.leftVector else horzLeftVector
@@ -53,7 +53,6 @@ import com.cobblemon.mod.common.pokemon.ai.ObtainableItemConditionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.HashBasedTable
@@ -158,8 +157,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
it.resetRegistryData("species")
it.sendRegistryData(allShowdownSpecies(), "species")
it.indicateSpeciesInitialized()
// Reload this with the mod
CobblemonHeldItemManager.load()
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
this.observable.emit(this)
}
@@ -8,9 +8,6 @@
package com.cobblemon.mod.common.api.riding
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import net.minecraft.world.entity.PlayerRideableJumping
/**
@@ -25,7 +25,8 @@ data class Seat(
val locator: String?,
val offset: Vec3?,
val poseOffsets: MutableList<SeatPoseOffset>?,
val poseAnimations: MutableList<SeatPoseAnimations>?
val poseAnimations: MutableList<SeatPoseAnimations>?,
val handsBusy: Boolean = false,
) : Encodable {
fun getOffset(poseType: PoseType) : Vec3 {
return poseOffsets?.firstOrNull { poseType in it.poseTypes }?.offset ?: Vec3.ZERO
@@ -40,6 +41,7 @@ data class Seat(
buffer.writeNullable(poseAnimations) { _, v ->
buffer.writeCollection(v) { _, animations -> animations.encode(buffer) }
}
buffer.writeBoolean(handsBusy)
}
companion object {
@@ -48,7 +50,8 @@ data class Seat(
buffer.readNullable { buffer.readString() },
buffer.readNullable { buffer.readVec3() },
buffer.readNullable { buffer.readList { SeatPoseOffset.decode(buffer) } },
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } }
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } },
buffer.readBoolean()
)
}
}
@@ -10,10 +10,8 @@ package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
/**
* Represents static settings of a riding behaviour.
@@ -27,10 +25,9 @@ interface RidingBehaviourSettings: Encodable, Decodable {
val key: ResourceLocation
val stats: MutableMap<RidingStat, IntRange>
fun calculate(stat: RidingStat, boosts: Int): Float {
fun calculate(stat: RidingStat, boostAmount: Float): Float {
val range = stats[stat] ?: return 0F
val boostQuotient = boosts / 255F
return range.first + (range.last - range.first) * boostQuotient
return range.first + boostAmount
}
fun hasStat(stat: RidingStat) = stats.containsKey(stat)
@@ -49,6 +49,7 @@ class RidingController(
if (newTransition != null && behaviourSettings != null) {
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
context?.let { newState.rideVelocity.set(it.state.rideVelocity.get(), forced = true) }
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
lastTransitionAge = entity.ticksLived
} else {
@@ -21,18 +21,18 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
@@ -50,7 +50,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
return true
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
//Need to check other fluids
if (vehicle.isInWater || vehicle.isUnderWater) {
return@any false
}
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
if (it.y.toDouble() == (vehicle.position().y)) {
val blockState = vehicle.level().getBlockState(it.below())
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
}
true
}
}
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
@@ -88,7 +99,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
if (cos(controller.roll.toRadians()) < 0.0) {
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
} else { // Its right side up so reset it
} else if (state.stamina.get() == 0.0f) { // checks stamina depletion and forces dive if depleted
val upsideDownRate = 1.0 / (2.0 * 20.0) // 1 seconds to max out
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate))
} else { // Its right side up or has stamina so reset it
state.currUpsideDownForce.set(0.0)
}
}
@@ -149,8 +163,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
}
// The downward force used to encourage players to stop flying upside down.
val extraDownwardForce = if(state.stamina.get() == 0.0f) -0.3
else state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
return velocity
@@ -275,18 +288,20 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //* sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))// * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
// Dampen yaw when upside down
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
// Correct orientation when at low speeds
correctOrientation(settings, state, vehicle, controller, deltaTime)
controller.applyGlobalPitch(state.currPitchCorrectionForce.get().toRadians())
//yaw, pitch, roll
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
}
private fun correctOrientation(
@@ -299,10 +314,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect)
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTimeRoll.get() - rollCorrectionTimer)).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
val rollArrivalDeg = 90.0
if(state.rideVelocity.get().length() < 0.1 || state.noInputTime.get() > rollCorrectionTimer) {
if(state.rideVelocity.get().length() < 0.1 || state.noInputTimeRoll.get() > rollCorrectionTimer) {
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
@@ -324,13 +339,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
}
//Calculate correcting pitch force.
val maxPitchCorrectionRate = 15
val maxPitchForce = maxPitchCorrectionRate / 40.0
val pitchArrivalDeg = 90.0
if(state.rideVelocity.get().length() < 0.1) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(controller.pitch).toDouble()) / pitchArrivalDeg)
val desiredPitchForce = (0 - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 8f * state.currUpsideDownForce.get() else 15f
val maxPitchForce = maxPitchCorrectionRate.toFloat() / 40.0f
val pitchArrivalDeg = 45.0f
var desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0 else 0.0
// if (cos(controller.roll.toRadians()) < 0.0) {
// desiredPitch *= -1 // If not rolled over then flip the pitch force. There are better ways to do this :(
// }
if((state.rideVelocity.get().length() < 0.1 && controller.pitch < 0.0) || state.currUpsideDownForce.get() != 0.0) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(Mth.wrapDegrees(desiredPitch.toFloat().angleDifference(controller.pitch)))) / pitchArrivalDeg)
val desiredPitchForce = maxPitchCorrectionRate.toFloat() * arrivalInfluence
var steeredPitchForce = (desiredPitchForce - state.currPitchCorrectionForce.get())
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredPitchForce) > maxPitchForce) {
@@ -361,12 +381,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
// Begin the roll correction time counter if not enough mouse input
// is detected
if (abs(mouseX) < 1 && abs(mouseY) < 1) {
state.noInputTime.set(state.noInputTime.get() + deltaTime)
if (mouseX < 1 && abs(mouseY) < 1) {
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime)
} else if (cos(controller.roll.toRadians()) < 0.1) {
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime * 0.5)
}
else {
state.noInputTimeRoll.set(0.0)
}
if (abs(mouseY) < 1) {
state.noInputTimePitch.set(state.noInputTimePitch.get() + deltaTime)
} else {
state.noInputTime.set(0.0)
state.noInputTimePitch.set(0.0)
}
return when {
@@ -464,7 +495,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val smoothingSpeed = 2.0
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
// of being upside down
@@ -476,10 +507,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
val hoverSpeed = 0.1
// Yaw locally when at or below hoverSpeed
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
val localYaw = if (currHorzSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
else 0.0 //xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
// Pitch locally up or down depending upon a number of factors:
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
@@ -487,14 +518,17 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// - pitch faster at higher speeds
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr)
var localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
localPitch *= if (currHorzSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)
localPitch *= if (currSpeed > hoverSpeed || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
else 0.0
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val pitchInfluencedRollCorrection = 0.0
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) + pitchInfluencedRollCorrection
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2).coerceIn(0.0f, 0.7f) //Coerce into how much roll you want to let in in the deadzone
val inputInRolledDir = if (xInput.sign.toFloat() == controller.roll.sign) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
val rollDampen = (1 - howRolledAmI * abs(cos(controller.pitch.toRadians())).pow(2) * inputInRolledDir)
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
else 0.0
//yaw, pitch, roll
@@ -693,7 +727,8 @@ class BirdState : RidingBehaviourState() {
var lastGlide = ridingState(-100L, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
var noInputTime = ridingState(0.0, Side.CLIENT)
var noInputTimeRoll = ridingState(0.0, Side.CLIENT)
var noInputTimePitch = ridingState(0.0, Side.CLIENT)
var currUpsideDownForce = ridingState(0.0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
@@ -712,7 +747,8 @@ class BirdState : RidingBehaviourState() {
lastGlide.set(-100L, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
currPitchCorrectionForce.set(0.0, forced = true)
noInputTime.set(0.0, forced = true)
noInputTimeRoll.set(0.0, forced = true)
noInputTimePitch.set(0.0, forced = true)
}
override fun toString(): String {
@@ -726,7 +762,8 @@ class BirdState : RidingBehaviourState() {
it.lastGlide.set(this.lastGlide.get(), forced = true)
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
it.currPitchCorrectionForce.set(this.currPitchCorrectionForce.get(), forced = true)
it.noInputTime.set(this.noInputTime.get(), forced = true)
it.noInputTimeRoll.set(this.noInputTimeRoll.get(), forced = true)
it.noInputTimePitch.set(this.noInputTimePitch.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -735,3 +772,16 @@ class BirdState : RidingBehaviourState() {
return super.shouldSync(previous)
}
}
/**
* Calculates the shortest difference between two angles.
* The result will be in the range [-180, 180].
* A positive result indicates a clockwise direction from this angle to the other,
* a negative result indicates a counter-clockwise direction.
*/
fun Float.angleDifference(other: Float): Float {
var diff = other - this
while (diff <= -180F) diff += 360F
while (diff > 180F) diff -= 360F
return diff
}
@@ -75,7 +75,6 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
val zVector = driver.zza.toDouble()
val speedStat = settings.calculate(RidingStat.SPEED, 0)
return Vec3(xVector, yVector, zVector)
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
@@ -27,7 +25,6 @@ import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.ClipContext
@@ -131,8 +128,8 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
) {
val stam = state.stamina.get()
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
val newStam = if (state.tooHigh.get()) max(0.0f, stam - stamDrainRate) else min(1.0f,stam + stamDrainRate)
val stamReplenishRate = (1.0f / vehicle.runtime.resolveDouble(settings.stamReplenishTimeSeconds) ).toFloat() / 20.0f // 2 seconds to replenish a full bar of stamina
val newStam = if (state.tooHigh.get()) max(0.0f, stam - stamDrainRate) else min(1.0f,stam + stamReplenishRate)
state.stamina.set(newStam)
}
@@ -433,6 +430,9 @@ class HoverSettings : RidingBehaviourSettings {
var canJump = "true".asExpression()
private set
var stamReplenishTimeSeconds = "2.0".asExpression()
private set
// Speed in block per second
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 7.0, 1.0)".asExpression()
private set
@@ -26,6 +26,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.Minecraft
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -34,6 +35,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Vector3f
import kotlin.math.*
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
@@ -87,7 +89,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
if(state.stamina.get() != 0.0f && boostKeyPressed) {
if (state.stamina.get() >= 0.25f) {
if (state.stamina.get() >= 0.0f) {
//If on the previous tick the boost key was held then don't change if the ride is boosting
if(state.boostIsToggleable.get()) {
//flip the boosting state if boost key is pressed
@@ -115,11 +117,17 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
if (state.boosting.get()) {
newStam = max(0.0f,stam - stamDrainRate * 1.5f)
} else {
newStam = max(0.0f,stam - stamDrainRate)
}
// If out of stamina then increase noStamTickCnt
if (newStam == 0.0f) {
state.noStamTickCnt.set(state.noStamTickCnt.get() + 1)
} else {
state.noStamTickCnt.get()
}
state.stamina.set(newStam)
}
@@ -139,27 +147,26 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
driver: Player,
input: Vec3
): Vec3 {
var upForce = 0.0
var forwardForce = 0.0
val controller = (driver as? OrientationControllable)?.orientationController
if (controller == null || controller.orientation == null) return Vec3.ZERO
//Calculate ride space velocity
calculateRideSpaceVel(settings, state, vehicle, driver)
//Translate ride space velocity to world space velocity.
if (controller != null) {
upForce = if(state.stamina.get() != 0.0f) -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
else min(0.0, -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z)
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
}
// The downward force used to encourage players to stop flying upside down.
val extraDownwardForce = if(state.stamina.get() == 0.0f) -0.3 else 0.0 // 6 blocks a second downward
val maxDownwardForceTickCnt = 20.0
val extraDownwardForce = if(state.stamina.get() == 0.0f) -0.3 * (state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0, 1.0) else 0.0 // 6 blocks a second downward
val velocity = Vec3(0.0, upForce + extraDownwardForce, forwardForce)
// Convert the local velocity vector into a world vector
val localVelVec = Vector3f(
state.rideVelocity.get().x.toFloat(),
(state.rideVelocity.get().y).toFloat(),
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed? orr.. right handed? idk, flip it though
)
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians()) // Unrotate preemptively as this vector gets rotate later down the line in MC logic.
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
return velocity
return worldVelVec
}
/*
@@ -172,9 +179,10 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
driver: Player
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) / 400.0
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed) / 20.0
val speed = state.rideVelocity.get().length()
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
val deccel = vehicle.runtime.resolveDouble(settings.deccelRate)//0.005
val minSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.minSpeedFactor)
val speed = state.rideVelocity.get().z
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr)
val boostTopSpeed = topSpeed * boostMult
@@ -187,7 +195,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel) / 2), minSpeed * 0.5)
max(state.rideVelocity.get().z - (deccel * 1.3), 0.3 * 0.5)
)
)
}
@@ -215,7 +223,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel) / 2), minSpeed)
max(state.rideVelocity.get().z - (deccel), minSpeed)
)
)
} else if (speed > topSpeed) {
@@ -223,6 +231,17 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
state.rideVelocity.get().scale(0.98)
)
}
/****************************************************************************
* Kill strafing velocities carried over from controller transition. Don't
* kill immediately to give some momentum to transitions.
***************************************************************************/
state.rideVelocity.set(Vec3(
lerp(state.rideVelocity.get().x, 0.0, 0.03),
lerp(state.rideVelocity.get().y, 0.0, 0.03),
state.rideVelocity.get().z
))
}
override fun angRollVel(
@@ -450,7 +469,12 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
vehicle: PokemonEntity,
driver: Player
): Float {
return if (state.boosting.get()) 1.2f else 1.0f
val maxDownwardForceTickCnt = 20.0f
return when {
state.noStamTickCnt.get() != 0 -> 1f - 0.2f*(state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0f,1.0f)
state.boosting.get() -> 1.2f
else -> 1.0f
}
}
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
@@ -526,7 +550,11 @@ class JetSettings : RidingBehaviourSettings {
var gravity: Expression = "0".asExpression()
private set
var minSpeed: Expression = "12.0".asExpression()
var deccelRate: Expression = "0.005".asExpression()
private set
// Mult to top speed in order to derive minSpeed
var minSpeedFactor: Expression = "0.25".asExpression()
private set
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
@@ -541,14 +569,14 @@ class JetSettings : RidingBehaviourSettings {
private set
// Turn rate in degrees per second
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 20.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 10.0)".asExpression()
private set
// Top Speed in blocks per second
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 24.0, 4.0)".asExpression()
private set
// Acceleration in blocks per s^2
// Seconds to get to top speed
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', 4.0, 1.0)".asExpression()
"q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 8.0)".asExpression()
private set
// Time in seconds to drain full bar of stamina flying
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 4.0)".asExpression()
@@ -561,7 +589,8 @@ class JetSettings : RidingBehaviourSettings {
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(deccelRate)
buffer.writeExpression(minSpeedFactor)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(jumpExpr)
@@ -575,7 +604,8 @@ class JetSettings : RidingBehaviourSettings {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
deccelRate = buffer.readExpression()
minSpeedFactor = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
jumpExpr = buffer.readExpression()
@@ -593,6 +623,7 @@ class JetState : RidingBehaviourState() {
var boosting = ridingState(false, Side.BOTH)
var boostIsToggleable = ridingState(false, Side.BOTH)
var canSpeedBurst = ridingState(false, Side.BOTH)
var noStamTickCnt = ridingState(0, Side.CLIENT) // Value that is increased for every tick you are out of stamina
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
@@ -619,6 +650,7 @@ class JetState : RidingBehaviourState() {
boosting.set(false, forced = true)
boostIsToggleable.set(false, forced = true)
canSpeedBurst.set(true, forced = true)
noStamTickCnt.set(0, forced = true)
}
override fun copy() = JetState().also {
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -30,6 +31,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Matrix3f
import kotlin.math.*
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
@@ -243,8 +245,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
val dmSpeed = vehicle.deltaMovement.length()
val rvSpeed = state.rideVelocity.get().length()
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
/******************************************************
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
// if (dmSpeed > rvSpeed) {
// // align the velocity vector to be in local vehicle space
// newVelocity = vehicle.deltaMovement
// val yawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
// newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
// }
if (vehicle.horizontalCollision) {
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
}
@@ -507,23 +522,23 @@ class HorseSettings : RidingBehaviourSettings {
var lookYawLimit = "90.0f".asExpression()
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.4)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.2, 0.1)".asExpression()
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', 0.5, 2.0)".asExpression()
"q.get_ride_stats('ACCELERATION', 'LAND', 0.1, 12.0)".asExpression()
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 60.0, 4.0)".asExpression()
private set
//Between a one block jump and a six block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.0, 0.38)".asExpression()
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.6, 0.2)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 90.0, 30.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.core.BlockPos
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
@@ -134,7 +135,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
val speed = vehicle.runtime.resolveDouble(settings.speedExpr)
val acceleration = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val acceleration = speed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20)
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
return velocity
@@ -170,14 +171,25 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
if (state.jumpBuffer.get() == -1 && (vehicle.isInWater || vehicle.isUnderWater)) {
val verticalVelocity = if (vehicle.isUnderWater) 0.5 else 0.0
return Vec3(velocity.x, verticalVelocity, velocity.z)
if (shouldFloatHigher(vehicle, settings)) {
return Vec3(velocity.x, 0.5, velocity.z)
}
else {
return Vec3(velocity.x, 0.0, velocity.z)
}
}
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity)
val gravity = (9.8 / ( 20.0)) * 0.2
return Vec3(velocity.x, max(velocity.y - gravity, terminalVelocity), velocity.z)
}
private fun shouldFloatHigher(vehicle: PokemonEntity, settings: BoatSettings): Boolean {
val surfaceOffset = vehicle.runtime.resolveFloat(settings.surfaceLevelOffset)
val blockPos = BlockPos.containing(vehicle.x, vehicle.eyeY + surfaceOffset, vehicle.z)
val fluidState = vehicle.level().getFluidState(blockPos)
return !fluidState.isEmpty
}
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState) {
val skillModifier = vehicle.runtime.resolveDouble(settings.rotationspeedModifierExpr)
val strafe = driver.xxa
@@ -394,14 +406,16 @@ class BoatSettings : RidingBehaviourSettings {
var speedExpr = "q.get_ride_stats('SPEED', 'LIQUID', 2.0, 0.5)".asExpression()
private set
var accelerationExpr = "q.get_ride_stats('ACCELERATION', 'LIQUID', 0.5, 0.05)".asExpression()
var accelerationExpr = "q.get_ride_stats('ACCELERATION', 'LIQUID', 0.1, 12.0)".asExpression()
private set
var sprintSpeedModifier = "1.5".asExpression()
var sprintSpeedModifier = "q.get_ride_stats('JUMP', 'LIQUID', 1.5, 1.25)".asExpression()
private set
var sprintFovModifier = "1.2".asExpression()
var surfaceLevelOffset = "0".asExpression()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
@@ -42,9 +42,10 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
var fade: Float = 0.0f
var shouldMuffle: Boolean = soundSettings.muffleEnabled
var rideAttenuation: RideAttenuationModel = soundSettings.attenuationModel
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is on muffle and 0.0 is full hFgain muting
// TODO: revisit below flag once multi passenger support is enabled
var isPassenger: Boolean = Minecraft.getInstance().player?.id == ride.controllingPassenger?.id
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is no muffle and 0.0 is full hFgain muting
var isPassenger: Boolean = ride.passengers.any { passenger ->
Minecraft.getInstance().player?.id == passenger.id
}
init {
this.looping = true
@@ -21,11 +21,11 @@ import net.minecraft.network.chat.Component
enum class RidingStat(
val flavour: Flavour
) {
SPEED(Flavour.SWEET),
ACCELERATION(Flavour.SPICY),
SKILL(Flavour.DRY),
JUMP(Flavour.BITTER),
STAMINA(Flavour.SOUR);
SPEED(Flavour.SWEET),
STAMINA(Flavour.SOUR),
JUMP(Flavour.BITTER);
companion object {
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
@@ -34,7 +34,7 @@ object CobblemonSpawnPools {
SpawnablePositionTypePrecalculation,
BucketPrecalculation,
BiomePrecalculation
)
), reloadable = true
)
}
@@ -104,7 +104,7 @@ object CobblemonClient {
teamData = ClientPlayerTeamData()
clientPokedexData = ClientPokedexManager(mutableMapOf())
storage.onLogin()
CobblemonDataProvider.canReload = false
// CobblemonDataProvider.canReload = false
}
fun onLogout() {
@@ -113,7 +113,7 @@ object CobblemonClient {
battleOverlay.onLogout()
ClientTaskTracker.clear()
checkedStarterScreen = false
CobblemonDataProvider.canReload = true
// CobblemonDataProvider.canReload = true
DeferredRenderer.clearAll()
ClientPlayerIcon.clear()
}
@@ -135,7 +135,11 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
// Configure what controls to show for each behaviour
when (currentBehaviourKey) {
BirdBehaviour.KEY -> {}
BoatBehaviour.KEY -> {}
BoatBehaviour.KEY -> {
showVerticalMouse = false
showHorizontalMouse = false
showSneakKey = false
}
BurstBehaviour.KEY -> {}
DolphinBehaviour.KEY -> {}
GliderBehaviour.KEY -> {}
@@ -82,9 +82,6 @@ class PokedexGUI private constructor(
private val arrowUpIcon = cobblemonResource("textures/gui/pokedex/arrow_up.png")
private val arrowDownIcon = cobblemonResource("textures/gui/pokedex/arrow_down.png")
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
private val tabSelectArrow = cobblemonResource("textures/gui/pokedex/select_arrow.png")
private val tabIcons = arrayOf(
cobblemonResource("textures/gui/pokedex/tab_info.png"),
@@ -325,17 +322,15 @@ class PokedexGUI private constructor(
// Search type tooltip
if (searchByTypeButton.isButtonHovered(mouseX, mouseY)) {
matrices.pushPose()
matrices.translate(0.0, 0.0, 1000.0)
val searchTypeText = lang("ui.pokedex.search.search_by", lang("ui.pokedex.search.type.${selectedSearchByType.name.lowercase()}")).bold()
val searchTypeTextWidth = Minecraft.getInstance().font.width(searchTypeText.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = searchTypeTextWidth + 6
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY - 16, width = 1, height = 11)
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY - 16, width = tooltipWidth, height = 11)
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY - 16, width = 1, height = 11)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = searchTypeText, x = mouseX, y = mouseY - 15, shadow = true, centered = true)
matrices.popPose()
renderTooltip(
context,
searchTypeText,
mouseX,
mouseY,
delta,
-14
)
}
}
@@ -481,6 +476,8 @@ class PokedexGUI private constructor(
fun displaytabInfoElement(tabIndex: Int, update: Boolean = true) {
val showActiveTab = selectedEntry?.let { selectedForm in CobblemonClient.clientPokedexData.getCaughtForms(it) } == true
var statSubIndex = 0
if (tabButtons.isNotEmpty() && tabButtons.size > tabIndex) {
tabButtons.forEachIndexed { index, tab -> tab.isWidgetActive = showActiveTab && index == tabIndex
}
@@ -491,6 +488,14 @@ class PokedexGUI private constructor(
removeWidget((tabInfoElement as AbilitiesWidget).rightButton)
}
if (tabInfoIndex == TAB_STATS && tabInfoElement is StatsWidget) {
statSubIndex = (tabInfoElement as StatsWidget).selectedStatTypeIndex
removeWidget((tabInfoElement as StatsWidget).leftButton)
removeWidget((tabInfoElement as StatsWidget).rightButton)
removeWidget((tabInfoElement as StatsWidget).leftSubButton)
removeWidget((tabInfoElement as StatsWidget).rightSubButton)
}
tabInfoIndex = tabIndex
if (::tabInfoElement.isInitialized) removeWidget(tabInfoElement)
@@ -508,7 +513,9 @@ class PokedexGUI private constructor(
tabInfoElement = SizeWidget(x + 180, y + 135)
}
TAB_STATS -> {
tabInfoElement = StatsWidget(x + 180, y + 135)
tabInfoElement = StatsWidget(x + 180, y + 135).also {
it.selectedStatTypeIndex = statSubIndex
}
}
TAB_DROPS -> {
tabInfoElement = DropsScrollingWidget(x + 189, y + 135)
@@ -555,6 +562,17 @@ class PokedexGUI private constructor(
}
TAB_STATS -> {
(tabInfoElement as StatsWidget).baseStats = form.baseStats
(tabInfoElement as StatsWidget).rideProperties = form.riding
form.riding.behaviours?.let {
addRenderableWidget((tabInfoElement as StatsWidget).leftButton)
addRenderableWidget((tabInfoElement as StatsWidget).rightButton)
if (it.size > 1) {
addRenderableWidget((tabInfoElement as StatsWidget).leftSubButton)
addRenderableWidget((tabInfoElement as StatsWidget).rightSubButton)
}
}
}
TAB_DROPS -> {
(tabInfoElement as DropsScrollingWidget).dropTable = form.drops
@@ -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.gui.pokedex
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.MutableComponent
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
fun renderTooltip(context: GuiGraphics, text: MutableComponent, posX: Int, posY: Int, delta: Float, offsetY: Int = 0) {
val poseStack = context.pose()
val textWidth = Minecraft.getInstance().font.width(text.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = textWidth + 6
val tooltipHeight = 11
val tooltipTop = posY + offsetY
poseStack.pushPose()
poseStack.translate(0.0, 0.0, 1000.0)
Minecraft.getInstance().let {
it.mainRenderTarget.bindWrite(false)
context.enableScissor(
posX - (tooltipWidth / 2),
tooltipTop + 1,
(posX - (tooltipWidth / 2)) + tooltipWidth,
tooltipTop + 10
)
it.gameRenderer.processBlurEffect(delta)
context.disableScissor()
it.mainRenderTarget.bindWrite(true)
}
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX - (tooltipWidth / 2) - 1, y = tooltipTop, width = 1, height = tooltipHeight)
blitk(matrixStack = poseStack, texture = tooltipBackground, x = posX - (tooltipWidth / 2), y = tooltipTop, width = tooltipWidth, height = tooltipHeight)
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX + (tooltipWidth / 2), y = tooltipTop, width = 1, height = tooltipHeight)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = text, x = posX, y = tooltipTop + 1, shadow = true, centered = true)
poseStack.popPose()
}
@@ -31,6 +31,7 @@ class ScaledButton(
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
var isWidgetActive = false
var isVisible = true
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
@@ -38,8 +39,7 @@ class ScaledButton(
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val matrices = context.pose()
if (resource != null) {
if (isVisible && resource != null) {
blitk(
matrixStack = matrices,
texture = resource,
@@ -55,14 +55,14 @@ class ScaledButton(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (active && isButtonHovered(mouseX, mouseY)) {
if (isVisible && active && isButtonHovered(mouseX, mouseY)) {
super.mouseClicked(mouseX, mouseY, button)
}
return false
}
override fun playDownSound(soundManager: SoundManager) {
if (active && !this.silent) {
if (isVisible && active && !this.silent) {
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.POKEDEX_CLICK_SHORT, 1.0F))
}
}
@@ -71,4 +71,4 @@ class ScaledButton(
return mouseX.toFloat() in (buttonX..(buttonX + (buttonWidth.toFloat() * scale)))
&& mouseY.toFloat() in (buttonY..(buttonY + (buttonHeight.toFloat() * scale)))
}
}
}
@@ -48,12 +48,11 @@ class AbilitiesWidget(x: Int, y: Int): InfoTextScrollWidget(pX = x, pY = y) {
init { setAbility() }
private fun switchAbility(nextIndex: Boolean) {
if (nextIndex) {
if (selectedAbilitiesIndex < abilitiesList.lastIndex) selectedAbilitiesIndex++
else selectedAbilitiesIndex = 0
val size = abilitiesList.size
selectedAbilitiesIndex = if (nextIndex) {
(selectedAbilitiesIndex + 1) % size
} else {
if (selectedAbilitiesIndex > 0) selectedAbilitiesIndex--
else selectedAbilitiesIndex = abilitiesList.lastIndex
(selectedAbilitiesIndex - 1 + size) % size
}
setAbility()
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.gui.pokedex.widgets
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.gui.ParentWidget
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
@@ -33,6 +34,7 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_POKE_BALL_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
@@ -94,9 +96,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
private val buttonGenderFemale = cobblemonResource("textures/gui/pokedex/button_female.png")
private val buttonNone = cobblemonResource("textures/gui/pokedex/button_none.png")
private val buttonShiny = cobblemonResource("textures/gui/pokedex/button_shiny.png")
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
}
var currentEntry : PokedexEntry? = null
@@ -440,13 +439,14 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
// Tooltip
if (it.isVisible() && it.getWidget().isButtonHovered(mouseX, mouseY)) {
val variationText = it.variation.displayName.asTranslated().bold()
val variationTextWidth = Minecraft.getInstance().font.width(variationText.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = variationTextWidth + 6
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY + 8, width = 1, height = 11)
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY + 8, width = tooltipWidth, height = 11)
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY + 8, width = 1, height = 11)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = variationText, x = mouseX, y = mouseY + 9, shadow = true, centered = true)
renderTooltip(
context,
variationText,
mouseX,
mouseY,
delta,
10
)
}
}
@@ -11,18 +11,24 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_DESCRIPTION_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.util.FastColor
class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
pX,
@@ -33,6 +39,18 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
) {
companion object {
const val STAT_TYPE_BASE = 0
const val STAT_TYPE_RIDE = 1
const val MAX_BAR_WIDTH = 75
private val statTypes = listOf(STAT_TYPE_BASE, STAT_TYPE_RIDE)
private val divider = cobblemonResource("textures/gui/pokedex/info_stat_divider.png")
private val arrowLeft = cobblemonResource("textures/gui/pokedex/info_arrow_left.png")
private val arrowRight = cobblemonResource("textures/gui/pokedex/info_arrow_right.png")
private val subArrowLeft = cobblemonResource("textures/gui/pokedex/info_sub_arrow_left.png")
private val subArrowRight = cobblemonResource("textures/gui/pokedex/info_sub_arrow_right.png")
private val statLabels = arrayOf(
lang("ui.stats.hp"),
lang("ui.stats.atk"),
@@ -52,12 +70,91 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
)
}
val leftButton: ScaledButton = ScaledButton(
pX + 2.5F,
pY - 8F,
7,
10,
arrowLeft,
clickAction = { switchStatType(false) }
)
val rightButton: ScaledButton = ScaledButton(
pX + 133F,
pY - 8F,
7,
10,
arrowRight,
clickAction = { switchStatType(true) }
)
val leftSubButton: ScaledButton = ScaledButton(
pX + 2.5F,
pY + 18.5F,
7,
10,
subArrowLeft,
clickAction = { switchRideBehaviour(false) }
)
val rightSubButton: ScaledButton = ScaledButton(
pX + 133F,
pY + 18.5F,
7,
10,
subArrowRight,
clickAction = { switchRideBehaviour(true) }
)
var selectedRideBehavioursIndex: Int = 0
var selectedStatTypeIndex: Int = STAT_TYPE_BASE
set(value) {
field = value
val isRideStatVisible = value == STAT_TYPE_RIDE
leftSubButton.visible = isRideStatVisible
rightSubButton.visible = isRideStatVisible
}
var baseStats: Map<Stat, Int>? = null
var rideProperties: RidingProperties? = null
private fun switchStatType(nextIndex: Boolean) {
val size = statTypes.size
selectedStatTypeIndex = if (nextIndex) {
(selectedStatTypeIndex + 1) % size
} else {
(selectedStatTypeIndex - 1 + size) % size
}
val showSubArrowButtons = selectedStatTypeIndex == STAT_TYPE_RIDE
leftSubButton.visible = showSubArrowButtons
rightSubButton.visible = showSubArrowButtons
}
private fun switchRideBehaviour(nextIndex: Boolean) {
val size = rideProperties?.behaviours?.size ?: 0
selectedRideBehavioursIndex = if (nextIndex) {
(selectedRideBehavioursIndex + 1) % size
} else {
(selectedRideBehavioursIndex - 1 + size) % size
}
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
if (baseStats == null) return
when (selectedStatTypeIndex) {
STAT_TYPE_BASE -> {
renderBaseStats(context)
}
STAT_TYPE_RIDE -> {
renderRideStats(context, mouseX, mouseY, delta)
}
}
}
val matrices = context.pose()
private fun renderBaseStats(context: GuiGraphics) {
if (baseStats == null) return
drawScaledText(
context = context,
@@ -68,18 +165,9 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
shadow = true
)
// Divider
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = pX + 50.5,
y = pY + 4,
height = 33.5,
width = 1,
red = 0.71F,
green = 0.95F,
blue = 0.95F
)
val matrices = context.pose()
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 4, height = 33.5, width = 1)
for (i in statLabels.indices) {
drawScaledText(
@@ -107,10 +195,10 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = pX + 54.5,
x = pX + 55,
y = pY + 4 + (6 * i),
height = 3.5F,
width = (statValue / 255F) * 77,
width = (statValue / 255F) * MAX_BAR_WIDTH,
red = red,
green = green,
blue = blue
@@ -119,6 +207,119 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
}
}
private fun renderRideStats(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
rideProperties?.behaviours?.let { behaviours ->
val selectedBehaviour = behaviours.entries.toList()[selectedRideBehavioursIndex]
val behaviourStats = selectedBehaviour.value.stats
val matrices = context.pose()
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride").bold(),
x = pX + 9,
y = pY - 10,
shadow = true
)
rideProperties?.seats?.size?.let { seats ->
drawScaledTextJustifiedRight(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride.seats", seats).bold(),
x = pX + 130,
y = pY - 10,
shadow = true
)
}
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 10, height = 27.5, width = 1)
blitk(matrixStack = matrices, texture = divider, x = pX + 9, y = pY + 2, height = 6, width = 121)
drawScaledText(
context = context,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
x = pX + 69.5,
y = pY + 3,
colour = 0x3A96B6,
scale = SCALE,
centered = true
)
RidingStat.entries.forEachIndexed { index, stat ->
drawScaledText(
context = context,
text = lang("ui.stats.ride.${stat.name.lowercase()}").bold(),
x = pX + 9,
y = pY + 10 + (6 * index),
colour = 0x606B6E,
scale = SCALE
)
behaviourStats[stat]?.let { range ->
val startPosY = pY + 10 + (6F * index)
val red = FastColor.ARGB32.red(stat.flavour.colour) / 255F
val green = FastColor.ARGB32.green(stat.flavour.colour) / 255F
val blue = FastColor.ARGB32.blue(stat.flavour.colour) / 255F
val barStart = pX + 55F
val baseStatWidth = (range.start / 100F) * MAX_BAR_WIDTH
val boostedStatWidth = ((range.endInclusive / 100F) * MAX_BAR_WIDTH) - baseStatWidth
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = barStart,
y = startPosY,
height = 3.5F,
width = baseStatWidth,
red = red,
green = green,
blue = blue
)
val isBoostedStatHovered = mouseX.toFloat() in ((barStart + baseStatWidth)..(barStart + baseStatWidth + boostedStatWidth)) && mouseY.toFloat() in (startPosY..(startPosY + 3.5F))
drawScaledTextJustifiedRight(
context = context,
text = if (isBoostedStatHovered) range.endInclusive.toString().text().bold() else range.start.toString().text(),
x = pX + 48,
y = startPosY,
colour = 0x3A96B6,
scale = SCALE
)
// Boostable range
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = barStart + baseStatWidth,
y = startPosY,
height = 3.5F,
width = boostedStatWidth,
red = red,
green = green,
blue = blue,
alpha = if (isBoostedStatHovered) 1F else 0.5F
)
if (isBoostedStatHovered) renderTooltip(
context,
lang("ui.stats.ride.boost_maximum").bold(),
mouseX,
mouseY,
delta,
-16
)
}
}
} ?: run {
// If ride properties null, reset index to base stats
selectedStatTypeIndex = STAT_TYPE_BASE
}
}
private fun getStatValueRGB(value: Int): Triple<Float, Float, Float> {
when {
value >= 150 -> return Triple(0.647F, 0.929F, 0.647F)
@@ -128,4 +329,4 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
}
return Triple(0.964F, 0.64F, 0.502F)
}
}
}
@@ -53,7 +53,7 @@ class SummaryButton(
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
public override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
if (!this.renderRequirement.invoke(this)) {
return
}
@@ -28,83 +28,91 @@ import net.minecraft.world.phys.Vec3
* @author Hiroku
* @since November 13th, 2023
*/
class BarSummarySpeciesFeatureRenderer(
open class BarSummarySpeciesFeatureRenderer(
override val name: String,
val displayName: MutableComponent,
val min: Int,
val max: Int,
val colour: Vec3,
val underlay: ResourceLocation,
val overlay: ResourceLocation,
val pokemon: Pokemon
val pokemon: Pokemon,
val min: Int,
val max: Int,
val value: Int = 0,
val colour: Vec3? = null
) : SummarySpeciesFeatureRenderer<IntSpeciesFeature> {
override fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
val value = feature.value
val barRatio = (value - min) / (max - min).toFloat()
val barWidth = Mth.ceil(barRatio * 108)
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
renderElement(guiGraphics, x, y, pokemon, feature.value)
}
open fun renderElement(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, barValue: Int = value) {
val barRatio = (barValue - min) / (max - min).toFloat()
val barWidth = Mth.ceil(barRatio * 110)
blitk(
matrixStack = GuiGraphics.pose(),
matrixStack = guiGraphics.pose(),
texture = underlay,
x = x,
y = y,
height = 28,
width = 124
height = 24,
width = 116
)
val red = colour.x / 255F
val green = colour.y / 255F
val blue = colour.z / 255F
renderBar(guiGraphics, x, y, barValue, barRatio, barWidth)
blitk(
matrixStack = GuiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 8,
y = y + 16,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = GuiGraphics.pose(),
matrixStack = guiGraphics.pose(),
texture = overlay,
x = x / StatWidget.SCALE,
y = (y + 16) / StatWidget.SCALE,
x = (x + 3) / StatWidget.SCALE,
y = (y + 13) / StatWidget.SCALE,
height = 20,
width = 248,
width = 220,
scale = StatWidget.SCALE
)
// Label
drawScaledText(
context = GuiGraphics,
context = guiGraphics,
font = CobblemonResources.DEFAULT_LARGE,
text = displayName.bold(),
x = x + 62,
x = x + 58,
y = y + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = GuiGraphics,
text = value.toString().text(),
x = x + 11,
context = guiGraphics,
text = barValue.toString().text(),
x = x + 9,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
drawScaledText(
context = GuiGraphics,
context = guiGraphics,
text = "${Mth.floor(barRatio * 100)}%".text(),
x = x + 113,
x = x + 107,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
}
}
open fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val red = (colour?.x ?: 255.0) / 255.0
val green = (colour?.y ?: 255.0) / 255.0
val blue = (colour?.z ?: 255.0) / 255.0
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -23,10 +23,10 @@ interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
val name: String
/** Draws it at a particular position. */
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
val feature = pokemon.getFeature<T>(name) ?: return false
render(GuiGraphics, x, y, pokemon, feature)
render(guiGraphics, x, y, pokemon, feature)
return true
}
}
}
@@ -14,12 +14,16 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.italicise
import com.cobblemon.mod.common.api.text.onHover
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.SummaryButton
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FriendshipFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FullnessFeatureRenderer
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
@@ -39,8 +43,6 @@ import net.minecraft.util.Mth.ceil
import net.minecraft.util.Mth.floor
import net.minecraft.world.phys.Vec2
import org.joml.Vector3f
import kotlin.math.cos
import kotlin.math.sin
class StatWidget(
pX: Int, pY: Int,
@@ -51,63 +53,129 @@ class StatWidget(
companion object {
// Stat tab options
private const val STATS = "stats"
private const val IV = "iv"
private const val EV = "ev"
private const val IV = "ivs"
private const val EV = "evs"
private const val RIDE = "ride"
private const val OTHER = "other"
private val statOptions = listOf(STATS, IV, EV, OTHER)
private const val statTabWidth = 24
private const val OTHER_STAT_BARS_PER_PAGE = 4
private const val STAT_TAB_WIDTH_6 = 22
private const val STAT_TAB_WIDTH_5 = 24
private const val MARKER_WIDTH = 8
private const val WIDTH = 134
private const val HEIGHT = 148
const val SCALE = 0.5F
private const val WHITE = 0x00FFFFFF
private const val GREY = 0x00AAAAAA
private const val GREY = 0x008F8F8F
private const val BLUE = 0x00548BFB
private const val RED = 0x00FB5454
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
private val statsChartPentagonResource = cobblemonResource("textures/gui/summary/summary_stats_chart_pentagon.png")
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
private val statsOtherBarTemplate = cobblemonResource("textures/gui/summary/summary_stats_other_bar.png")
private val friendshipOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png")
private val fullnessOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png")
private val statsOtherSidebarsResource = cobblemonResource("textures/gui/summary/summary_stats_other_sidebars.png")
private val statsOtherSidebarArrowLeft = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_left.png")
private val statsOtherSidebarArrowRight = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_right.png")
private val tabMarkerResource = cobblemonResource("textures/gui/summary/summary_stats_tab_marker.png")
private val statIncreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_increase.png")
private val statDecreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_decrease.png")
private val statsLabel = lang("ui.stats")
private val ivLabel = lang("ui.stats.ivs")
private val evLabel = lang("ui.stats.evs")
private val otherLabel = lang("ui.stats.other")
private val statLabels: Map<MutableComponent, Stats> = mapOf(
lang("ui.stats.hp").bold() to Stats.HP,
lang("ui.stats.atk").bold() to Stats.ATTACK,
lang("ui.stats.def").bold() to Stats.DEFENCE,
lang("ui.stats.speed").bold() to Stats.SPEED,
lang("ui.stats.sp_def").bold() to Stats.SPECIAL_DEFENCE,
lang("ui.stats.sp_atk").bold() to Stats.SPECIAL_ATTACK
)
private val hpLabel = lang("ui.stats.hp")
private val spAtkLabel = lang("ui.stats.sp_atk")
private val atkLabel = lang("ui.stats.atk")
private val spDefLabel = lang("ui.stats.sp_def")
private val defLabel = lang("ui.stats.def")
private val speedLabel = lang("ui.stats.speed")
private val hexagonVerticesOffset = listOf(
Pair(67.0, 10.5), // 12 o'clock
Pair(122.0, 42.5), // 2 o'clock
Pair(122.0, 93.5), // 4 o'clock
Pair(67.0, 124.5), // 6 o'clock
Pair(12.0, 93.5), // 8 o'clock
Pair(12.0, 42.5) // 10 o'clock
)
private val pentagonVerticesOffset = listOf(
Pair(67.0, 10.5), // 12 o'clock
Pair(123.0, 47.5), // 2 o'clock
Pair(103.0, 112.5), // 5 o'clock
Pair(31.0, 112.5), // 7 o'clock
Pair(11.0, 47.5), // 10 o'clock
)
}
var effectiveBattleIVs: Map<Stat, Int> = Stats.PERMANENT.associateWith { stat ->
pokemon.ivs.getEffectiveBattleIV(stat)
}
private val statOptions = if (pokemon.form.riding.behaviours != null) listOf(STATS, IV, EV, RIDE, OTHER) else listOf(STATS, IV, EV, OTHER)
private var _statTabIndex: Int = tabIndex
var statTabIndex: Int
get() = _statTabIndex.coerceIn(0, statOptions.size - 1)
set(value) {
_statTabIndex = value.coerceIn(0, statOptions.size - 1)
}
private var _rideBehaviourIndex: Int = 0
var rideBehaviourIndex: Int
get() = _rideBehaviourIndex.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
set(value) {
_rideBehaviourIndex = value.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
}
val universalFeatures = listOf(
FriendshipFeatureRenderer(pokemon),
FullnessFeatureRenderer(pokemon)
)
var statTabIndex = tabIndex
val renderableFeatures = SpeciesFeatures
.getFeaturesFor(pokemon.species)
.filterIsInstance<SynchronizedSpeciesFeatureProvider<*>>()
.mapNotNull { it.getRenderer(pokemon) }
private fun drawTriangle(
colour: Vector3f,
v1: Vec2,
v2: Vec2,
v3: Vec2
) {
var otherStatsPageIndex: Int = 0
var otherStatLeftButton: SummaryButton? = null
var otherStatRightButton: SummaryButton? = null
init {
if (renderableFeatures.size + universalFeatures.size > OTHER_STAT_BARS_PER_PAGE) {
otherStatLeftButton = SummaryButton(
x - 1F,
y + 67F,
14,
14,
resource = statsOtherSidebarArrowLeft,
scale = SCALE,
clickAction = { switchOtherStatsPage(false) }
).also { addWidget(it) }
otherStatRightButton = SummaryButton(
x + 128F,
y + 67F,
14,
14,
resource = statsOtherSidebarArrowRight,
scale = SCALE,
clickAction = { switchOtherStatsPage(true) }
).also { addWidget(it) }
}
}
private fun switchOtherStatsPage(nextIndex: Boolean) {
val pages = ceil((renderableFeatures.size + universalFeatures.size).toDouble() / OTHER_STAT_BARS_PER_PAGE.toDouble())
otherStatsPageIndex = if (nextIndex) {
(otherStatsPageIndex + 1) % pages
} else {
(otherStatsPageIndex - 1 + pages) % pages
}
}
private fun drawTriangle(colour: Vector3f, v1: Vec2, v2: Vec2, v3: Vec2, opacity: Float = 0.6F) {
CobblemonResources.WHITE.let { RenderSystem.setShaderTexture(0, it) }
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, 0.6F)
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, opacity)
val bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION)
bufferBuilder.addVertex(v1.x, v1.y, 10F)
bufferBuilder.addVertex(v2.x, v2.y, 10F)
@@ -116,396 +184,326 @@ class StatWidget(
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
}
private fun drawStatHexagon(stats: Map<Stat, Int>, colour: Vector3f, maximum: Int) {
val hexLeftX = x + 25.5
val hexTopY = y + 22
val hexAttackY = hexTopY + 24.5
val hexDefenceY = hexAttackY + 47.0
val hexBottomY = hexDefenceY + 24.5
val hexRightX = x + 108.5
val hexCenterX = (hexLeftX + hexRightX) / 2
val hexCenterY = (hexTopY + hexBottomY) / 2
val minTriangleSize = 8F
val minXTriangleLen = sin(Math.toRadians(61.0)).toFloat() * minTriangleSize * 0.95F
val minYTriangleLen = cos(Math.toRadians(60.0)).toFloat() * minTriangleSize
/**
* Render a 5 or 6 sided polygon
*
* @param ratios list of ratios for how far each vertex is from the center, starting from top vertex going clockwise
* @param colour the colour to render
*/
private fun drawStatPolygon(ratios: List<Float>, colour: Vector3f) {
val sides = ratios.size
if (sides !in 5 .. 6) return
val triangleLongEdge = (hexCenterY - hexTopY - minTriangleSize).toFloat()
val triangleMediumEdge = (triangleLongEdge * sin(Math.toRadians(61.0))).toFloat()
val triangleShortEdge = (triangleLongEdge * cos(Math.toRadians(61.0))).toFloat()
val topVertexY = y + 22F
val centerX = x + 67F
val centerY = topVertexY + if (sides > 5) 48F else 49F
val hpRatio = (stats.getOrDefault(Stats.HP, 0).toFloat() / maximum).coerceIn(0F, 1F)
val atkRatio = (stats.getOrDefault(Stats.ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
val defRatio = (stats.getOrDefault(Stats.DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spAtkRatio = (stats.getOrDefault(Stats.SPECIAL_ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spDefRatio = (stats.getOrDefault(Stats.SPECIAL_DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spdRatio = (stats.getOrDefault(Stats.SPEED, 0).toFloat() / maximum).coerceIn(0F, 1F)
val radius = centerY - topVertexY
val hpPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat() - minTriangleSize - hpRatio * triangleLongEdge
)
val startAngleDeg = -90.0
val angleStepDeg = 360.0 / sides
val attackPoint = Vec2(
hexCenterX.toFloat() + minXTriangleLen + atkRatio * triangleMediumEdge,
hexCenterY.toFloat() - minYTriangleLen - atkRatio * triangleShortEdge
)
// Generate the angles for each vertex
val anglesDeg = List(sides) { i -> startAngleDeg + i * angleStepDeg }
val anglesRad = anglesDeg.map { Math.toRadians(it) }
val defencePoint = Vec2(
hexCenterX.toFloat() + minXTriangleLen + defRatio * triangleMediumEdge,
hexCenterY.toFloat() + minYTriangleLen + defRatio * triangleShortEdge
)
val specialAttackPoint = Vec2(
hexCenterX.toFloat() - minXTriangleLen - spAtkRatio * triangleMediumEdge,
hexCenterY.toFloat() - minYTriangleLen - spAtkRatio * triangleShortEdge
)
val specialDefencePoint = Vec2(
hexCenterX.toFloat() - minXTriangleLen - spDefRatio * triangleMediumEdge,
hexCenterY.toFloat() + minYTriangleLen + spDefRatio * triangleShortEdge
)
val speedPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat() + minTriangleSize + spdRatio * triangleLongEdge
)
val centerPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat()
)
// 1-o'clock
drawTriangle(colour, hpPoint, centerPoint, attackPoint)
// 3-o'clock
drawTriangle(colour, attackPoint, centerPoint, defencePoint)
// 5-o'clock
drawTriangle(colour, defencePoint, centerPoint, speedPoint)
// 7-o'clock
drawTriangle(colour, speedPoint, centerPoint, specialDefencePoint)
// 9-o'clock
drawTriangle(colour, specialDefencePoint, centerPoint, specialAttackPoint)
// 11-o'clock
drawTriangle(colour, specialAttackPoint, centerPoint, hpPoint)
}
private fun drawFriendship(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, friendship: Int) {
val barRatio = friendship / 255F
val barWidth = ceil(barRatio * 108)
blitk(
matrixStack = matrices,
texture = statsOtherBarTemplate,
x = moduleX,
y = moduleY,
height = 28,
width = 124
)
val red = 1
val green: Number = if (pokemon.friendship >= 160) 0.28 else 0.56
val blue: Number = if (pokemon.friendship >= 160) 0.4 else 0.64
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = matrices,
texture = friendshipOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
// Label
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.stats.friendship").bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = context,
text = friendship.toString().text(),
x = moduleX + 11,
y = moduleY + 6,
scale = SCALE,
centered = true
)
drawScaledText(
context = context,
text = "${floor(barRatio * 100)}%".text(),
x = moduleX + 113,
y = moduleY + 6,
scale = SCALE,
centered = true
)
}
private fun drawFullness(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, pokemon: Pokemon) {
val currentFullness = pokemon.currentFullness
val maxFullness = pokemon.getMaxFullness()
val barRatio = currentFullness.toFloat() / maxFullness.toFloat()
val barWidth = ceil(barRatio * 108)
blitk(
matrixStack = matrices,
texture = statsOtherBarTemplate,
x = moduleX,
y = moduleY,
height = 28,
width = 124
)
val (red, green, blue) = when {
barRatio <= 0.33 -> Triple(0f, 1f, 0f) // Green (full green)
barRatio <= 0.66 -> Triple(1f, 1f, 0f) // Yellow (red + green)
else -> Triple(1f, 0f, 0f) // Red (full red)
// Calculate maximum points on the circle
val maxPoints = anglesRad.map { angle ->
Vec2(
centerX + radius * Math.cos(angle).toFloat(),
centerY + radius * Math.sin(angle).toFloat()
)
}
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
// Coerce values as 0.0 will prevent triangles from rendering
val coercedRatios = ratios.map { it.coerceIn(5 / radius, 1F) }
// Draw the overlay
blitk(
matrixStack = matrices,
texture = fullnessOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
// Interpolate vertices based on ratios
val vertices = maxPoints.mapIndexed { index, maxPoint ->
Vec2(
centerX + (maxPoint.x - centerX) * coercedRatios[index],
centerY + (maxPoint.y - centerY) * coercedRatios[index]
)
}
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = "Fullness".text().bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
val centerPoint = Vec2(centerX, centerY)
drawScaledText(
context = context,
text = currentFullness.toString().text(),
x = moduleX + 11,
y = moduleY + 6,
scale = SCALE,
centered = true
)
drawScaledText(
context = context,
text = "${floor(barRatio * 100)}%".text(),
x = moduleX + 113,
y = moduleY + 6,
scale = SCALE,
centered = true
)
// Draw triangles between each vertex and the next clockwise starting from top vertex
RenderSystem.disableDepthTest()
for (i in vertices.indices) {
val nextIndex = (i + 1) % vertices.size
drawTriangle(colour, vertices[i], centerPoint, vertices[nextIndex])
}
RenderSystem.enableDepthTest()
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val renderChart = statOptions.get(statTabIndex) != OTHER
val renderOtherStats = statOptions.get(statTabIndex) == OTHER
val renderPentagonStats = statOptions.get(statTabIndex) == RIDE
val matrices = context.pose()
// Background
blitk(
matrixStack = matrices,
texture = if (renderChart) statsBaseResource else statsOtherBaseResource,
texture = if (renderOtherStats) statsOtherBaseResource else statsBaseResource,
x= x,
y = y,
width = width,
height = height
)
// Chart
if (renderChart) {
blitk(
matrixStack = matrices,
texture = statsChartResource,
x= (x + 25.5) / SCALE,
y = (y + 22) / SCALE,
width = 166,
height = 192,
scale = SCALE
)
}
when (statOptions.get(statTabIndex)) {
STATS -> drawStatHexagon(
mapOf(
Stats.HP to pokemon.maxHealth,
Stats.ATTACK to pokemon.attack,
Stats.DEFENCE to pokemon.defence,
Stats.SPECIAL_ATTACK to pokemon.specialAttack,
Stats.SPECIAL_DEFENCE to pokemon.specialDefence,
Stats.SPEED to pokemon.speed
),
colour = Vector3f(50F/255, 215F/255F, 1F),
maximum = 400
)
IV -> drawStatHexagon(
effectiveBattleIVs,
colour = Vector3f(216F/255, 100F/255, 1F),
maximum = 31
)
EV -> drawStatHexagon(
pokemon.evs.associate { it.key to it.value },
colour = Vector3f(1F, 1F, 100F/255),
maximum = 252
)
}
if (renderOtherStats) {
val barOffsetY = 29
val barPosX = x + 9F
var drawY = y + 15F
drawScaledText(
context = context,
text = statsLabel.bold(),
x = x + 31,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == STATS) WHITE else GREY,
centered = true
)
val featuresList: MutableList<Any> = renderableFeatures.toMutableList()
featuresList.addAll(0, universalFeatures)
drawScaledText(
context = context,
text = ivLabel.bold(),
x = x + 55,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == IV) WHITE else GREY,
centered = true
)
drawScaledText(
context = context,
text = evLabel.bold(),
x = x + 79,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == EV) WHITE else GREY,
centered = true
)
drawScaledText(
context = context,
text = otherLabel.bold(),
x = x + 103,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == OTHER) WHITE else GREY,
centered = true
)
val paddingLeft = (WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2
blitk(
matrixStack = context.pose(),
texture = tabMarkerResource,
x= ((x + paddingLeft + ((statTabIndex + 1) * statTabWidth)) / SCALE) - 2,
y = (y + 140) / SCALE,
width = 8,
height = 4,
scale = SCALE,
)
if (renderChart) {
// Stat Labels
renderTextAtVertices(
context = context,
hp = hpLabel.bold(),
spAtk = spAtkLabel.bold(),
atk = atkLabel.bold(),
spDef = spDefLabel.bold(),
def = defLabel.bold(),
speed = speedLabel.bold()
val pageFeatures = featuresList.subList(
otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE,
minOf((otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE) + OTHER_STAT_BARS_PER_PAGE, featuresList.size)
)
// Stat Values
renderTextAtVertices(
context = context,
offsetY = 5.5,
enableColour = false,
hp = getStatValueAsText(Stats.HP),
spAtk = getStatValueAsText(Stats.SPECIAL_ATTACK),
atk = getStatValueAsText(Stats.ATTACK),
spDef = getStatValueAsText(Stats.SPECIAL_DEFENCE),
def = getStatValueAsText(Stats.DEFENCE),
speed = getStatValueAsText(Stats.SPEED)
)
// Nature-modified Stat Icons
if (statOptions.get(statTabIndex) == STATS) {
val nature = pokemon.effectiveNature
renderModifiedStatIcon(matrices, nature.increasedStat, true)
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
for (feature in pageFeatures) {
if (feature is BarSummarySpeciesFeatureRenderer) {
val rendered = feature.render(
guiGraphics = context,
x = barPosX,
y = drawY,
pokemon = pokemon
)
if (rendered) drawY += barOffsetY
}
}
if (featuresList.size > 4) {
blitk(
matrixStack = matrices,
texture = statsOtherSidebarsResource,
x= x,
y = y + 13,
width = 134,
height = 115
)
otherStatLeftButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
otherStatRightButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
}
} else {
var drawY = y + 11
drawFriendship(x + 5, drawY, matrices, context, pokemon.friendship)
drawY += 30
drawFullness(x + 5, drawY, matrices, context, pokemon)
drawY += 30
for (renderableFeature in renderableFeatures) {
val rendered = renderableFeature.render(
GuiGraphics = context,
x = x + 5F,
y = drawY.toFloat(),
pokemon = pokemon
if (renderPentagonStats) {
blitk(
matrixStack = matrices,
texture = statsChartPentagonResource,
x= (x + 20.5) / SCALE,
y = (y + 22) / SCALE,
width = 186,
height = 176,
scale = SCALE
)
if (rendered) {
drawY += 30
pokemon.form.riding.behaviours?.let {
val behaviours = it.entries.toList()
val selectedBehaviour = behaviours[rideBehaviourIndex]
val pentagonColour = when (selectedBehaviour.key) {
RidingStyle.AIR -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(70F/255F, 235F/225F, 195F/255F)
else Vector3f(40F/255F, 205F/255F, 165F/255F)
}
RidingStyle.LIQUID -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(95F/255F, 165F/255F, 1F)
else Vector3f(65F/255F, 135F/255F, 1F)
}
else -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(1F, 195F/255F, 30F/255F)
else Vector3f(1F, 165F/255F, 0F)
}
}
drawStatPolygon(
ratios = RidingStat.entries.map { pokemon.getRideStat(selectedBehaviour.key, it) / 100F },
colour = pentagonColour
)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
x = x + (WIDTH / 2),
y = y + 66,
shadow = true,
centered = true
)
// Stat Labels
renderPolygonLabels(
context = context,
labels = RidingStat.entries.toList().map { stat ->
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
"${floor(pokemon.getRideStat(selectedBehaviour.key, stat))}/${selectedBehaviour.value.stats[stat]?.endInclusive}".text()
else lang("ui.stats.ride.${stat.name.lowercase()}").bold()
},
verticesOffset = pentagonVerticesOffset
)
// Stat Values
renderPolygonLabels(
context = context,
labels = RidingStat.entries.toList().map { stat ->
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
"${floor((pokemon.getRideStat(selectedBehaviour.key, stat) / (selectedBehaviour.value.stats[stat]?.endInclusive ?: 1)) * 100)}%".text()
else floor(pokemon.getRideStat(selectedBehaviour.key, stat)).toString().text()
},
verticesOffset = pentagonVerticesOffset,
offsetY = 5.5
)
}
} else {
blitk(
matrixStack = matrices,
texture = statsChartResource,
x= (x + 25.5) / SCALE,
y = (y + 22) / SCALE,
width = 166,
height = 192,
scale = SCALE
)
when (statOptions.get(statTabIndex)) {
STATS -> drawStatPolygon(
listOf(
pokemon.maxHealth,
pokemon.attack,
pokemon.defence,
pokemon.speed,
pokemon.specialDefence,
pokemon.specialAttack
).map { it / 400F },
colour = Vector3f(50F/255F, 215F/255F, 1F)
)
IV -> drawStatPolygon(
statLabels.values.map { pokemon.ivs.getEffectiveBattleIV(it) / 31F },
colour = Vector3f(216F/255, 100F/255, 1F)
)
EV -> drawStatPolygon(
statLabels.values.map { (pokemon.evs[it]?.toFloat() ?: 0F) / 252F },
colour = Vector3f(1F, 1F, 100F/255F)
)
}
val labelsHovered = statLabelsHovered(hexagonVerticesOffset, pMouseX, pMouseY)
val labels = statLabels.map { stat ->
if (labelsHovered) {
when (statOptions.get(statTabIndex)) {
IV -> {
"${
if (pokemon.ivs.isHyperTrained(stat.value)) "${pokemon.ivs[stat.value]} (${pokemon.ivs.hyperTrainedIVs[stat.value]})"
else pokemon.ivs[stat.value].toString()
}/31".text()
}
EV -> "${pokemon.evs.getOrDefault(stat.value)}/252".text()
else -> stat.key
}
} else { stat.key }
}
// Stat Labels
renderPolygonLabels(
context = context,
labels = labels,
verticesOffset = hexagonVerticesOffset,
enableColour = true
)
// Stat Values
renderPolygonLabels(
context = context,
labels = statValuesAsText(
statLabels.values.toList(),
labelsHovered
),
verticesOffset = hexagonVerticesOffset,
offsetY = 5.5,
enableColour = true
)
// Nature-modified Stat Icons
if (statOptions.get(statTabIndex) == STATS) {
val nature = pokemon.effectiveNature
renderModifiedStatIcon(matrices, nature.increasedStat, true)
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
}
}
}
// Stat type select bar
val hasRideBehaviour = pokemon.form.riding.behaviours != null
val startPosX = x + if (hasRideBehaviour) 23 else 31
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
statOptions.forEachIndexed { index, stat ->
val statKey = if (stat == STATS) "" else ".$stat"
drawScaledText(
context = context,
text = lang("ui.stats$statKey").bold(),
x = startPosX + (statTabWidth * index),
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == stat) WHITE else GREY,
centered = true
)
}
blitk(
matrixStack = context.pose(),
texture = tabMarkerResource,
x= (startPosX + (statTabIndex * statTabWidth) - (MARKER_WIDTH / 4)) / SCALE,
y = (y + 140) / SCALE,
width = MARKER_WIDTH,
height = 4,
scale = SCALE
)
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
val index = getTabIndexFromPos(pMouseX, pMouseY)
// Only play sound here as the rest of the widget is meant to be silent
if (index in 0..4 && statTabIndex != index) {
if (index in 0 until statOptions.size && statTabIndex != index) {
statTabIndex = index
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
if (statOptions.get(statTabIndex) == RIDE && ((pokemon.riding.behaviours?.size ?: 0) > 1) && centerHovered(pMouseX, pMouseY)) {
rideBehaviourIndex = (rideBehaviourIndex + 1) % (pokemon.riding.behaviours?.size ?: 1)
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
private fun getStatValueAsText(stat: Stat): MutableComponent {
val value = when (statOptions.get(statTabIndex)) {
STATS -> if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()
IV -> if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})" else pokemon.ivs[stat].toString()
EV -> pokemon.evs.getOrDefault(stat).toString()
else -> "0"
private fun statValuesAsText(stats: List<Stat>, asPercent: Boolean): List<MutableComponent> {
return stats.map { stat ->
when (statOptions.get(statTabIndex)) {
STATS -> (if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()).text()
IV -> {
(if (asPercent) {
"${floor((((if (pokemon.ivs.isHyperTrained(stat)) pokemon.ivs.hyperTrainedIVs[stat] else pokemon.ivs[stat]) ?: 0) / 31.0) * 100)}%"
} else {
if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})"
else pokemon.ivs[stat].toString()
}).text()
}
EV -> {
(if (asPercent) "${floor((pokemon.evs.getOrDefault(stat) / 252.0) * 100)}%"
else pokemon.evs.getOrDefault(stat).toString()).text()
}
else -> "0".text()
}
}
return value.text()
}
private fun renderModifiedStatIcon(pPoseStack: PoseStack, stat: Stat?, increasedStat: Boolean) {
@@ -534,7 +532,7 @@ class StatWidget(
}
}
private fun getModifiedStatColour(stat: Stat, enableColour: Boolean): Int {
private fun getModifiedStatColour(stat: Stat?, enableColour: Boolean): Int {
if (statOptions.get(statTabIndex) == STATS && enableColour) {
val nature = pokemon.effectiveNature
@@ -544,81 +542,38 @@ class StatWidget(
return WHITE
}
private fun renderTextAtVertices(
context: GuiGraphics,
offsetY: Double = 0.0,
enableColour: Boolean = true,
hp: MutableComponent,
spAtk: MutableComponent,
atk: MutableComponent,
spDef: MutableComponent,
def: MutableComponent,
speed: MutableComponent
) {
drawScaledText(
context = context,
text = hp,
x = x + 67,
y = y + 10.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.HP, enableColour),
centered = true
)
private fun renderPolygonLabels(context: GuiGraphics, labels: List<MutableComponent>, verticesOffset: List<Pair<Double, Double>>, offsetY: Double = 0.0, enableColour: Boolean = false) {
if (labels.size != verticesOffset.size) return
drawScaledText(
context = context,
text = spAtk,
x = x + 12,
y = y + 42.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPECIAL_ATTACK, enableColour),
centered = true
)
labels.forEachIndexed { index, label ->
drawScaledText(
context = context,
text = label,
x = x + verticesOffset[index].first,
y = y + verticesOffset[index].second + offsetY,
scale = SCALE,
colour = getModifiedStatColour(statLabels[label], enableColour),
centered = true
)
}
}
drawScaledText(
context = context,
text = atk,
x = x + 122,
y = y + 42.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.ATTACK,enableColour),
centered = true
)
private fun centerHovered(pMouseX: Number, pMouseY: Number): Boolean {
return (pMouseX.toInt() in (x + 44)..(x + 90)) && (pMouseY.toInt() in (y + 47)..(y + 93))
}
drawScaledText(
context = context,
text = spDef,
x = x + 12,
y = y + 93.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPECIAL_DEFENCE, enableColour),
centered = true
)
drawScaledText(
context = context,
text = def,
x = x + 122,
y = y + 93.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.DEFENCE, enableColour),
centered = true
)
drawScaledText(
context = context,
text = speed,
x = x + 67,
y = y + 124.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPEED, enableColour),
centered = true
)
private fun statLabelsHovered(labelOffsets: List<Pair<Double, Double>>, mouseX: Int, mouseY: Int): Boolean {
return labelOffsets.any { pos ->
(mouseX.toDouble() in (x + pos.first - 12)..(x + pos.first + 12))
&& (mouseY.toDouble() in (y + pos.second - 1)..(y + pos.second + 10))
}
}
private fun getTabIndexFromPos(mouseX: Double, mouseY: Double): Int {
val paddingLeft = ((WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2.0) + (statTabWidth / 2)
val left = x + paddingLeft
val hasRideBehaviour = pokemon.form.riding.behaviours != null
val left = x + if (hasRideBehaviour) ((23 + 1) / 2).toFloat() else ((31 + 1) / 2).toFloat()
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
val top = y + 140.0
if (mouseX in left..(left + (statTabWidth * (statOptions.size + 1))) && mouseY in top..(top + 9.0)) {
var startX = left
@@ -631,4 +586,4 @@ class StatWidget(
}
return -1
}
}
}
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
class FriendshipFeatureRenderer(
val selectedPokemon: Pokemon
) : BarSummarySpeciesFeatureRenderer(
"friendship",
lang("ui.stats.friendship"),
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png"),
selectedPokemon,
0,
255,
selectedPokemon.friendship
) {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
renderElement(guiGraphics, x, y, pokemon)
return true
}
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val red = 1
val green: Number = if (barValue >= 160) 0.28 else 0.56
val blue: Number = if (barValue >= 160) 0.4 else 0.64
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
class FullnessFeatureRenderer(
val selectedPokemon: Pokemon
) : BarSummarySpeciesFeatureRenderer(
"fullness",
lang("ui.stats.fullness"),
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png"),
selectedPokemon,
0,
selectedPokemon.getMaxFullness(),
selectedPokemon.currentFullness
) {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
renderElement(guiGraphics, x, y, pokemon)
return true
}
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val (red, green, blue) = when {
barRatio <= 0.33 -> Triple(120F/255F, 200F/255F, 80F/255F) // Green
barRatio <= 0.66 -> Triple(240F/255F, 200F/255F, 65F/255F) // Yellow
else -> Triple(230F/255F, 80F/255F, 65F/255F) // Red
}
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.data
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import net.minecraft.client.Minecraft
internal object UnlockReloadPacketHandler : ClientNetworkPacketHandler<UnlockReloadPacket> {
override fun handle(packet: UnlockReloadPacket, client: Minecraft) {
CobblemonDataProvider.canReload = true
}
}
@@ -9,30 +9,63 @@
package com.cobblemon.mod.common.client.render.player
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.riding.RidingAnimation
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.model.AnimationUtils
import net.minecraft.client.model.HumanoidModel
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.player.AbstractClientPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.HumanoidArm
import net.minecraft.world.phys.Vec3
/**
* @author landonjw
*/
object MountedPlayerRenderer {
@JvmField
var shouldApplyRootAnimation = false
@JvmField
val relevantPartsByName = mutableMapOf(
"body" to ModelPart(emptyList(), emptyMap()),
)
val defaultAnimations = listOf(
RidingAnimation(
fileName = "player",
animationName = "animation.player.default_ride_pose"
)
)
fun animate(
model: HumanoidModel<AbstractClientPlayer>,
pokemonEntity: PokemonEntity,
player: AbstractClientPlayer,
relevantPartsByName: Map<String, ModelPart>,
headYaw: Float,
headPitch: Float,
ageInTicks: Float,
limbSwing: Float,
limbSwingAmount: Float
) {
// We need to reset the root because it's a synthetic part, Minecraft won't do this for us.
val root = relevantPartsByName["body"] ?: return
root.x = 0F
root.y = 0F
root.z = 0F
root.xRot = 0F
root.yRot = 0F
root.zRot = 0F
val seatIndex = pokemonEntity.passengers.indexOf(player).takeIf { it != -1 && it < pokemonEntity.seats.size } ?: return
val seat = pokemonEntity.seats[seatIndex]
val animations = seat.poseAnimations?.firstOrNull { it.poseTypes.isEmpty() || it.poseTypes.contains(pokemonEntity.getCurrentPoseType()) }?.animations
?: return
val animations = seat.poseAnimations
?.firstOrNull { it.poseTypes.isEmpty() || it.poseTypes.contains(pokemonEntity.getCurrentPoseType()) }?.animations
?: defaultAnimations
val state = pokemonEntity.delegate as PokemonClientDelegate
relevantPartsByName.values.forEach {
@@ -44,6 +77,8 @@ object MountedPlayerRenderer {
state.runtime.environment.setSimpleVariable("age_in_ticks", DoubleValue(ageInTicks.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing", DoubleValue(limbSwing.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing_amount", DoubleValue(limbSwingAmount.toDouble()))
state.runtime.environment.setSimpleVariable("head_yaw", DoubleValue(headYaw.toDouble()))
state.runtime.environment.setSimpleVariable("head_pitch", DoubleValue(headPitch.toDouble()))
// TODO add the q.player reference, requires a cached thing or something on the client idk
@@ -61,5 +96,59 @@ object MountedPlayerRenderer {
intensity = 1F
)
}
applyRegularAnimation(model, player, ageInTicks)
}
/**
* This does what Minecraft would typically do for various additional body part
* rendering thingies. It's largely copy paste.
*/
fun applyRegularAnimation(
model: HumanoidModel<AbstractClientPlayer>,
player: AbstractClientPlayer,
ageInTicks: Float
) {
val rightHanded = player.mainArm == HumanoidArm.RIGHT
if (player.isUsingItem) {
val usingMainHand = player.usedItemHand == InteractionHand.MAIN_HAND
if (usingMainHand == rightHanded) {
model.poseRightArm(player)
} else {
model.poseLeftArm(player)
}
} else {
val offHandIsTwoHanded: Boolean = if (rightHanded) model.leftArmPose.isTwoHanded else model.rightArmPose.isTwoHanded
if (rightHanded != offHandIsTwoHanded) {
model.poseLeftArm(player)
model.poseRightArm(player)
} else {
model.poseRightArm(player)
model.poseLeftArm(player)
}
}
model.setupAttackAnimation(player, ageInTicks)
if (model.rightArmPose != HumanoidModel.ArmPose.SPYGLASS) {
AnimationUtils.bobModelPart(model.rightArm, ageInTicks, 1.0F);
}
if (model.leftArmPose != HumanoidModel.ArmPose.SPYGLASS) {
AnimationUtils.bobModelPart(model.leftArm, ageInTicks, -1.0F);
}
model.hat.copyFrom(model.head)
}
const val RIDING_SITTING_OFFSET = -10.0
fun animateRoot(stack: PoseStack) {
val root = relevantPartsByName["body"] ?: return
val rootOffset = Vec3(root.x.toDouble(), root.y.toDouble() + RIDING_SITTING_OFFSET, root.z.toDouble()).scale(1/16.0)
val rotation = Vec3(root.xRot.toDouble(), root.yRot.toDouble(), root.zRot.toDouble())
stack.mulPose(Axis.ZP.rotation(rotation.z.toFloat()))
stack.mulPose(Axis.YP.rotation(rotation.y.toFloat()))
stack.mulPose(Axis.XP.rotation(rotation.x.toFloat()))
stack.translate(rootOffset.x.toFloat(), rootOffset.y.toFloat(), rootOffset.z.toFloat())
}
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.client.tooltips
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonRecipeTypes
import com.cobblemon.mod.common.api.cooking.Flavour
import com.cobblemon.mod.common.api.cooking.Food
import com.cobblemon.mod.common.api.cooking.PokePuffUtils
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
@@ -20,20 +18,16 @@ import com.cobblemon.mod.common.api.fishing.SpawnBaitUtils
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.item.components.FoodComponent
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
import com.cobblemon.mod.common.item.crafting.CookingPotRecipeBase
import com.cobblemon.mod.common.item.interactive.PokerodItem
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.lang
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.Style
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffectUtil
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.crafting.RecipeManager
import net.minecraft.world.level.Level
import java.text.DecimalFormat
val cookingPotRecipeAbsorbHeader by lazy { lang("item_class.cooking_pot_recipe_absorbs").gray() }
@@ -121,8 +115,7 @@ fun generateAdditionalFlavorTooltip(flavours: Map<Flavour, Int>): MutableList<Co
val combinedFlavorsLine = Component.literal("")
flavours.filter { it.key != Flavour.MILD }.forEach { (flavour, value) ->
var flavourText = lang("seasoning_flavor.${flavour.name.lowercase()}").withStyle(flavour.chatFormatting)
val flavourText = lang("seasoning_flavor.${flavour.name.lowercase()}").setStyle(Style.EMPTY.withColor(flavour.colour))
if (combinedFlavorsLine.string.isNotEmpty()) {
combinedFlavorsLine.append(" ")
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.item.HeldItems
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.battles.BagItems
@@ -36,7 +37,7 @@ object ReloadShowdownCommand {
ShowdownService.service.resetAllRegistries()
ShowdownService.service.sendRegistryData(Abilities.abilityScripts, "ability")
ShowdownService.service.sendRegistryData(BagItems.bagItemsScripts, "bagItem")
// TODO: ShowdownService.service.sendRegistryData(notYetImplemented, "heldItem")
ShowdownService.service.sendRegistryData(HeldItems.heldItemsScripts, "heldItem")
ShowdownService.service.sendRegistryData(Moves.moveScripts, "move")
ShowdownService.service.sendRegistryData(PokemonSpecies.allShowdownSpecies(), "species")
context.source.sendSystemMessage(Component.literal("Reloaded showdown"))
@@ -10,9 +10,9 @@ package com.cobblemon.mod.common.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonCallbacks
import com.cobblemon.mod.common.CobblemonBehaviours
import com.cobblemon.mod.common.CobblemonCallbacks
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonMechanics
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
import com.cobblemon.mod.common.api.abilities.Abilities
@@ -20,15 +20,15 @@ import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.data.DataProvider
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.dialogue.Dialogues
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
import com.cobblemon.mod.common.api.fishing.PokeRods
import com.cobblemon.mod.common.api.fossil.Fossils
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
import com.cobblemon.mod.common.api.interaction.PokemonInteractions
import com.cobblemon.mod.common.api.item.HeldItems
import com.cobblemon.mod.common.api.mark.Marks
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
@@ -36,6 +36,7 @@ import com.cobblemon.mod.common.api.npc.NPCClasses
import com.cobblemon.mod.common.api.npc.NPCPresets
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokedex.DexAdditions
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.DexEntryAdditions
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
@@ -47,72 +48,71 @@ import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.battles.BagItems
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.cobblemon.mod.common.util.server
import java.util.UUID
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.server.packs.resources.ResourceManagerReloadListener
import java.util.*
object CobblemonDataProvider : DataProvider {
internal var canReload = true
// Both Forge n Fabric keep insertion order so if a registry depends on another simply register it after
private val registries = linkedSetOf<DataRegistry>()
private val reloadableRegistries = linkedSetOf<DataRegistry>()
private val synchronizedPlayerIds = mutableListOf<UUID>()
private val scheduledActions = mutableMapOf<UUID, MutableList<() -> Unit>>()
fun registerDefaults() {
this.register(CobblemonScripts)
this.register(SpeciesFeatures)
this.register(GlobalSpeciesFeatures)
this.register(SpeciesFeatureAssignments)
this.register(ActionEffects)
this.register(Moves)
this.register(Abilities)
this.register(CobblemonBehaviours)
this.register(PokemonSpecies)
this.register(SpeciesAdditions)
this.register(PokeBalls)
this.register(PropertiesCompletionProvider)
this.register(SpawnDetailPresets)
this.register(CobblemonSpawnRules)
this.register(CobblemonMechanics)
this.register(BagItems)
this.register(Dialogues)
this.register(NaturalMaterials)
this.register(Fossils)
this.register(NPCPresets)
this.register(NPCClasses)
this.register(DexEntries)
this.register(DexEntryAdditions)
this.register(Dexes)
this.register(DexAdditions)
this.register(CobblemonCosmeticItems)
this.register(CobblemonCallbacks)
this.register(CobblemonUnlockableWallpapers)
this.register(Marks)
this.register(StarterDataLoader)
this.register(CobblemonScripts, reloadable = true)
this.register(SpeciesFeatures, reloadable = false)
this.register(GlobalSpeciesFeatures, reloadable = false)
this.register(SpeciesFeatureAssignments, reloadable = false)
this.register(ActionEffects, reloadable = true)
this.register(Moves, reloadable = false)
this.register(Abilities, reloadable = false)
this.register(CobblemonBehaviours, reloadable = false)
this.register(PokemonSpecies, reloadable = false)
this.register(SpeciesAdditions, reloadable = false)
this.register(PokeBalls, reloadable = false)
this.register(PropertiesCompletionProvider, reloadable = false)
this.register(SpawnDetailPresets, reloadable = true)
this.register(CobblemonSpawnRules, reloadable = true)
this.register(CobblemonMechanics, reloadable = true)
this.register(BagItems, reloadable = false)
this.register(HeldItems, reloadable = false)
this.register(Dialogues, reloadable = true)
this.register(NaturalMaterials, reloadable = true)
this.register(Fossils, reloadable = true)
this.register(NPCPresets, reloadable = false)
this.register(NPCClasses, reloadable = false)
this.register(DexEntries, reloadable = false)
this.register(DexEntryAdditions, reloadable = false)
this.register(Dexes, reloadable = false)
this.register(DexAdditions, reloadable = false)
this.register(CobblemonCosmeticItems, reloadable = true)
this.register(CobblemonCallbacks, reloadable = true)
this.register(CobblemonUnlockableWallpapers, reloadable = true)
this.register(Marks, reloadable = false)
this.register(StarterDataLoader, reloadable = true)
CobblemonSpawnPools.load()
this.register(PokeRods)
this.register(Berries)
this.register(Seasonings)
this.register(PokemonInteractions)
this.register(SpawnBaitEffects)
this.register(PokeRods, reloadable = false)
this.register(Berries, reloadable = false)
this.register(Seasonings, reloadable = false)
this.register(PokemonInteractions, reloadable = false)
this.register(SpawnBaitEffects, reloadable = false)
SpawnBait.Effects.setupEffects()
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
synchronizedPlayerIds.remove(it.player.uuid)
UnlockReloadPacket().sendToPlayer(it.player)
}
ifClient {
@@ -121,12 +121,15 @@ object CobblemonDataProvider : DataProvider {
Cobblemon.implementation.registerResourceReloader(cobblemonResource("data_resources"), SimpleResourceReloader(PackType.SERVER_DATA), PackType.SERVER_DATA, emptyList())
}
override fun <T : DataRegistry> register(registry: T): T {
override fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T {
// Only send message once
if (this.registries.isEmpty()) {
LOGGER.info("Note: Cobblemon data registries are only loaded once per server instance as Pokémon species are not safe to reload.")
}
this.registries.add(registry)
if (reloadable) {
this.reloadableRegistries.add(registry)
}
LOGGER.info("Registered the {} registry", registry.id.toString())
LOGGER.debug("Registered the {} registry of class {}", registry.id.toString(), registry::class.qualifiedName)
return registry
@@ -157,15 +160,9 @@ object CobblemonDataProvider : DataProvider {
private class SimpleResourceReloader(private val type: PackType) : ResourceManagerReloadListener {
override fun onResourceManagerReload(manager: ResourceManager) {
// Check for a server running, this is due to the create a world screen triggering datapack reloads, these are fine to happen as many times as needed as players may be in the process of adding their datapacks.
val isInGame = server() != null
if (isInGame && this.type == PackType.SERVER_DATA && !canReload) {
return
}
registries.filter { it.type == this.type }
val reloadAllowed = server()?.isReady != true
registries.filter { it.type == this.type && (reloadAllowed || it in reloadableRegistries) }
.forEach { it.reload(manager) }
if (isInGame && this.type == PackType.SERVER_DATA) {
canReload = false
}
}
}
}
@@ -38,7 +38,6 @@ class EntityCallbacks(val entity: Entity) : HashMap<ResourceLocation, MutableLis
companion object {
val HIT_BY_POKEBALL = cobblemonResource("hit_by_pokeball")
val SAW_ENTITY = cobblemonResource("saw_entity")
val LOST_ENTITY = cobblemonResource("lost_sight_of_entity")
val HURT = cobblemonResource("hurt")
val DIED = cobblemonResource("died")
}
@@ -21,7 +21,9 @@ import net.minecraft.world.level.material.FluidState
interface OmniPathingEntity {
fun canWalk(): Boolean
fun canSwimInWater(): Boolean
fun canWalkOnWater(): Boolean
fun canSwimInLava(): Boolean
fun canWalkOnLava(): Boolean
fun canSwimUnderFluid(fluidState: FluidState): Boolean
fun canFly(): Boolean
fun isFlying(): Boolean
@@ -49,7 +49,7 @@ enum class PoseType {
val UI_POSES = EnumSet.of(PROFILE, PORTRAIT)
val MOVING_POSES = EnumSet.of(WALK, SWIM, FLY)
val STATIONARY_POSES = EnumSet.of(STAND, FLOAT, HOVER)
val NO_GRAV_POSES = EnumSet.of(FLY, HOVER, FLOAT, SWIM)
val NO_GRAV_POSES = EnumSet.of(FLY, HOVER, SWIM)
fun identified() : List<ResourceLocation> {
return ALL_POSES.stream()
@@ -0,0 +1,125 @@
/*
* 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.entity.ai
import com.cobblemon.mod.common.entity.OmniPathingEntity
import net.minecraft.core.BlockPos
import net.minecraft.tags.FluidTags
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.util.GoalUtils
import net.minecraft.world.entity.ai.util.RandomPos
import net.minecraft.world.phys.Vec3
import java.util.*
import java.util.function.Supplier
import java.util.function.ToDoubleFunction
/*
*
* This is a rewrite of Minecraft's LandRandomPos tuned for
* water and lava walkers.
* The only real difference is movePosUpOutOfSolid() which
* moves the target pos out of liquids
*
* */
class CobblemonRandomSurfacePos {
companion object {
fun getPos(mob: PathfinderMob, radius: Int, verticalRange: Int): Vec3? {
Objects.requireNonNull(mob)
return getPos(
mob,
radius,
verticalRange,
{ pos: BlockPos? ->
if (pos != null) {
mob.getWalkTargetValue(pos).toDouble()
}
0.0
})
}
fun getPos(mob: PathfinderMob, radius: Int, yRange: Int, toDoubleFunction: ToDoubleFunction<BlockPos?>): Vec3? {
val bl = GoalUtils.mobRestricted(mob, radius)
return RandomPos.generateRandomPos(Supplier {
val blockPos = RandomPos.generateRandomDirection(mob.getRandom(), radius, yRange)
val blockPos2 = generateRandomPosTowardDirection(mob, radius, bl, blockPos)
if (blockPos2 == null) null else movePosUpOutOfSolid(mob, blockPos2)
}, toDoubleFunction)
}
fun getPosTowards(mob: PathfinderMob, radius: Int, yRange: Int, vectorPosition: Vec3): Vec3? {
val vec3 = vectorPosition.subtract(mob.getX(), mob.getY(), mob.getZ())
val bl = GoalUtils.mobRestricted(mob, radius)
return getPosInDirection(mob, radius, yRange, vec3, bl)
}
private fun getPosInDirection(
mob: PathfinderMob,
radius: Int,
yRange: Int,
vectorPosition: Vec3,
shortCircuit: Boolean
): Vec3? {
return RandomPos.generateRandomPos(mob, Supplier {
val blockPos: BlockPos? =
RandomPos.generateRandomDirectionWithinRadians(
mob.getRandom(),
radius,
yRange,
0,
vectorPosition.x,
vectorPosition.z,
(Math.PI.toFloat() / 2f).toDouble()
)
if (blockPos == null) {
return@Supplier null
} else {
val blockPos2: BlockPos? =
generateRandomPosTowardDirection(
mob,
radius,
shortCircuit,
blockPos
)
return@Supplier if (blockPos2 == null) null else movePosUpOutOfSolid(
mob,
blockPos2
)
}
})
}
fun generateRandomPosTowardDirection(
mob: PathfinderMob,
radius: Int,
shortCircuit: Boolean,
pos: BlockPos
): BlockPos? {
val blockPos = RandomPos.generateRandomPosTowardDirection(mob, radius, mob.getRandom(), pos)
return if (!GoalUtils.isOutsideLimits(blockPos, mob) && !GoalUtils.isRestricted(
shortCircuit,
mob,
blockPos
) && !GoalUtils.isNotStable(mob.getNavigation(), blockPos)
) blockPos else null
}
fun movePosUpOutOfSolid(mob: PathfinderMob, pos: BlockPos): BlockPos? {
var pos = pos
pos = RandomPos.moveUpOutOfSolid(
pos,
mob.level().maxBuildHeight
) { blockPos: BlockPos ->
if (GoalUtils.isSolid(mob, blockPos)) true
val fluidState = mob.level().getFluidState(blockPos)
!fluidState.isEmpty && (mob !is OmniPathingEntity || ((mob.canWalkOnWater() && fluidState.type == FluidTags.WATER) || (mob.canWalkOnLava() && fluidState.type == FluidTags.LAVA)))
}
return if (!GoalUtils.hasMalus(mob, pos)) pos else null
}
}
}
@@ -15,9 +15,11 @@ import com.google.common.collect.ImmutableMap
import java.util.UUID
import kotlin.math.pow
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.MoverType
import net.minecraft.world.entity.ai.behavior.Behavior
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.phys.Vec3
class FollowHerdLeaderTask : Behavior<PokemonEntity>(
@@ -67,8 +69,14 @@ class FollowHerdLeaderTask : Behavior<PokemonEntity>(
entity.brain.eraseMemory(CobblemonMemories.HERD_LEADER) // We can't go where this guy's going
return
}
entity.setFlying(true)
entity.addDeltaMovement(Vec3(0.0, 2.0, 0.0))
entity.brain.setMemory(
MemoryModuleType.WALK_TARGET,
CobblemonWalkTarget(
pos = leader.blockPosition(),
speedModifier = 0.3F,
completionRange = closeEnough.toInt()
)
)
} else if (leaderMoveControl.banking) {
if (leader.isInLiquid && !entity.isInLiquid) {
// We should get into water asap so we can mimic the banking
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.entity.ai
import com.cobblemon.mod.common.entity.OmniPathingEntity
import com.cobblemon.mod.common.pokemon.ai.OmniPathNodeMaker
import com.cobblemon.mod.common.util.deleteNode
import com.cobblemon.mod.common.util.getWaterAndLavaIn
import com.cobblemon.mod.common.util.toVec3d
import com.google.common.collect.ImmutableSet
@@ -27,7 +28,6 @@ import net.minecraft.world.level.pathfinder.Path
import net.minecraft.world.level.pathfinder.PathComputationType
import net.minecraft.world.level.pathfinder.PathFinder
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator
import net.minecraft.world.phys.Vec3
import kotlin.math.PI
import kotlin.math.abs
@@ -132,7 +132,7 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
val f = abs(mob.z - targetVec3d.z)
val closeEnough = d < maxDistanceToWaypoint.toDouble()
&& f < this.maxDistanceToWaypoint.toDouble()
&& e < (if (currentNode.type in verticallyPreciseNodeTypes) maxDistanceToWaypoint else 1.0).toDouble()
&& e < (if (currentNode.type in verticallyPreciseNodeTypes && (mob.isUnderWater || pather.isFlying())) 1.0 else 1.0).toDouble()
// Corner cutting is commented out because it makes pokemon and NPCs 'cut' the corner and fall into water or lava
if (closeEnough) {// || mob.navigation.canCutCorner(path!!.nextNode.type) && shouldTargetNextNodeInDirection(vec3d)) {
@@ -228,6 +228,25 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
return true
}
}
if (mob.canBreatheUnderwater()) {
val blockGetter: BlockGetter = level
if (blockGetter.getFluidState(pos).`is`(FluidTags.WATER)) {
val blockPos = pos.below()
return level.getBlockState(blockPos).isSolidRender(this.level, blockPos)
}
}
if (pather.canWalkOnWater()) {
val blockGetter: BlockGetter = level
if (blockGetter.getFluidState(pos.below()).`is`(FluidTags.WATER)) {
return !level.getBlockState(pos).isSolidRender(this.level, pos)
}
}
if (pather.canWalkOnLava()) {
val blockGetter: BlockGetter = level
if (blockGetter.getFluidState(pos.below()).`is`(FluidTags.LAVA)) {
return !level.getBlockState(pos).isSolidRender(this.level, pos)
}
}
if (pather.canFly()) {
return this.level.getBlockState(pos).isAir || super.isStableDestination(pos)
// Note the below is what is used by default for minecraft fliers
@@ -245,7 +264,7 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
// If we can fly and we're airborne, return the current Y position
return vec.y
}
return if ((canFloat()) && blockGetter.getFluidState(blockPos).`is`(FluidTags.WATER)) vec.y + 0.5 else WalkNodeEvaluator.getFloorLevel(blockGetter, blockPos)
return if ((canFloat()) && blockGetter.getFluidState(blockPos).`is`(FluidTags.WATER)) vec.y + 0.5 else super.getGroundY(vec)
}
override fun createPath(entity: Entity, distance: Int): Path? {
@@ -269,6 +288,28 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
override fun trimPath() {
super.trimPath()
val path = getPath() ?: return
// What's there to trim
if (path.nodeCount < 2) {
return
}
/*
* Sometimes entities spin in place at the start of their path
* because of rounding stuff (I think) so try to detect those
* cases and nix the first node so they just move forward.
*/
val introNode = path.getNode(0)
val subsequentNode = path.getNode(1)
val mobMiddle = entity.position()
val toIntroNode = introNode.asVec3().add(0.5, 0.0, 0.5).subtract(mobMiddle)
val toSubsequentNode = subsequentNode.asVec3().add(0.5, 0.0, 0.5).subtract(mobMiddle)
// if the first two nodes are pointing in opposite directions (>90 degrees) from the entity or the second node is closer, trim the first node
if (toIntroNode.dot(toSubsequentNode) < 0 || toIntroNode.lengthSqr() > toSubsequentNode.lengthSqr()) {
path.deleteNode(0)
}
var i = 2
// Tries to skip some nodes that are all lined up
@@ -20,7 +20,8 @@ class StayAfloatTask(private val chance: Float = 0F) : Behavior<PathfinderMob>(
ImmutableMap.of(CobblemonMemories.POKEMON_BATTLE, MemoryStatus.VALUE_ABSENT) // Prevents Illusion Pokemon from bouncing in the water when disguised as swimmers.
) {
override fun checkExtraStartConditions(world: ServerLevel, entity: PathfinderMob): Boolean {
return entity.isInWater && (entity.getFluidHeight(FluidTags.WATER) > entity.fluidJumpThreshold) || entity.isInLava
return entity.isInWater && (entity.getFluidHeight(FluidTags.WATER) > entity.fluidJumpThreshold) ||
(entity.isInLava && !entity.fireImmune())
}
override fun canStillUse(world: ServerLevel, entity: PathfinderMob, l: Long): Boolean {
@@ -644,20 +644,6 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
return InteractionResult.SUCCESS
}
override fun hurt(source: DamageSource, amount: Float): Boolean {
if (super.hurt(source, amount)) {
callbacks.process(
type = EntityCallbacks.HURT,
functions = mapOf(
"source" to { source.asMoLangValue() },
"amount" to { DoubleValue(amount.toDouble()) },
)
)
return true
}
return false
}
override fun recalling(pokemonEntity: PokemonEntity): CompletableFuture<Unit> {
playAnimation(RECALL_ANIMATION, pokemonExpressions(pokemonEntity.pokemon))
return delayedFuture(seconds = 1.6F)
@@ -710,10 +696,18 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
return false
}
override fun canWalkOnLava(): Boolean {
return false
}
override fun canSwimUnderFluid(fluidState: FluidState): Boolean {
return false
}
override fun canWalkOnWater(): Boolean {
return false
}
override fun canFly(): Boolean {
return false
}
@@ -9,9 +9,7 @@
package com.cobblemon.mod.common.entity.npc.ai
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.getMemorySafely
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.resolveInt
@@ -26,11 +24,9 @@ object MeleeAttackTask {
fun create(range: Expression, cooldownTicks: Expression): OneShot<LivingEntity> = BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.ATTACK_TARGET),
it.absent(MemoryModuleType.ATTACK_COOLING_DOWN),
it.absent(MemoryModuleType.ATTACK_COOLING_DOWN)
).apply(it) { attackTarget, cooldown ->
Trigger { world, entity, _ ->
val attackTargetData = entity.brain.getMemorySafely(CobblemonMemories.ATTACK_TARGET_DATA).orElse(null)
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val range = mainThreadRuntime.resolveFloat(range)
val cooldownTicks = mainThreadRuntime.resolveInt(cooldownTicks)
@@ -39,14 +35,6 @@ object MeleeAttackTask {
if (entity.distanceTo(attackTarget) <= range) {
entity.doHurtTarget(attackTarget)
cooldown.setWithExpiry(true, cooldownTicks.toLong())
if (attackTargetData != null) {
if (attackTargetData.dealtHitsUntilDisengage == 1) {
attackTargetData.dealtHitsUntilDisengage = 0
attackTargetData.disengageAction(entity)
} else if (attackTargetData.dealtHitsUntilDisengage > 0){
attackTargetData.dealtHitsUntilDisengage--
}
}
true
} else {
false
@@ -36,7 +36,6 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addLivingEntityFuncti
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonEntityFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.net.serializers.PlatformTypeDataSerializer
@@ -97,6 +96,7 @@ import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.server.behaviour.DamageOnCollisionPacket
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler.SEND_OUT_DURATION
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
@@ -124,6 +124,7 @@ import java.util.UUID
import java.util.concurrent.CompletableFuture
import kotlin.math.PI
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
import net.minecraft.core.BlockPos
import net.minecraft.core.component.DataComponents
@@ -175,6 +176,7 @@ import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.LightLayer
import net.minecraft.world.level.block.SuspiciousEffectHolder
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.level.material.EmptyFluid
import net.minecraft.world.level.material.FluidState
@@ -1221,14 +1223,6 @@ open class PokemonEntity(
// Only touch battle HP for non-wild Pokémon so that quick ball et. al. aren't owerpowered.
pokemon.currentHealth = (pokemon.maxHealth * (this.health / this.maxHealth)).toInt()
}
callbacks.process(
type = EntityCallbacks.HURT,
functions = mapOf(
"source" to { source.asMoLangValue() },
"amount" to { DoubleValue(amount.toDouble()) },
)
)
true
} else false
}
@@ -1804,7 +1798,7 @@ open class PokemonEntity(
for (passenger in this.passengers) {
passenger.deltaMovement = this.deltaMovement
}
this.ejectPassengers()
DismountPokemonPacket().sendToServer()
// Reset ride velocity
state.rideVelocity.set(state.rideVelocity.get().multiply(0.0, 1.0, 0.0))
@@ -1871,6 +1865,13 @@ open class PokemonEntity(
fun isFalling() =
this.fallDistance > 0 && this.level().getBlockState(this.blockPosition().below()).isAir && !this.isFlying()
override fun checkFallDamage(y: Double, onGround: Boolean, state: BlockState, pos: BlockPos) {
super.checkFallDamage(y, onGround, state, pos)
if (isFlying() && this.passengers.isEmpty() && y < 0.0 && !onGround) {
fallDistance = 0F // Prevent fall damage after flying without a rider
}
}
override fun getCurrentPoseType(): PoseType = this.entityData.get(POSE_TYPE)
/**
@@ -2043,10 +2044,6 @@ open class PokemonEntity(
}
override fun canBeLeashed() = true
// override fun canBeLeashedBy(player: Player): Boolean {
// return this.ownerUuid == null || this.ownerUuid == player.uuid
// }
override fun setLeashedTo(entity: Entity, bl: Boolean) {
super.setLeashedTo(entity, bl)
if (this.ownerUUID != null && this.ownerUUID != entity.uuid) {
@@ -2054,8 +2051,8 @@ open class PokemonEntity(
}
}
/** Retrieves the battle theme associated with this Pokemon's Species/Form, or the default PVW theme if not found. */
fun getBattleTheme() = this.form.battleTheme ?: CobblemonSounds.PVW_BATTLE.location
/** Retrieves the battle theme associated with this Pokemon's Species/Form which falls back to the default PVW theme if not found. */
fun getBattleTheme() = this.form.battleTheme
/**
* A utility method to instance a [Pokemon] aware if the [world] is client sided or not.
@@ -2069,22 +2066,23 @@ open class PokemonEntity(
}
// Takes in a requested stat type with a base minimum and base maximum and returns the interpolated
// stat based on the boost of that pokemons stat
fun getRideStat(stat: RidingStat, style: RidingStyle, baseMin: Double, baseMax: Double): Double {
//TODO: Change from static zero boost once aprijuice is implemented.
if (rideStatOverrides[style] != null && rideStatOverrides[style]!![stat] != null) {
return (((baseMax - baseMin) / 100) * rideStatOverrides[style]!![stat]!!) + baseMin
// stat based on the boost of that Pokémon's stat
fun getRideStat(rideStat: RidingStat, style: RidingStyle, baseMin: Double, baseMax: Double): Double {
if (rideStatOverrides[style] != null && rideStatOverrides[style]!![rideStat] != null) {
return (((baseMax - baseMin) / 100) * rideStatOverrides[style]!![rideStat]!!) + baseMin
}
val stat = this.rideProp.behaviours?.get(style)?.calculate(stat, 0) ?: return 0.0
val stat = this.rideProp.behaviours?.get(style)?.calculate(rideStat, entityData.get(RIDE_BOOSTS)[rideStat] ?: 0F) ?: return 0.0
val statVal = (((baseMax - baseMin) / 100) * stat) + baseMin
return statVal
// Cap the minimum value at a very small value to prevent control inversions and other unexpected behaviour in
// the ride controllers when using negative values
return max(statVal, 1e-6)
}
fun getRawRideStat(stat: RidingStat, style: RidingStyle): Double {
if (rideStatOverrides[style] != null && rideStatOverrides[style]!![stat] != null) {
return rideStatOverrides[style]!![stat]!!
}
return this.rideProp.behaviours?.get(style)?.calculate(stat, 0)?.toDouble() ?: 0.0
return this.rideProp.behaviours?.get(style)?.calculate(stat, 0F)?.toDouble() ?: 0.0
}
internal fun overrideRideStat(style: RidingStyle, stat: RidingStat, value: Double) {
@@ -2398,8 +2396,10 @@ open class PokemonEntity(
override fun canWalk() = exposedForm.behaviour.moving.walk.canWalk
override fun canSwimInWater() = exposedForm.behaviour.moving.swim.canSwimInWater
override fun canWalkOnWater() = exposedForm.behaviour.moving.swim.canWalkOnWater
override fun canFly() = exposedForm.behaviour.moving.fly.canFly
override fun canSwimInLava() = exposedForm.behaviour.moving.swim.canSwimInLava
override fun canWalkOnLava() = exposedForm.behaviour.moving.swim.canWalkOnLava
override fun entityOnGround() = onGround()
override fun canSwimUnderFluid(fluidState: FluidState): Boolean {
@@ -76,12 +76,14 @@ class PokemonServerDelegate : PokemonSideDelegate {
val moving = pokemon.form.behaviour.moving
entity.setPathfindingMalus(PathType.LAVA, if (moving.swim.canSwimInLava) 12F else -1F)
entity.setPathfindingMalus(PathType.WATER, if (moving.swim.canSwimInWater) 12F else -1F)
entity.setPathfindingMalus(PathType.WATER_BORDER, if (moving.swim.canSwimInWater) 6F else -1F)
entity.setPathfindingMalus(PathType.WATER_BORDER, if (moving.swim.canSwimInWater || moving.walk.avoidsLand || moving.swim.canBreatheUnderwater) 6F else -1F)
if (moving.swim.canBreatheUnderwater) {
entity.setPathfindingMalus(PathType.WATER, if (moving.walk.avoidsLand) 0F else 4F)
// Must have a malus of zero to be a valid wander target
entity.setPathfindingMalus(PathType.WATER, 0F)
}
if (moving.swim.canBreatheUnderlava) {
entity.setPathfindingMalus(PathType.LAVA, if (moving.swim.canSwimInLava) 4F else -1F)
// Must have a malus of zero to be a valid wander target
entity.setPathfindingMalus(PathType.LAVA, if (moving.swim.canSwimInLava) 0F else -1F)
}
if (moving.walk.avoidsLand) {
entity.setPathfindingMalus(PathType.WALKABLE, 12F)
@@ -1,26 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.components
import com.mojang.serialization.Codec
import io.netty.buffer.ByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
data class HeldItemCapableComponent(val item: ResourceLocation) {
companion object {
val CODEC: Codec<HeldItemCapableComponent> = ResourceLocation.CODEC.xmap(
{ HeldItemCapableComponent(it) },
{ it.item }
)
val PACKET_CODEC: StreamCodec<ByteBuf, HeldItemCapableComponent> = ByteBufCodecs.fromCodec(CODEC)
}
}
@@ -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.item.components
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import io.netty.buffer.ByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
data class HeldItemEffectComponent(val showdownId: String, val consumed: Boolean = false) {
companion object {
val CODEC: Codec<HeldItemEffectComponent> = RecordCodecBuilder.create { builder ->
builder.group(
Codec.STRING.fieldOf("showdownId").forGetter { it.showdownId },
Codec.BOOL.fieldOf("consumed").forGetter { it.consumed },
).apply(builder, ::HeldItemEffectComponent)
}
val PACKET_CODEC: StreamCodec<ByteBuf, HeldItemEffectComponent> = ByteBufCodecs.fromCodec(CODEC)
}
override fun toString(): String { return showdownId }
override fun equals(other: Any?): Boolean =
other is HeldItemEffectComponent
&& this.showdownId == other.showdownId
&& this.consumed == other.consumed
override fun hashCode(): Int {
var result = consumed.hashCode()
result = 31 * result + showdownId.hashCode()
return result
}
}
@@ -1,24 +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.net.messages.client.data
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
internal class UnlockReloadPacket : NetworkPacket<UnlockReloadPacket> {
companion object {
val ID = cobblemonResource("unlock_reload")
fun decode(buffer: RegistryFriendlyByteBuf) = UnlockReloadPacket()
}
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.mark.Marks
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PlatformType
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -26,31 +27,32 @@ import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
import net.minecraft.resources.ResourceLocation
class SpawnPokemonPacket(
var ownerId: UUID?,
var pokemonUUID: UUID,
var scaleModifier: Float,
var speciesId: ResourceLocation,
var gender: Gender,
var shiny: Boolean,
var formName: String,
var aspects: Set<String>,
var battleId: UUID?,
var phasingTargetId: Int,
var beamMode: Byte,
var platform: PlatformType,
var nickname: MutableComponent?,
var mark: ResourceLocation?,
var labelLevel: Int,
var poseType: PoseType,
var unbattlable: Boolean,
var hideLabel: Boolean,
var caughtBall: ResourceLocation,
var spawnYaw: Float,
var friendship: Int,
var freezeFrame: Float,
var passengers: IntArray,
var tickSpawned: Int,
vanillaSpawnPacket: ClientboundAddEntityPacket,
var ownerId: UUID?,
var pokemonUUID: UUID,
var scaleModifier: Float,
var speciesId: ResourceLocation,
var gender: Gender,
var shiny: Boolean,
var formName: String,
var aspects: Set<String>,
var battleId: UUID?,
var phasingTargetId: Int,
var beamMode: Byte,
var platform: PlatformType,
var nickname: MutableComponent?,
var mark: ResourceLocation?,
var labelLevel: Int,
var poseType: PoseType,
var unbattlable: Boolean,
var hideLabel: Boolean,
var caughtBall: ResourceLocation,
var spawnYaw: Float,
var friendship: Int,
var freezeFrame: Float,
var passengers: IntArray,
var tickSpawned: Int,
var rideBoosts: Map<RidingStat, Float>,
vanillaSpawnPacket: ClientboundAddEntityPacket,
) : SpawnExtraDataEntityPacket<SpawnPokemonPacket, PokemonEntity>(vanillaSpawnPacket) {
override val id: ResourceLocation = ID
@@ -80,6 +82,7 @@ class SpawnPokemonPacket(
entity.entityData.get(PokemonEntity.FREEZE_FRAME),
entity.passengers.map { it.id }.toIntArray(),
entity.tickCount,
entity.entityData.get(PokemonEntity.RIDE_BOOSTS),
vanillaSpawnPacket
)
@@ -108,6 +111,11 @@ class SpawnPokemonPacket(
buffer.writeFloat(this.freezeFrame)
buffer.writeVarIntArray(this.passengers)
buffer.writeInt(this.tickSpawned)
buffer.writeMap(
rideBoosts,
{ _, stat -> buffer.writeEnumConstant(stat) },
{ _, value -> buffer.writeFloat(value) }
)
}
override fun applyData(entity: PokemonEntity, level: ClientLevel) {
@@ -137,6 +145,7 @@ class SpawnPokemonPacket(
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, spawnYaw)
entity.entityData.set(PokemonEntity.FRIENDSHIP, friendship)
entity.entityData.set(PokemonEntity.FREEZE_FRAME, freezeFrame)
entity.entityData.set(PokemonEntity.RIDE_BOOSTS, rideBoosts)
entity.ejectPassengers()
passengers.forEach {
@@ -176,9 +185,13 @@ class SpawnPokemonPacket(
val freezeFrame = buffer.readFloat()
val passengers = buffer.readVarIntArray()
val tickSpawned = buffer.readInt()
val rideBoosts = buffer.readMap(
{ buffer.readEnumConstant(RidingStat::class.java) },
{ buffer.readFloat() }
)
val vanillaPacket = decodeVanillaPacket(buffer)
return SpawnPokemonPacket(ownerId, pokemonUUID, scaleModifier, speciesId, gender, shiny, formName, aspects, battleId, phasingTargetId, beamModeEmitter, platform, nickname, mark, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, friendship, freezeFrame, passengers, tickSpawned, vanillaPacket)
return SpawnPokemonPacket(ownerId, pokemonUUID, scaleModifier, speciesId, gender, shiny, formName, aspects, battleId, phasingTargetId, beamModeEmitter, platform, nickname, mark, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, friendship, freezeFrame, passengers, tickSpawned, rideBoosts, vanillaPacket)
}
}
@@ -58,6 +58,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.reactive.SettableObservable
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.storage.StoreCoordinates
@@ -1821,8 +1822,10 @@ open class Pokemon : ShowdownIdentifiable {
moveSet.update()
}
fun getMaxRideBoost(stat: RidingStat): Int {
return form.riding.behaviours?.maxOf { it.value.stats[stat]?.endInclusive ?: 0 } ?: 0
fun getMaxRideBoost(stat: RidingStat): Float {
val behaviours = form.riding.behaviours ?: return 0F
// Get the widest range for this stat, max - min, since that's how far it can be boosted in theory.
return behaviours.values.maxOfOrNull { it.stats[stat]?.let { it.last - it.first }?.toFloat() ?: 0F } ?: 0F
}
fun getRideBoost(stat: RidingStat): Float {
@@ -1833,31 +1836,36 @@ open class Pokemon : ShowdownIdentifiable {
return rideBoosts.toMap()
}
fun canAddRideBoost(stat: RidingStat): Boolean {
val max = getMaxRideBoost(stat)
val current = rideBoosts[stat] ?: 0F
return current < max
fun getRideStat(style: RidingStyle, stat: RidingStat): Float {
form.riding.behaviours?.let {
return it[style]?.calculate(stat, getRideBoost(stat)) ?: 0F
}
return 0F
}
fun addRideBoost(stat: RidingStat, boost: Float): Boolean {
fun canAddRideBoost(stat: RidingStat): Boolean {
val current = rideBoosts[stat] ?: 0F
return current < getMaxRideBoost(stat)
}
fun addRideBoost(stat: RidingStat, boostAmount: Float): Boolean {
if (!canAddRideBoost(stat)) {
return false
}
val max = getMaxRideBoost(stat)
rideBoosts[stat] = (getRideBoost(stat) + boost).coerceIn(0F, max.toFloat())
rideBoosts[stat] = (getRideBoost(stat) + boostAmount).coerceAtMost(max)
onChange(RideBoostsUpdatePacket({ this }, rideBoosts))
return true
}
fun setRideBoost(stat: RidingStat, boost: Float) {
val max = getMaxRideBoost(stat)
rideBoosts[stat] = boost.coerceIn(0F, max.toFloat())
rideBoosts[stat] = boost.coerceIn(0F, getMaxRideBoost(stat))
onChange(RideBoostsUpdatePacket({ this }, rideBoosts))
}
fun setRideBoosts(boosts: Map<RidingStat, Float>) {
rideBoosts.clear()
rideBoosts.putAll(boosts.mapValues { it.value.coerceIn(0F, getMaxRideBoost(it.key).toFloat()) })
rideBoosts.putAll(boosts.mapValues { it.value.coerceIn(0F, getMaxRideBoost(it.key)) })
onChange(RideBoostsUpdatePacket({ this }, rideBoosts))
}
@@ -1877,7 +1885,7 @@ open class Pokemon : ShowdownIdentifiable {
if (result.experienceAdded <= 0) {
return result
}
player.sendSystemMessage(lang("experience.gained", getDisplayName(), xp), true)
player.sendSystemMessage(lang("experience.gained", getDisplayName(), result.experienceAdded), true)
if (result.oldLevel != result.newLevel) {
player.sendSystemMessage(lang("experience.level_up", getDisplayName(), result.newLevel))
val repeats = result.newLevel - result.oldLevel
@@ -67,12 +67,12 @@ class FormPokemonBehaviour {
val entityInteract: EntityBehaviour
get() = _entityInteract ?: parent.entityInteract
val blockInteract: BlockBehavior
get() = _blockInteract ?: parent.blockInteract
val dampensVibrations: Boolean
get() = _dampensVibrations ?: parent.dampensVibrations
val blockInteract: BlockBehavior
get() = _blockInteract ?: parent.blockInteract
val combat: CombatBehaviour
get() = _combat ?: parent.combat
@@ -35,6 +35,5 @@ class MoveBehaviour {
it.addFunction("wander_chance") { DoubleValue(wanderChance) }
it.addFunction("wander_speed") { DoubleValue(wanderSpeed) }
it.addFunction("can_look") { DoubleValue(canLook) }
it.addFunction("can_move") { DoubleValue(walk.canWalk || swim.canSwimInLava || swim.canSwimInWater || fly.canFly) }
}
}
@@ -417,7 +417,11 @@ class OmniPathNodeMaker : NodeEvaluator() {
PathType.FENCE
}
} else if (isWater && !canSwimInWater() && canBreatheUnderFluid && blockState.isPathfindable(PathComputationType.LAND) ) {
PathType.OPEN
if (pfContext.getPathTypeFromState(pos.x, pos.y - 1, pos.z) == PathType.BLOCKED) {
PathType.WALKABLE
} else {
PathType.OPEN
}
} else if (isLava && canSwimInLava()) {
PathType.LAVA
} else if (isWater) {
@@ -440,12 +444,13 @@ class OmniPathNodeMaker : NodeEvaluator() {
if (figuredNode == PathType.OPEN && pos.y >= pfContext.level().getMinBuildHeight() + 1) {
val var10000: PathType = when (pfContext.getPathTypeFromState(pos.x, pos.y - 1, pos.z)) {
PathType.OPEN, PathType.WATER, PathType.LAVA, PathType.WALKABLE -> PathType.OPEN
PathType.OPEN, PathType.LAVA, PathType.WALKABLE -> PathType.OPEN
PathType.DAMAGE_OTHER -> PathType.DAMAGE_OTHER
PathType.STICKY_HONEY -> PathType.STICKY_HONEY
PathType.POWDER_SNOW -> PathType.DANGER_POWDER_SNOW
PathType.DAMAGE_CAUTIOUS -> PathType.DAMAGE_CAUTIOUS
PathType.TRAPDOOR -> PathType.DANGER_TRAPDOOR
PathType.WATER -> if (canWalkOnWater()) PathType.WALKABLE else PathType.OPEN
PathType.FENCE -> {
if (canFly())
PathType.BLOCKED
@@ -605,6 +610,14 @@ class OmniPathNodeMaker : NodeEvaluator() {
}
}
fun canWalkOnWater(): Boolean {
return if (this.mob is OmniPathingEntity) {
(this.mob as OmniPathingEntity).canWalkOnWater()
} else {
false
}
}
fun canFly(): Boolean {
return if (this.mob is OmniPathingEntity) {
(this.mob as OmniPathingEntity).canFly()
@@ -10,9 +10,9 @@ package com.cobblemon.mod.common.pokemon.helditem
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.api.item.HeldItems
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.google.common.collect.HashBiMap
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.chat.Component
@@ -32,17 +32,11 @@ abstract class BaseCobblemonHeldItemManager : HeldItemManager {
internal open fun load() {
this.itemIds.clear()
val itemsJson = ShowdownService.service.getRegistryData("heldItem")
val showdownIds = hashSetOf<String>()
for (i in 0 until itemsJson.size()) {
val jsItem = itemsJson[i].asJsonObject
showdownIds += jsItem.get("id").asString
}
BuiltInRegistries.ITEM.forEach { item ->
val identifier = BuiltInRegistries.ITEM.getKey(item)
if (identifier.namespace == Cobblemon.MODID) {
val formattedPath = identifier.path.replace("_", "")
if (showdownIds.contains(formattedPath)) {
if (HeldItems.showdownItems.contains(formattedPath)) {
this.itemIds[formattedPath] = item
}
}
@@ -51,9 +45,9 @@ abstract class BaseCobblemonHeldItemManager : HeldItemManager {
override fun showdownId(pokemon: BattlePokemon): String? {
val item = pokemon.effectedPokemon.heldItemNoCopy()
val identifier = item.get(CobblemonItemComponents.HELD_ITEM_REP)?.item ?: BuiltInRegistries.ITEM.getKey(item.item)
val identifier = item.get(CobblemonItemComponents.HELD_ITEM_EFFECT)?.showdownId ?: BuiltInRegistries.ITEM.getKey(item.item).path
val formattedPath = identifier.path.replace("_", "")
val formattedPath = identifier.replace("_", "")
if (this.itemIds.containsKey(formattedPath)) {
return formattedPath
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.pokemon.helditem
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItemComponents.HELD_ITEM_EFFECT
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
@@ -60,9 +61,12 @@ object CobblemonHeldItemManager : BaseCobblemonHeldItemManager() {
override fun showdownId(pokemon: BattlePokemon): String? {
val itemStack = pokemon.effectedPokemon.heldItemNoCopy()
if (remaps.containsKey(itemStack.item)) {
if (itemStack.has(HELD_ITEM_EFFECT))
return itemStack.get(HELD_ITEM_EFFECT)!!.showdownId
if (remaps.containsKey(itemStack.item))
return remaps[itemStack.item]
}
for (remap in stackRemaps) {
val id = remap.apply(itemStack)
@@ -80,9 +84,11 @@ object CobblemonHeldItemManager : BaseCobblemonHeldItemManager() {
}
fun showdownId(itemStack: ItemStack): String? {
if (remaps.containsKey(itemStack.item)) {
if (itemStack.has(HELD_ITEM_EFFECT))
return itemStack.get(HELD_ITEM_EFFECT)?.showdownId
if (remaps.containsKey(itemStack.item))
return remaps[itemStack.item]
}
for (remap in stackRemaps) {
val id = remap.apply(itemStack)
@@ -173,13 +179,18 @@ object CobblemonHeldItemManager : BaseCobblemonHeldItemManager() {
}
override fun shouldConsumeItem(pokemon: BattlePokemon, battle: PokemonBattle, showdownId: String): Boolean {
val itemStack = pokemon.effectedPokemon.heldItem()
if (itemStack.has(HELD_ITEM_EFFECT))
return itemStack.get(HELD_ITEM_EFFECT)!!.consumed
// In 3rd party and the future battles might have multiple types, give it a priority from pvp down to wild.
val tag = when {
battle.isPvP -> CobblemonItemTags.CONSUMED_IN_PVP_BATTLE
battle.isPvN -> CobblemonItemTags.CONSUMED_IN_NPC_BATTLE
else -> CobblemonItemTags.CONSUMED_IN_WILD_BATTLE
}
return pokemon.effectedPokemon.heldItem().`is`(tag)
return itemStack.`is`(tag)
}
/**
@@ -0,0 +1,49 @@
{
"format_version": "1.8.0",
"animations": {
"animation.player.test_riding_pose": {
"loop": "hold_on_last_frame",
"bones": {
"body": {
"rotation": [22.5, 0, 0],
"position": [0, 6, 0]
},
"arm_right": {
"rotation": [-145.18392, -14.90835, "-20.3017894552"]
},
"leg_right": {
"rotation": [47.48933, -18.97397, 16.56855]
},
"arm_left": {
"rotation": [-145.18392, -14.90835, 20.30179]
},
"leg_left": {
"rotation": [48.65802, 13.31791, -11.45696]
}
}
},
"animation.player.default_ride_pose": {
"loop": "hold_on_last_frame",
"bones": {
"body": {
"position": [0, -10, 0]
},
"head": {
"rotation": ["v.head_pitch", "v.head_yaw", 0]
},
"arm_right": {
"rotation": [-40, 0, 0]
},
"arm_left": {
"rotation": [-40, 0, 0]
},
"leg_right": {
"rotation": [-81, 18, 4.5]
},
"leg_left": {
"rotation": [-81, -18, -4.5]
}
}
}
}
}
@@ -5,9 +5,10 @@
"loop": true,
"bones": {
"body": {
"position": [0, -9, 0]
"position": [0, -9, 0],
"rotation": [25, 0, 0]
},
"torso": {
"_torso_disabled_because_it_cannot_be_reparented": {
"rotation": [25, 0, 0]
},
"arm_right": {
@@ -1,79 +1,15 @@
{
"format_version": "1.8.0",
"animations": {
"animation.dewgong.pose": {
"loop": true,
"bones": {
"body": {
"rotation": [0, 12.5, 0]
},
"tail": {
"rotation": [2.41492, 0.64686, -14.98637]
},
"tail2": {
"rotation": [34.97911, 1.05627, -2.26603]
},
"tail3": {
"rotation": [46.78082, -13.99545, -14.43276]
},
"tail4": {
"rotation": [7.5, 0, 0]
},
"tail6": {
"rotation": [30, 0, 0]
},
"chest": {
"rotation": [-32.5, 0, 0]
},
"neck": {
"rotation": [-52.52635, -1.5216, 1.98385]
},
"head": {
"rotation": [83.46987, -8.35694, -6.53013],
"position": [0.58854, -0.68263, -0.01172]
},
"flipper_left": {
"rotation": [32.28184, -14.8934, 25.70507],
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
"rotation": [0, -15, -15]
},
"flipper_left3": {
"rotation": [0, 0, -37.5]
},
"flipper_right": {
"rotation": [99.40658, 28.75123, -7.23212],
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
"rotation": [0, 0, 15]
},
"flipper_right3": {
"rotation": [0, 0, 37.5]
},
"tail5": {
"rotation": [27.5, 0, 0]
},
"eye_right": {
"position": [0.25, 0, 0]
},
"eye_highlight_left": {
"position": [0.25, 0, 0]
},
"eye_highlight_right": {
"position": [0.25, 0, 0]
}
}
},
"animation.dewgong.ground_idle": {
"loop": true,
"bones": {
"body": {
"rotation": [-5, 0, 0]
"rotation": ["-5+Math.cos((query.anim_time) * 90 * 2) * 1", 0, 0],
"position": [0, 0, "0+Math.cos((query.anim_time) * 90 * 2) * -0.3"]
},
"tail": {
"rotation": [-2.5, 0, 0]
"rotation": ["-2.5+Math.cos((query.anim_time) * 90 * 2) * -1", 0, 0]
},
"tail2": {
"rotation": [0, 0, 0]
@@ -88,16 +24,16 @@
"rotation": [5, 0, 0]
},
"chest": {
"rotation": [-32.5, 0, 0]
"rotation": ["-32.5+Math.cos((query.anim_time) * 90 * 2 - 45) * 0.5", 0, 0]
},
"neck": {
"rotation": [-50, 0, 0]
"rotation": ["-50+Math.cos((query.anim_time) * 90 * 2) * -1", 0, 0]
},
"head": {
"rotation": [85, 0, 0]
"rotation": ["85+Math.cos((query.anim_time) * 90 * 2 + 45) * 1", 0, 0]
},
"flipper_left": {
"rotation": [25.90134, -21.60983, 48.92416],
"rotation": [25.90134, -21.60983, "51.4242+Math.cos((query.anim_time) * 90 * 2) * -1"],
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
@@ -107,7 +43,7 @@
"rotation": [0, 0, -37.5]
},
"flipper_right": {
"rotation": [25.90134, 21.60983, -48.92416],
"rotation": [25.90134, 21.60983, "-51.4242+Math.cos((query.anim_time) * 90 * 2) * 1"],
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
@@ -117,7 +53,507 @@
"rotation": [0, 0, 37.5]
},
"torso": {
"rotation": [5, 0, 0]
"rotation": ["5+Math.cos((query.anim_time) * 90 * 2) * -0.5", 0, 0]
}
}
},
"animation.dewgong.ground_walk": {
"loop": true,
"animation_length": 1,
"bones": {
"body": {
"rotation": {
"0.0": [-5, 0, 0],
"0.1667": [-0.495, 0, 0],
"0.25": [0, 0, 0],
"0.3333": [-0.5, 0, 0],
"0.5": [-4.795, 0, 0],
"0.5417": [-5.4, 0, 0],
"0.6667": [-5, 0, 0]
},
"position": {
"0.0": [0, 0, "0+Math.cos((query.anim_time) * 90 * 4) * 3"],
"0.0833": [0, 0, "0+Math.cos((query.anim_time) * 90 * 4) * 3"],
"0.2083": [0, 1.36, "0+Math.cos((query.anim_time) * 90 * 4) * 3"],
"0.2917": [0, 1.845, "0+Math.cos((query.anim_time) * 90 * 4) * 3"],
"0.3333": [0, 2, "0+Math.cos((query.anim_time) * 90 * 4) * 3"],
"0.375": [0, 1.845, "0+Math.cos((query.anim_time) * 90 * 4) * 3"],
"0.4583": [0, 1.36, "0+Math.cos((query.anim_time) * 90 * 4) * 3"],
"0.5833": [0, 0, "0+Math.cos((query.anim_time) * 90 * 4) * 3"]
}
},
"tail": {
"rotation": {
"0.0": [5.775, 0, 0],
"0.1667": [-13.44, 0, 0],
"0.2083": [-15, 0, 0],
"0.5": [0, 0, 0],
"0.875": [7.5, 0, 0],
"1.0": [5.775, 0, 0]
}
},
"tail2": {
"rotation": {
"0.0": [15, 0, 0],
"0.1667": [0, 0, 0],
"0.5417": [0, 0, 0],
"0.6667": [-7.5, 0, 0],
"0.8333": [12.46, 0, 0],
"0.9167": [17.42, 0, 0],
"1.0": [15, 0, 0]
}
},
"tail3": {
"rotation": {
"0.0": [22.5, 0, 0],
"0.125": [34.5, 0, 0],
"0.2083": [17.5, 0, 0],
"0.3333": [-5, 0, 0],
"0.5417": [-5, 0, 0],
"0.6667": [0, 0, 0],
"0.75": [-22.5, 0, 0],
"1.0": [22.5, 0, 0]
}
},
"tail4": {
"rotation": {
"0.0": [25, 0, 0],
"0.125": [17.5, 0, 0],
"0.25": [-5.45, 0, 0],
"0.2917": [2.5, 0, 0],
"0.7083": [2.5, 0, 0],
"0.75": [-1.67, 0, 0],
"0.8333": [-25, 0, 0],
"1.0": [25, 0, 0]
}
},
"tail6": {
"rotation": {
"0.0": [-42.5, 0, 0],
"0.1667": [44.17, 0, 0],
"0.25": [42.5, 0, 0],
"0.2917": [2.5, 0, 0],
"0.3333": [2.5, 0, 0],
"0.75": [2.5, 0, 0],
"0.7917": [20, 0, 0],
"0.8333": [40, 0, 0],
"0.875": [-40, 0, 0],
"1.0": [-42.5, 0, 0]
}
},
"chest": {
"rotation": {
"0.0": [-32.5, 0, 0],
"0.0833": {
"pre": [-26.79, 0, 0],
"post": [-26.79, 0, 0],
"lerp_mode": "catmullrom"
},
"0.1667": {
"post": [-22, 0, 0],
"lerp_mode": "catmullrom"
},
"0.3333": {
"post": [-18.575, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [-17.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [-21.6, 0, 0],
"lerp_mode": "catmullrom"
},
"0.6667": {
"post": [-29.47, 0, 0],
"lerp_mode": "catmullrom"
},
"0.7083": {
"post": [-33.695, 0, 0],
"lerp_mode": "catmullrom"
},
"0.7917": {
"post": [-38, 0, 0],
"lerp_mode": "catmullrom"
},
"0.875": {
"post": [-36.9, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0": [-32.5, 0, 0]
}
},
"neck": {
"rotation": {
"0.0": [-50, 0, 0],
"0.1667": [-40, 0, 0],
"0.2917": [-37.575, 0, 0],
"0.5833": [-36.815, 0, 0],
"0.7083": [-39.345, 0, 0],
"0.8333": [-47.315, 0, 0],
"0.9167": [-49.585, 0, 0],
"1.0": [-50, 0, 0]
},
"position": {
"0.0": [0, 0, 0],
"0.1667": [0, 0, 1],
"0.6667": [0, 0, 1],
"1.0": [0, 0, 0]
}
},
"head": {
"rotation": {
"0.0": [85, 0, 0],
"0.1667": [62, 0, 0],
"0.2083": [60, 0, 0],
"0.4167": [55, 0, 0],
"0.5417": [57, 0, 0],
"0.7917": [79.5, 0, 0],
"0.9167": [84.175, 0, 0],
"1.0": [85, 0, 0]
}
},
"flipper_left": {
"rotation": {
"0.0": [70.90134, 0.89017, 78.92416],
"0.25": [17.15, -1.36, 58.92],
"0.2917": [13.11, -5.57, 54.92],
"0.4583": [-3.06, -22.4, 43.92],
"0.5": [-9.1, -26.61, 43.92],
"0.625": [16.53, -12.375, 38.315],
"0.6667": [22.57, -3.065, 36.275],
"0.75": [34.65, 4.64, 35.195],
"0.9167": [58.82, 6.785, 50.56],
"1.0": [70.90134, 0.89017, 78.92416]
},
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
"rotation": {
"0.0": [0, 0, -42.5],
"0.0417": [0, 0, -26.875],
"0.0833": [0, 0, -40],
"0.125": [0, 0, -48.13],
"0.1667": [0, 0, -51.25],
"0.25": [0, 0, -42.5],
"0.375": [0, 0, -29.25],
"0.5": [0, 0, -20],
"0.5417": [0, 0, -18.75],
"0.5833": [0, 0, -32.5],
"0.7917": [0, 0, -42.5],
"1.0": [0, 0, -42.5]
}
},
"flipper_left3": {
"rotation": {
"0.0": [0, 0, -70],
"0.0833": [0, 0, -22.5],
"0.1667": [0, 0, -11.67],
"0.2083": [0, 0, -16.25],
"0.5": [0, 0, -25],
"0.5417": [0, 0, -25.83],
"0.625": [0, 0, 20],
"0.7917": [0, 0, -47.5],
"1.0": [0, 0, -70]
}
},
"flipper_right": {
"rotation": {
"0.0": [70.90134, -0.89017, -78.92416],
"0.25": [17.15, 1.36, -58.92],
"0.2917": [13.11, 5.57, -54.92],
"0.4583": [-3.06, 22.4, -43.92],
"0.5": [-9.1, 26.61, -43.92],
"0.625": [16.53, 12.375, -38.315],
"0.6667": [22.57, 3.065, -36.275],
"0.75": [34.65, -4.64, -35.195],
"0.9167": [58.82, -6.785, -50.56],
"1.0": [70.90134, -0.89017, -78.92416]
},
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
"rotation": {
"0.0": [0, 0, 42.5],
"0.0417": [0, 0, 26.875],
"0.0833": [0, 0, 40],
"0.125": [0, 0, 48.13],
"0.1667": [0, 0, 51.25],
"0.25": [0, 0, 42.5],
"0.375": [0, 0, 29.25],
"0.5": [0, 0, 20],
"0.5417": [0, 0, 18.75],
"0.5833": [0, 0, 32.5],
"0.7917": [0, 0, 42.5],
"1.0": [0, 0, 42.5]
}
},
"flipper_right3": {
"rotation": {
"0.0": [0, 0, 70],
"0.0833": [0, 0, 22.5],
"0.1667": [0, 0, 11.67],
"0.2083": [0, 0, 16.25],
"0.5": [0, 0, 25],
"0.5417": [0, 0, 25.83],
"0.625": [0, 0, -20],
"0.7917": [0, 0, 47.5],
"1.0": [0, 0, 70]
}
},
"torso": {
"rotation": {
"0.0": [4.6, 0, 0],
"0.0417": [6.03, 0, 0],
"0.125": [8.44, 0, 0],
"0.2917": {
"pre": [9.6, 0, 0],
"post": [9.6, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [8.35, 0, 0],
"lerp_mode": "catmullrom"
},
"0.7917": {
"post": [-1.9, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9583": [3.13, 0, 0],
"1.0": [4.6, 0, 0]
}
},
"tail5": {
"rotation": {
"0.0": [-6.95, 0, 0],
"0.0833": [23.83, 0, 0],
"0.2083": [32.5, 0, 0],
"0.2917": [0, 0, 0],
"0.3333": [7.5, 0, 0],
"0.5417": [5, 0, 0],
"0.6667": [5, 0, 0],
"0.7083": [10, 0, 0],
"0.75": [22.5, 0, 0],
"0.7917": [1.25, 0, 0],
"0.8333": [-22.5, 0, 0],
"1.0": [-6.95, 0, 0]
}
},
"jaw": {
"rotation": {
"0.0833": [0, 0, 0],
"0.2083": [25.75, 0, 0],
"0.2917": [32.24, 0, 0],
"0.4583": [32.5, 0, 0],
"0.7083": [0, 0, 0]
}
}
}
},
"animation.dewgong.water_idle": {
"loop": true,
"bones": {
"body": {
"rotation": ["-5+Math.cos((query.anim_time) * 90 * 2 - 90) * -1", 0, 0],
"position": [0, "0+Math.cos((query.anim_time) * 90 * 2) * 1", 0]
},
"tail": {
"rotation": ["-2.5+Math.cos((query.anim_time) * 90 * 2 - 150) * 2", 0, 0]
},
"tail2": {
"rotation": ["-5+Math.cos((query.anim_time) * 90 * 2 - 210) * 2", 0, 0]
},
"tail3": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 2 - 270) * 3", 0, 0]
},
"tail4": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 2 - 300) * 3", 0, 0]
},
"tail6": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 2 - 450) * 12", 0, 0]
},
"chest": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 2 - 150) * -1", 0, 0],
"position": [0, 0, 0.25]
},
"neck": {
"rotation": ["-50+Math.cos((query.anim_time) * 90 * 2 - 90) * 1", 0, 0]
},
"head": {
"rotation": ["52.5+Math.cos((query.anim_time) * 90 * 2 - 90) * 1", 0, 0],
"position": [0, 0, 1]
},
"flipper_left": {
"rotation": [0, -12.5, "12.5+Math.cos((query.anim_time) * 90 * 2 - 90) * -4"],
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
"rotation": [0, 0, "10+Math.cos((query.anim_time) * 90 * 2 - 150) * -7"]
},
"flipper_left3": {
"rotation": [0, 0, "10+Math.cos((query.anim_time) * 90 * 2 - 210) * -7"]
},
"flipper_right": {
"rotation": [0, 12.5, "-12.5+Math.cos((query.anim_time) * 90 * 2 - 90) * 4"],
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
"rotation": [0, 0, "-10+Math.cos((query.anim_time) * 90 * 2 - 150) * 7"]
},
"flipper_right3": {
"rotation": [0, 0, "-10+Math.cos((query.anim_time) * 90 * 2 - 210) * 7"]
},
"torso": {
"rotation": [2.5, 0, 0]
},
"tail5": {
"rotation": ["7.5+Math.cos((query.anim_time) * 90 * 2 - 390) * 8", 0, 0]
}
}
},
"animation.dewgong.water_swim": {
"loop": true,
"animation_length": 0.75,
"bones": {
"body": {
"rotation": ["-5+Math.cos((query.anim_time) * 90 * 16/3 - 45) * 2", 0, 0],
"position": [0, "0+Math.cos((query.anim_time) * 90 * 16/3 + 45) * 2", "0+Math.cos((query.anim_time) * 90 * 16/3) * 3"]
},
"tail": {
"rotation": ["-2.5+Math.cos((query.anim_time) * 90 * 16/3 - 90) * 6", 0, 0]
},
"tail2": {
"rotation": ["-5+Math.cos((query.anim_time) * 90 * 16/3 - 135) * 6", 0, 0]
},
"tail3": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 16/3 - 180) * 6", 0, 0]
},
"tail4": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 16/3 - 200) * 16", 0, 0]
},
"tail6": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 16/3 - 350) * 26", 0, 0]
},
"chest": {
"rotation": ["5+Math.cos((query.anim_time) * 90 * 16/3 + 45) * 1", 0, 0],
"position": [0, 0, 0.25]
},
"neck": {
"rotation": ["-50+Math.cos((query.anim_time) * 90 * 16/3 + 90) * -2", 0, 0]
},
"head": {
"rotation": ["47.5+Math.cos((query.anim_time) * 90 * 16/3 - 45) * -6", 0, 0],
"position": [0, 0, 1]
},
"flipper_left": {
"rotation": {
"0.0": [37.25, 50.65, 0],
"0.0417": [37.25, 46.72, 1.1],
"0.375": [37.25, -45.255, 12.5],
"0.4167": [37.25, -47.5, 12.5],
"0.4583": [39.9, -45.255, 12.5],
"0.5417": [52.825, -13.75, 8.97],
"0.5833": [55.5, -0.315, 7.2],
"0.625": [52.325, 16.325, 5.03],
"0.7083": [38.4, 46.72, 0.7],
"0.75": [37.25, 50.65, 0]
},
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
"rotation": {
"0.0": [0, 0, 0],
"0.125": [0, 0, -10],
"0.2917": [0, 0, -0.76],
"0.4167": [0, 0, 24.75],
"0.4583": [0, 0, 37.5],
"0.625": [0, 0, 64.5],
"0.75": [0, 0, 0]
}
},
"flipper_left3": {
"rotation": {
"0.0": [0, 0, 10],
"0.125": [0, 0, -7.5],
"0.375": [0, 0, -9.37],
"0.4583": [0, 0, 20],
"0.625": [0, 0, 35],
"0.75": [0, 0, 10]
}
},
"flipper_right": {
"rotation": {
"0.0": [37.25, -50.65, 0],
"0.0417": [37.25, -46.72, -1.1],
"0.375": [37.25, 45.255, -12.5],
"0.4167": [37.25, 47.5, -12.5],
"0.4583": [39.9, 45.255, -12.5],
"0.5417": [52.825, 13.75, -8.97],
"0.5833": [55.5, 0.315, -7.2],
"0.625": [52.325, -16.325, -5.03],
"0.7083": [38.4, -46.72, -0.7],
"0.75": [37.25, -50.65, 0]
},
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
"rotation": {
"0.0": [0, 0, 0],
"0.125": [0, 0, 10],
"0.2917": [0, 0, 0.76],
"0.4167": [0, 0, -24.75],
"0.4583": [0, 0, -37.5],
"0.625": [0, 0, -64.5],
"0.75": [0, 0, 0]
}
},
"flipper_right3": {
"rotation": {
"0.0": [0, 0, -30],
"0.0833": [0, 0, -3.17],
"0.125": [0, 0, 7.5],
"0.375": [0, 0, 9.37],
"0.4583": [0, 0, -20],
"0.625": [0, 0, -35],
"0.75": [0, 0, -30]
}
},
"torso": {
"rotation": ["2.5+Math.cos((query.anim_time) * 90 * 16/3 + 0) * 2", 0, 0]
},
"tail5": {
"rotation": ["7.5+Math.cos((query.anim_time) * 90 * 16/3 - 260) * 26", 0, 0]
}
}
},
"animation.dewgong.ride_water_swim": {
"loop": true,
"bones": {
"body": {
"rotation": [0, 0, "q.r.yaw_change(0)*-10"]
},
"tail": {
"rotation": ["q.r.pitch_change(0)*5", "q.r.yaw_change(0)*-5", 0]
},
"tail2": {
"rotation": ["q.r.pitch_change(4)*8", "q.r.yaw_change(0)*-5", 0]
},
"tail3": {
"rotation": ["q.r.pitch_change(6)*12", "q.r.yaw_change(0)*-5", 0]
},
"torso": {
"rotation": ["q.r.pitch_change(0)*-5", "q.r.yaw_change(0)*5", 0]
},
"chest": {
"rotation": ["q.r.pitch_change(0)*-10", "q.r.yaw_change(0)*5", 0],
"position": [0, 0, "2+q.r.pitch_change(0)*-2"]
},
"neck": {
"rotation": ["q.r.pitch_change(0)*-5", "q.r.yaw_change(0)*5", 0]
},
"head": {
"rotation": ["q.r.pitch_change(0)*-20", 0, 0]
}
}
},
@@ -140,11 +576,6 @@
"1.25": [0, 0, 0]
}
}
},
"sound_effects": {
"0.0417": {
"effect": "pokemon.dewgong.cry"
}
}
},
"animation.dewgong.blink": {
@@ -210,7 +641,7 @@
}
}
},
"animation.dewgong.new": {
"animation.dewgong.neckdemo": {
"loop": true,
"bones": {
"neck": {
@@ -220,6 +651,195 @@
"rotation": [72.5, 0, 0]
}
}
},
"animation.dewgong.pose": {
"loop": true,
"bones": {
"body": {
"rotation": [0, 12.5, 0]
},
"tail": {
"rotation": [2.41492, 0.64686, -14.98637]
},
"tail2": {
"rotation": [34.97911, 1.05627, -2.26603]
},
"tail3": {
"rotation": [46.78082, -13.99545, -14.43276]
},
"tail4": {
"rotation": [7.5, 0, 0]
},
"tail6": {
"rotation": [30, 0, 0]
},
"chest": {
"rotation": [-32.5, 0, 0]
},
"neck": {
"rotation": [-52.52635, -1.5216, 1.98385]
},
"head": {
"rotation": [83.46987, -8.35694, -6.53013],
"position": [0.58854, -0.68263, -0.01172]
},
"flipper_left": {
"rotation": [32.28184, -14.8934, 25.70507],
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
"rotation": [0, -15, -15]
},
"flipper_left3": {
"rotation": [0, 0, -37.5]
},
"flipper_right": {
"rotation": [99.40658, 28.75123, -7.23212],
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
"rotation": [0, 0, 15]
},
"flipper_right3": {
"rotation": [0, 0, 37.5]
},
"tail5": {
"rotation": [27.5, 0, 0]
},
"eye_right": {
"position": [0.25, 0, 0]
},
"eye_highlight_left": {
"position": [0.25, 0, 0]
},
"eye_highlight_right": {
"position": [0.25, 0, 0]
}
}
},
"animation.dewgong.test_walkspeed": {
"loop": true,
"animation_length": 3.5,
"bones": {
"body": {
"position": [0, 0, "39-q.anim_time*90*0.26"]
}
}
},
"animation.dewgong.basepose": {
"loop": true,
"bones": {
"body": {
"rotation": [-5, 0, 0]
},
"tail": {
"rotation": [-2.5, 0, 0]
},
"tail2": {
"rotation": [0, 0, 0]
},
"tail3": {
"rotation": [4.5, 0, 0]
},
"tail4": {
"rotation": [-2.5, 0, 0]
},
"tail6": {
"rotation": [5, 0, 0]
},
"chest": {
"rotation": [-32.5, 0, 0]
},
"neck": {
"rotation": [-50, 0, 0]
},
"head": {
"rotation": [85, 0, 0]
},
"flipper_left": {
"rotation": [25.90134, -21.60983, 48.92416],
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
"rotation": [0, 0, -15]
},
"flipper_left3": {
"rotation": [0, 0, -37.5]
},
"flipper_right": {
"rotation": [25.90134, 21.60983, -48.92416],
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
"rotation": [0, 0, 15]
},
"flipper_right3": {
"rotation": [0, 0, 37.5]
},
"torso": {
"rotation": [5, 0, 0]
}
}
},
"animation.dewgong.basepose_water": {
"loop": true,
"bones": {
"body": {
"rotation": [-5, 0, 0]
},
"tail": {
"rotation": [-2.5, 0, 0]
},
"tail2": {
"rotation": [-5, 0, 0]
},
"tail3": {
"rotation": [5, 0, 0]
},
"tail4": {
"rotation": [5, 0, 0]
},
"tail6": {
"rotation": [5, 0, 0]
},
"chest": {
"rotation": [5, 0, 0],
"position": [0, 0, 0.25]
},
"neck": {
"rotation": [-50, 0, 0]
},
"head": {
"rotation": [52.5, 0, 0],
"position": [0, 0, 1]
},
"flipper_left": {
"rotation": [0, -12.5, 12.5],
"position": [0, -1.68678, 1.0746]
},
"flipper_left2": {
"rotation": [0, 0, 10]
},
"flipper_left3": {
"rotation": [0, 0, 10]
},
"flipper_right": {
"rotation": [0, 12.5, -12.5],
"position": [0, -1.68678, 1.0746]
},
"flipper_right2": {
"rotation": [0, 0, -10]
},
"flipper_right3": {
"rotation": [0, 0, -10]
},
"torso": {
"rotation": [2.5, 0, 0]
},
"tail5": {
"rotation": [7.5, 0, 0]
}
}
}
}
}
@@ -40,19 +40,19 @@
"tail_right5": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9+120)*10", "-math.sin(q.anim_time*90*1.5-33*2+120)*10", 0]
},
"tail": {
"tail_middle": {
"rotation": ["-math.sin(q.anim_time*90*3-33*5-120)*10+5", "-math.sin(q.anim_time*90*1.5-33*5-120)*4", 0]
},
"tail2": {
"tail_middle2": {
"rotation": ["-math.sin(q.anim_time*90*3-33*6-120)*8", "math.sin(q.anim_time*90*1.5-33*6-120)*6", 0]
},
"tail3": {
"tail_middle3": {
"rotation": ["-math.sin(q.anim_time*90*3-33*7-120)*8", "math.sin(q.anim_time*90*1.5-33*4-120)*8", 0]
},
"tail4": {
"tail_middle4": {
"rotation": ["-math.sin(q.anim_time*90*3-33*8-120)*8", "math.sin(q.anim_time*90*1.5-33*3-120)*8", 0]
},
"tail5": {
"tail_middle5": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9-120)*10", "-math.sin(q.anim_time*90*1.5-33*2-120)*10", 0]
},
"tail_left": {
@@ -175,7 +175,7 @@
"tail_right6": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9+120)*6", "-math.sin(q.anim_time*90*1.5-33*2+120)*10", 0]
},
"tail6": {
"tail_middle6": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9+120)*6", "-math.sin(q.anim_time*90*1.5-33*2+120)*10", 0]
},
"tail_left6": {
@@ -223,19 +223,19 @@
"tail_right5": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*8+45)*16", "-math.sin(q.anim_time*90*2-33*8)*6", 0]
},
"tail": {
"tail_middle": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*5+45)*10+5", "-math.sin(q.anim_time*90*4-33*6+45)*2", 0]
},
"tail2": {
"tail_middle2": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*6+45)*12", "-math.sin(q.anim_time*90*4-33*7+45)*2", 0]
},
"tail3": {
"tail_middle3": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*7+45)*14", "-math.sin(q.anim_time*90*4-33*8+45)*2", 0]
},
"tail4": {
"tail_middle4": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*8+45)*16", "-math.sin(q.anim_time*90*4-33*9+45)*2", 0]
},
"tail5": {
"tail_middle5": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*9+45)*16", "-math.sin(q.anim_time*90*4-33*10+45)*4", 0]
},
"tail_left": {
@@ -345,7 +345,7 @@
"tail_right6": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*8+45)*14", "-math.sin(q.anim_time*90*2-33*8)*10", 0]
},
"tail6": {
"tail_middle6": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*9+45)*14", "-math.sin(q.anim_time*90*4-33*10+45)*6", 0]
},
"tail_left6": {
@@ -387,19 +387,19 @@
"tail_right5": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9+120)*10", "-math.sin(q.anim_time*90*1.5-33*2+120)*10", 0]
},
"tail": {
"tail_middle": {
"rotation": ["-math.sin(q.anim_time*90*3-33*5-120)*10+5", "-math.sin(q.anim_time*90*1.5-33*5-120)*4", 0]
},
"tail2": {
"tail_middle2": {
"rotation": ["-math.sin(q.anim_time*90*3-33*6-120)*8", "math.sin(q.anim_time*90*1.5-33*6-120)*6", 0]
},
"tail3": {
"tail_middle3": {
"rotation": ["-math.sin(q.anim_time*90*3-33*7-120)*8", "math.sin(q.anim_time*90*1.5-33*4-120)*8", 0]
},
"tail4": {
"tail_middle4": {
"rotation": ["-math.sin(q.anim_time*90*3-33*8-120)*8", "math.sin(q.anim_time*90*1.5-33*3-120)*8", 0]
},
"tail5": {
"tail_middle5": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9-120)*10", "-math.sin(q.anim_time*90*1.5-33*2-120)*10", 0]
},
"tail_left": {
@@ -472,7 +472,7 @@
"tail_right6": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9+120)*6", "-math.sin(q.anim_time*90*1.5-33*2+120)*10", 0]
},
"tail6": {
"tail_middle6": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9-120)*6", "-math.sin(q.anim_time*90*1.5-33*2-120)*10", 0]
},
"tail_left6": {
@@ -480,6 +480,255 @@
}
}
},
"animation.tauros.ride_ground_walk": {
"loop": true,
"bones": {
"hips": {
"rotation": [0, "q.r.yaw_change(0)*-5", 0]
},
"tails": {
"rotation": [0, "q.r.yaw_change(0)*-15", 0]
},
"torso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"torso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*15", 0]
},
"ftorso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"ftorso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
}
}
},
"animation.tauros.ride_ground_run": {
"loop": true,
"bones": {
"hips": {
"rotation": [0, "q.r.yaw_change(0)*-5", 0]
},
"torso": {
"rotation": [0, 0, "q.r.yaw_change(0)*-10"]
},
"tails": {
"rotation": [0, "q.r.yaw_change(0)*-15", 0]
},
"torso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"torso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*15", 0]
},
"ftorso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"ftorso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
}
}
},
"animation.tauros.ride_jump": {
"loop": true,
"bones": {
"body": {
"position": [0, "2+math.sin(q.anim_time*90*12)/24", 0]
},
"hips": {
"rotation": ["q.r.velocity_y(0)*-20", 0, 0]
},
"tails": {
"rotation": ["q.r.velocity_y(0)*20", 0, 0]
},
"tail_left": {
"rotation": ["-math.sin(q.anim_time*90*10)*2", -15, 0]
},
"tail_left2": {
"rotation": ["-math.sin(q.anim_time*90*10-30)*3", 0, 0]
},
"tail_left3": {
"rotation": ["-math.sin(q.anim_time*90*10-60)*4", 0, 0]
},
"tail_left4": {
"rotation": ["-math.sin(q.anim_time*90*10-90)*5", 0, 0]
},
"tail_left5": {
"rotation": ["-math.sin(q.anim_time*90*10-120)*6", 0, 0]
},
"tail_left6": {
"rotation": ["-math.sin(q.anim_time*90*10-150)*8", 0, 0]
},
"tail_middle": {
"rotation": ["math.sin(q.anim_time*90*10)*2", 0, 0]
},
"tail_middle2": {
"rotation": ["math.sin(q.anim_time*90*10-30)*3", 0, 0]
},
"tail_middle3": {
"rotation": ["math.sin(q.anim_time*90*10-60)*4", 0, 0]
},
"tail_middle4": {
"rotation": ["math.sin(q.anim_time*90*10-90)*5", 0, 0]
},
"tail_middle5": {
"rotation": ["math.sin(q.anim_time*90*10-120)*6", 0, 0]
},
"tail_middle6": {
"rotation": ["math.sin(q.anim_time*90*10-150)*8", 0, 0]
},
"tail_right": {
"rotation": ["-math.sin(q.anim_time*90*10-60)*2", 15, 0]
},
"tail_right2": {
"rotation": ["-math.sin(q.anim_time*90*10-90)*3", 0, 0]
},
"tail_right3": {
"rotation": ["-math.sin(q.anim_time*90*10-120)*4", 0, 0]
},
"tail_right4": {
"rotation": ["-math.sin(q.anim_time*90*10-150)*5", 0, 0]
},
"tail_right5": {
"rotation": ["-math.sin(q.anim_time*90*10-180)*6", 0, 0]
},
"tail_right6": {
"rotation": ["-math.sin(q.anim_time*90*10-210)*8", 0, 0]
},
"mane_bottom": {
"rotation": ["math.sin(q.anim_time*90*20)*2", 0, 0]
},
"mane_top": {
"rotation": ["-math.sin(q.anim_time*90*20)*2", 0, 0]
},
"leg_front_right": {
"rotation": ["-30+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_right2": {
"rotation": ["40+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_front_left": {
"rotation": ["-25+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_left2": {
"rotation": ["40+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_back_right": {
"rotation": ["5+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_right2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left": {
"rotation": ["10+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*20", 0, 0]
}
}
},
"animation.tauros.render": {
"loop": true,
"bones": {
"torso": {
"rotation": [5, 0, 0]
},
"head_angle": {
"rotation": [7.5, 0, 0]
},
"tail_right": {
"rotation": [28.49002, 17.28889, 10.8585]
},
"tail_right2": {
"rotation": [-2.61298, -9.65594, 15.22074]
},
"tail_right3": {
"rotation": [0, -15, 0]
},
"tail_right4": {
"rotation": [0, -27.5, 0]
},
"tail_right5": {
"rotation": [0, -17.5, 0]
},
"tail_middle": {
"rotation": [47.5, 0, 0]
},
"tail_middle2": {
"rotation": [10.15375, 12.42297, 0.88796]
},
"tail_middle3": {
"rotation": [-5.03364, 9.99043, -0.22117]
},
"tail_middle4": {
"rotation": [0, 7.5, 0]
},
"tail_middle5": {
"rotation": [17.5, -5, -5]
},
"tail_left": {
"rotation": [-5, 0, 0]
},
"tail_left2": {
"rotation": [17.61435, 9.91358, -6.18155]
},
"tail_left3": {
"rotation": [12.92638, 14.63639, 3.31914]
},
"tail_left4": {
"rotation": [0, 37.5, 0]
},
"tail_left5": {
"rotation": [0, 52.5, 0]
},
"leg_front_right": {
"rotation": [-7.5, 0, 0]
},
"leg_front_right2": {
"rotation": [22.5, 0, 0]
},
"leg_front_right3": {
"rotation": [22.5, 0, 0]
},
"leg_front_left2": {
"rotation": [-7.5, 0, 0]
},
"leg_front_left3": {
"rotation": [7.5, 0, 0]
},
"leg_back_right2": {
"rotation": [-10, 0, 0]
},
"leg_back_right3": {
"rotation": [10, 0, 0]
}
}
},
"animation.tauros.blink": {
"animation_length": 0.1667,
"bones": {
"eyelid_right": {
"position": {
"0.0": [-0.1, 0, -0.1],
"0.125": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
"0.1667": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0.1, 0, -0.1],
"0.125": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
"0.1667": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
}
}
}
},
"animation.tauros.cry": {
"animation_length": 2.125,
"bones": {
@@ -617,15 +866,15 @@
},
"position": {
"0.0": {
"post": [0, -0.5, 0],
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.4583": {
"post": [0, -1, 0],
"lerp_mode": "catmullrom"
},
"0.5417": [0, -0.5, 0],
"1.7917": [0, -0.5, 0]
"0.5417": [0, 0, 0],
"1.7917": [0, 0, 0]
}
},
"leg_front_right2": {
@@ -711,6 +960,10 @@
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.3333": {
"post": [-0.00259, -0.29316, -0.18789],
"lerp_mode": "catmullrom"
},
"0.5417": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
@@ -739,102 +992,6 @@
"effect": "pokemon.tauros.cry"
}
}
},
"animation.tauros.render": {
"loop": true,
"bones": {
"torso": {
"rotation": [5, 0, 0]
},
"head_angle": {
"rotation": [7.5, 0, 0]
},
"tail_right": {
"rotation": [28.49002, 17.28889, 10.8585]
},
"tail_right2": {
"rotation": [-2.61298, -9.65594, 15.22074]
},
"tail_right3": {
"rotation": [0, -15, 0]
},
"tail_right4": {
"rotation": [0, -27.5, 0]
},
"tail_right5": {
"rotation": [0, -17.5, 0]
},
"tail": {
"rotation": [47.5, 0, 0]
},
"tail2": {
"rotation": [10.15375, 12.42297, 0.88796]
},
"tail3": {
"rotation": [-5.03364, 9.99043, -0.22117]
},
"tail4": {
"rotation": [0, 7.5, 0]
},
"tail5": {
"rotation": [17.5, -5, -5]
},
"tail_left": {
"rotation": [-5, 0, 0]
},
"tail_left2": {
"rotation": [17.61435, 9.91358, -6.18155]
},
"tail_left3": {
"rotation": [12.92638, 14.63639, 3.31914]
},
"tail_left4": {
"rotation": [0, 37.5, 0]
},
"tail_left5": {
"rotation": [0, 52.5, 0]
},
"leg_front_right": {
"rotation": [-7.5, 0, 0]
},
"leg_front_right2": {
"rotation": [22.5, 0, 0]
},
"leg_front_right3": {
"rotation": [22.5, 0, 0]
},
"leg_front_left2": {
"rotation": [-7.5, 0, 0]
},
"leg_front_left3": {
"rotation": [7.5, 0, 0]
},
"leg_back_right2": {
"rotation": [-10, 0, 0]
},
"leg_back_right3": {
"rotation": [10, 0, 0]
}
}
},
"animation.tauros.blink": {
"animation_length": 0.1667,
"bones": {
"eyelid_right": {
"position": {
"0.0": [-0.1, 0, -0.1],
"0.125": [-0.1, 0, -0.1],
"0.1667": [0, 0, 0]
}
},
"eyelid_left": {
"position": {
"0.0": [0.1, 0, -0.1],
"0.125": [0.1, 0, -0.1],
"0.1667": [0, 0, 0]
}
}
}
}
}
}
@@ -227,7 +227,7 @@
"rotation": ["-math.sin(q.anim_time*90*4*2)*20+10", 0, 0]
},
"tail_right": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*4+45)*10+5", "math.sin(q.anim_time*90*2-33*4)*1+5", 0]
"rotation": ["-math.sin(q.anim_time*90*4*2-33*3+45)*12+5", "NaN-math.sin(q.anim_time*90*2-33*3+180)*1+5", 0]
},
"tail_right2": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*5+45)*12", "-math.sin(q.anim_time*90*2-33*5)*2+5", 0]
@@ -257,7 +257,7 @@
"rotation": ["-math.sin(q.anim_time*90*4*2-33*9+45)*16", "-math.sin(q.anim_time*90*4-33*10+45)*4", 0]
},
"tail_left": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*3+45)*12+5", "-math.sin(q.anim_time*90*2-33*3+180)*1-5", 0]
"rotation": ["-math.sin(q.anim_time*90*4*2-33*3+45)*12+5", "-math.sin(q.anim_time*90*2-33*3+180)*1+5", 0]
},
"tail_left2": {
"rotation": ["-math.sin(q.anim_time*90*4*2-33*4+45)*10", "math.sin(q.anim_time*90*2-33*4+180)*2-5", 0]
@@ -534,6 +534,684 @@
}
}
},
"animation.tauros_paldean_aqua.ride_ground_walk": {
"loop": true,
"bones": {
"torso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"torso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
},
"hips": {
"rotation": [0, "q.r.yaw_change(0)*-5", 0]
},
"tails": {
"rotation": [0, "q.r.yaw_change(0)*-15", 0]
},
"ftorso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"ftorso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
}
}
},
"animation.tauros_paldean_aqua.ride_ground_run": {
"loop": true,
"bones": {
"torso": {
"rotation": [0, 0, "q.r.yaw_change(0)*-10"]
},
"torso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"torso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*15", 0]
},
"hips": {
"rotation": [0, "q.r.yaw_change(0)*-5", 0]
},
"tails": {
"rotation": [0, "q.r.yaw_change(0)*-15", 0]
},
"ftorso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"ftorso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
}
}
},
"animation.tauros_paldean_aqua.ride_jump_land": {
"loop": true,
"bones": {
"body": {
"position": [0, "2+math.sin(q.anim_time*90*12)/24", 0]
},
"mane_bottom": {
"rotation": ["math.sin(q.anim_time*90*20)*2", 0, 0]
},
"mane_top": {
"rotation": ["-math.sin(q.anim_time*90*20)*2", 0, 0]
},
"leg_front_right": {
"rotation": ["-30+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_right2": {
"rotation": ["40+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_front_left": {
"rotation": ["-25+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_left2": {
"rotation": ["40+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_back_right": {
"rotation": ["5+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_right2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left": {
"rotation": ["10+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*20", 0, 0]
},
"hips": {
"rotation": ["q.r.velocity_y(0)*-20", 0, 0]
},
"tails": {
"rotation": ["q.r.velocity_y(0)*20", 0, 0]
},
"tail_left": {
"rotation": ["-math.sin(q.anim_time*90*10)*2", -15, 0]
},
"tail_left2": {
"rotation": ["-math.sin(q.anim_time*90*10-30)*3", 0, 0]
},
"tail_left3": {
"rotation": ["-math.sin(q.anim_time*90*10-60)*4", 0, 0]
},
"tail_left4": {
"rotation": ["-math.sin(q.anim_time*90*10-90)*5", 0, 0]
},
"tail_left5": {
"rotation": ["-math.sin(q.anim_time*90*10-120)*6", 0, 0]
},
"tail_left6": {
"rotation": ["-math.sin(q.anim_time*90*10-150)*8", 0, 0]
},
"tail_middle": {
"rotation": ["math.sin(q.anim_time*90*10)*2", 0, 0]
},
"tail_middle2": {
"rotation": ["math.sin(q.anim_time*90*10-30)*3", 0, 0]
},
"tail_middle3": {
"rotation": ["math.sin(q.anim_time*90*10-60)*4", 0, 0]
},
"tail_middle4": {
"rotation": ["math.sin(q.anim_time*90*10-90)*5", 0, 0]
},
"tail_middle5": {
"rotation": ["math.sin(q.anim_time*90*10-120)*6", 0, 0]
},
"tail_middle6": {
"rotation": ["math.sin(q.anim_time*90*10-150)*8", 0, 0]
},
"tail_right": {
"rotation": ["-math.sin(q.anim_time*90*10-60)*2", 15, 0]
},
"tail_right2": {
"rotation": ["-math.sin(q.anim_time*90*10-90)*3", 0, 0]
},
"tail_right3": {
"rotation": ["-math.sin(q.anim_time*90*10-120)*4", 0, 0]
},
"tail_right4": {
"rotation": ["-math.sin(q.anim_time*90*10-150)*5", 0, 0]
},
"tail_right5": {
"rotation": ["-math.sin(q.anim_time*90*10-180)*6", 0, 0]
},
"tail_right6": {
"rotation": ["-math.sin(q.anim_time*90*10-210)*8", 0, 0]
},
"horn_glow_right": {
"scale": 0
},
"horn_glow_right2": {
"scale": 0
},
"horn_glow_right3": {
"scale": 0
},
"horn_glow_left": {
"scale": 0
},
"horn_glow_left2": {
"scale": 0
},
"horn_glow_left3": {
"scale": 0
}
}
},
"animation.tauros_paldean_aqua.surfacewater_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, "-15+math.sin(q.anim_time*90)/2", 0]
},
"hips": {
"rotation": ["-5-math.sin(q.anim_time*90-90)/2", 0, 0]
},
"torso": {
"rotation": [0, 0, "math.sin(q.anim_time*45-90)*2"]
},
"tails": {
"rotation": [-30, 0, 0]
},
"tail_left": {
"rotation": ["-math.sin(q.anim_time*90*1.5)*8", "math.sin(q.anim_time*90*(1.5/2))*3", 0]
},
"tail_left2": {
"rotation": ["-5-math.sin(q.anim_time*90*1.5-30)*8", "math.sin(q.anim_time*90*(1.5/2)-30)*3", 0]
},
"tail_left3": {
"rotation": ["-math.sin(q.anim_time*90*1.5-60)*8", "15+math.sin(q.anim_time*90*(1.5/2)-60)*3", 0]
},
"tail_left4": {
"rotation": ["-math.sin(q.anim_time*90*1.5-90)*8", "15+math.sin(q.anim_time*90*(1.5/2)-90)*3", 0]
},
"tail_left5": {
"rotation": ["-math.sin(q.anim_time*90*1.5-120)*8", "math.sin(q.anim_time*90*(1.5/2)-120)*3", 0]
},
"tail_left6": {
"rotation": ["-math.sin(q.anim_time*90*1.5-150)*8", "math.sin(q.anim_time*90*(1.5/2)-150)*3", 0]
},
"tail_left7": {
"rotation": ["-math.sin(q.anim_time*90*1.5-180)*8", "math.sin(q.anim_time*90*(1.5/2)-180)*3", 0]
},
"tail_middle": {
"rotation": [2, 0, 0]
},
"tail_middle2": {
"rotation": ["15-math.sin(q.anim_time*90*1.5)*8", "math.sin(q.anim_time*90*(1.5/2))*3", 0]
},
"tail_middle3": {
"rotation": ["-math.sin(q.anim_time*90*1.5-30)*8", "math.sin(q.anim_time*90*(1.5/2)-30)*3", 0]
},
"tail_middle4": {
"rotation": ["-math.sin(q.anim_time*90*1.5-60)*8", "math.sin(q.anim_time*90*(1.5/2)-60)*3", 0]
},
"tail_middle5": {
"rotation": ["-math.sin(q.anim_time*90*1.5-90)*8", "math.sin(q.anim_time*90*(1.5/2)-90)*3", 0]
},
"tail_middle6": {
"rotation": ["-math.sin(q.anim_time*90*1.5-120)*8", "math.sin(q.anim_time*90*(1.5/2)-120)*3", 0]
},
"tail_middle7": {
"rotation": ["-math.sin(q.anim_time*90*1.5-150)*8", "math.sin(q.anim_time*90*(1.5/2)-150)*3", 0]
},
"tail_right": {
"rotation": ["-math.sin(q.anim_time*90*1.5)*8", "math.sin(q.anim_time*90*(1.5/2))*3", 0]
},
"tail_right2": {
"rotation": ["-5-math.sin(q.anim_time*90*1.5-30)*8", "math.sin(q.anim_time*90*(1.5/2)-30)*3", 0]
},
"tail_right3": {
"rotation": ["-math.sin(q.anim_time*90*1.5-60)*8", "-15-math.sin(q.anim_time*90*(1.5/2)-60)*3", 0]
},
"tail_right4": {
"rotation": ["-math.sin(q.anim_time*90*1.5-90)*8", "-15+math.sin(q.anim_time*90*(1.5/2)-90)*3", 0]
},
"tail_right5": {
"rotation": ["-math.sin(q.anim_time*90*1.5-120)*8", "math.sin(q.anim_time*90*(1.5/2)-120)*3", 0]
},
"tail_right6": {
"rotation": ["-math.sin(q.anim_time*90*1.5-150)*8", "math.sin(q.anim_time*90*(1.5/2)-150)*3", 0]
},
"tail_right7": {
"rotation": ["-math.sin(q.anim_time*90*1.5-180)*8", "math.sin(q.anim_time*90*(1.5/2)-180)*3", 0]
},
"torso2": {
"rotation": ["-2.5-math.sin(q.anim_time*90-150)/2", 0, 0]
},
"torso3": {
"rotation": ["-5-math.sin(q.anim_time*90-210)", 0, 0]
},
"mane_bottom": {
"rotation": ["-47.5-math.sin(q.anim_time*90-120)*8", 0, 0]
},
"lower_mane_right": {
"rotation": [0, 0, "math.sin(q.anim_time*90-120)*8"]
},
"lower_mane_left": {
"rotation": [0, 0, "-math.sin(q.anim_time*90-120)*8"]
},
"mane_top": {
"rotation": ["math.sin(q.anim_time*90-120)*8", 0, 0]
},
"mane_left_top": {
"rotation": [0, 0, "-math.sin(q.anim_time*90-120)*8"]
},
"mane_right_top": {
"rotation": [0, 0, "math.sin(q.anim_time*90-120)*8"]
},
"mane_left_bottom": {
"rotation": [0, 0, "-math.sin(q.anim_time*90-120)*8"]
},
"mane_right_bottom": {
"rotation": [0, 0, "math.sin(q.anim_time*90-120)*8"]
},
"head": {
"rotation": ["-math.sin(q.anim_time*90-270)", 0, "math.sin(q.anim_time*45-150)*2"]
},
"horn_glow_right": {
"scale": 0
},
"horn_glow_right2": {
"scale": 0
},
"horn_glow_right3": {
"scale": 0
},
"horn_glow_left": {
"scale": 0
},
"horn_glow_left2": {
"scale": 0
},
"horn_glow_left3": {
"scale": 0
},
"ftorso2": {
"rotation": [-2.5, 0, 0]
},
"ftorso3": {
"rotation": [-5, 0, 0]
},
"leg_front_right": {
"rotation": ["math.sin(q.anim_time*90*2-60)*4", 0, 0],
"position": [0, "-math.sin(q.anim_time*90*2)/4", "-math.cos(q.anim_time*90*2)/4"]
},
"leg_front_right2": {
"rotation": ["20+math.sin(q.anim_time*90*2-120)*6", 0, 0]
},
"leg_front_right3": {
"rotation": ["15+math.sin(q.anim_time*90*2-180)*8", 0, 0]
},
"leg_front_left": {
"rotation": ["-math.sin(q.anim_time*90*2-60)*4", 0, 0],
"position": [0, "math.sin(q.anim_time*90*2)/4", "math.cos(q.anim_time*90*2)/4"]
},
"leg_front_left2": {
"rotation": ["20-math.sin(q.anim_time*90*2-120)*6", 0, 0]
},
"leg_front_left3": {
"rotation": ["15-math.sin(q.anim_time*90*2-180)*8", 0, 0]
},
"leg_back_right": {
"rotation": ["-math.sin(q.anim_time*90*2-60)*4", 0, 0],
"position": [0, "math.sin(q.anim_time*90*2)/4", "math.cos(q.anim_time*90*2)/4"]
},
"leg_back_right2": {
"rotation": ["-20-math.sin(q.anim_time*90*2-120)*15", 0, 0]
},
"leg_back_right3": {
"rotation": ["12.5-math.sin(q.anim_time*90*2-180)*8", 0, 0]
},
"leg_back_left": {
"rotation": ["math.sin(q.anim_time*90*2-60)*4", 0, 0],
"position": [0, "-math.sin(q.anim_time*90*2)/4", "-math.cos(q.anim_time*90*2)/4"]
},
"leg_back_left2": {
"rotation": ["-20+math.sin(q.anim_time*90*2-120)*15", 0, 0]
},
"leg_back_left3": {
"rotation": ["12.5+math.sin(q.anim_time*90*2-180)*8", 0, 0]
}
}
},
"animation.tauros_paldean_aqua.surfacewater_swim": {
"loop": true,
"bones": {
"body": {
"position": [0, "-15+math.sin(q.anim_time*90*3)/4", 0]
},
"hips": {
"rotation": ["-10-math.sin(q.anim_time*90*3)", 0, 0]
},
"torso": {
"rotation": [7.5, 0, 0]
},
"tails": {
"rotation": [15, 0, "math.mod(q.anim_time*2,1)*360"]
},
"tail_left": {
"rotation": [-15, 17.5, 27.5]
},
"tail_left2": {
"rotation": [0, -5, 0]
},
"tail_left3": {
"rotation": [0, -5, 0]
},
"tail_left4": {
"rotation": [0, 12.5, 0]
},
"tail_left5": {
"rotation": [15, 12.5, 0]
},
"tail_left6": {
"rotation": [17.5, 15, 0]
},
"tail_left7": {
"rotation": [12.5, 15, 0]
},
"tail_middle": {
"rotation": [-7.5, 0, 0]
},
"tail_middle2": {
"rotation": [-7.5, 0, 0]
},
"tail_middle3": {
"rotation": [-7.5, 0, 0]
},
"tail_middle4": {
"rotation": [20, 0, 0]
},
"tail_middle5": {
"rotation": [15, -22.5, 0]
},
"tail_middle6": {
"rotation": [10, -20, 0]
},
"tail_middle7": {
"rotation": [10, -22.5, 0]
},
"tail_right": {
"rotation": [-15, -17.5, -27.5]
},
"tail_right2": {
"rotation": [0, 5, 0]
},
"tail_right3": {
"rotation": [0, 5, 0]
},
"tail_right4": {
"rotation": [0, -12.5, 0]
},
"tail_right5": {
"rotation": [-15, -12.5, 0]
},
"tail_right6": {
"rotation": [-17.5, -15, 0]
},
"tail_right7": {
"rotation": [-12.5, -15, 0]
},
"tail_right8": {
"rotation": [-25, 0, 0]
},
"torso2": {
"rotation": ["-math.sin(q.anim_time*90*3-60)", 0, 0]
},
"torso3": {
"rotation": ["2.5-math.sin(q.anim_time*90*3-120)", 0, 0]
},
"mane_bottom": {
"rotation": ["25+math.sin(q.anim_time*90*3)*8", 0, 0]
},
"lower_mane_right": {
"rotation": ["math.sin(q.anim_time*90*3+90)*4", 0, 0]
},
"lower_mane_left": {
"rotation": ["math.sin(q.anim_time*90*3+90)*4", 0, 0]
},
"mane_left_top": {
"rotation": ["math.sin(q.anim_time*90*3-30)*8", 0, 0]
},
"mane_right_top": {
"rotation": ["math.sin(q.anim_time*90*3-30)*8", 0, 0]
},
"mane_left_bottom": {
"rotation": ["math.sin(q.anim_time*90*3+30)*8", 0, 0]
},
"mane_right_bottom": {
"rotation": ["math.sin(q.anim_time*90*3+30)*8", 0, 0]
},
"head": {
"rotation": ["-25-math.sin(q.anim_time*90*3-150)", 0, 0]
},
"horn_glow_right": {
"scale": 0
},
"horn_glow_right2": {
"scale": 0
},
"horn_glow_right3": {
"scale": 0
},
"horn_glow_left": {
"scale": 0
},
"horn_glow_left2": {
"scale": 0
},
"horn_glow_left3": {
"scale": 0
},
"ftorso2": {
"rotation": ["-math.sin(q.anim_time*90*3-60)", 0, 0]
},
"ftorso3": {
"rotation": ["2.5-math.sin(q.anim_time*90*3-120)", 0, 0]
},
"leg_front_right": {
"rotation": ["5-math.sin(q.anim_time*90*3-120)*4", 0, 0],
"position": [0, "-2+math.sin(q.anim_time*90*3-30)/4", "math.cos(q.anim_time*90*3-30)/4"]
},
"leg_front_right2": {
"rotation": ["57.5-math.sin(q.anim_time*90*3-180)*8", 0, 0]
},
"leg_front_right3": {
"rotation": ["32.5-math.sin(q.anim_time*90*3-210)*6", 0, 0],
"position": [0, 0.25, 0]
},
"leg_front_left": {
"rotation": ["5-math.sin(q.anim_time*90*3-90)*4", 0, 0],
"position": [0, "-2+math.sin(q.anim_time*90*3)/4", "math.cos(q.anim_time*90*3)/4"]
},
"leg_front_left2": {
"rotation": ["57.5-math.sin(q.anim_time*90*3-150)*8", 0, 0]
},
"leg_front_left3": {
"rotation": ["32.5-math.sin(q.anim_time*90*3-180)*6", 0, 0],
"position": [0, 0.25, 0]
},
"leg_back_right": {
"rotation": ["57.5+math.sin(q.anim_time*90*3-120)*4", 0, 0],
"position": [0, "-math.sin(q.anim_time*90*3)/4", "-math.cos(q.anim_time*90*3)/4"]
},
"leg_back_right2": {
"rotation": ["-15+math.sin(q.anim_time*90*3-180)*4", 0, 0]
},
"leg_back_right3": {
"rotation": ["15+math.sin(q.anim_time*90*3-210)*6", 0, 0]
},
"leg_back_left": {
"rotation": ["42.5+math.sin(q.anim_time*90*3-90)*4", 0, 0],
"position": [0, "-math.sin(q.anim_time*90*3)/4", "-math.cos(q.anim_time*90*3)/4"]
},
"leg_back_left2": {
"rotation": ["-37.5+math.sin(q.anim_time*90*3-150)*4", 0, 0]
},
"leg_back_left3": {
"rotation": ["15+math.sin(q.anim_time*90*3-180)*6", 0, 0]
}
}
},
"animation.tauros_paldean_aqua.ride_surfacewater_swim": {
"loop": true,
"bones": {
"torso2": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
},
"torso3": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
},
"hips": {
"rotation": [0, "q.r.yaw_change(0)*-10", 0]
},
"ftorso2": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
},
"ftorso3": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
}
}
},
"animation.tauros_paldean_aqua.ride_jump_liquid": {
"loop": true,
"bones": {
"body": {
"position": [0, "2+math.sin(q.anim_time*90*12)/24", 0]
},
"mane_bottom": {
"rotation": ["math.sin(q.anim_time*90*20)*2", 0, 0]
},
"mane_top": {
"rotation": ["-math.sin(q.anim_time*90*20)*2", 0, 0]
},
"leg_front_right": {
"rotation": ["-30+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_right2": {
"rotation": ["40+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_front_left": {
"rotation": ["-25+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_left2": {
"rotation": ["40+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_back_right": {
"rotation": ["5+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_right2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left": {
"rotation": ["10+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*20", 0, 0]
},
"hips": {
"rotation": ["q.r.velocity_y(0)*-20", 0, 0]
},
"tails": {
"rotation": [15, 0, "math.mod(q.anim_time*1,1)*360"]
},
"tail_left": {
"rotation": [-15, 17.5, 27.5]
},
"tail_left2": {
"rotation": [0, -5, 0]
},
"tail_left3": {
"rotation": [0, -5, 0]
},
"tail_left4": {
"rotation": [0, 12.5, 0]
},
"tail_left5": {
"rotation": [15, 12.5, 0]
},
"tail_left6": {
"rotation": [17.5, 15, 0]
},
"tail_left7": {
"rotation": [12.5, 15, 0]
},
"tail_middle": {
"rotation": [-7.5, 0, 0]
},
"tail_middle2": {
"rotation": [-7.5, 0, 0]
},
"tail_middle3": {
"rotation": [-7.5, 0, 0]
},
"tail_middle4": {
"rotation": [20, 0, 0]
},
"tail_middle5": {
"rotation": [15, -22.5, 0]
},
"tail_middle6": {
"rotation": [10, -20, 0]
},
"tail_middle7": {
"rotation": [10, -22.5, 0]
},
"tail_right": {
"rotation": [-15, -17.5, -27.5]
},
"tail_right2": {
"rotation": [0, 5, 0]
},
"tail_right3": {
"rotation": [0, 5, 0]
},
"tail_right4": {
"rotation": [0, -12.5, 0]
},
"tail_right5": {
"rotation": [-15, -12.5, 0]
},
"tail_right6": {
"rotation": [-17.5, -15, 0]
},
"tail_right7": {
"rotation": [-12.5, -15, 0]
},
"tail_right8": {
"rotation": [-25, 0, 0]
},
"horn_glow_right": {
"scale": 0
},
"horn_glow_right2": {
"scale": 0
},
"horn_glow_right3": {
"scale": 0
},
"horn_glow_left": {
"scale": 0
},
"horn_glow_left2": {
"scale": 0
},
"horn_glow_left3": {
"scale": 0
}
}
},
"animation.tauros_paldean_aqua.render": {
"loop": true,
"bones": {
@@ -893,6 +1571,20 @@
"effect": "pokemon.tauros.cry"
}
}
},
"animation.tauros_paldean_aqua.glow": {
"loop": true,
"bones": {
"head_marking_glow": {
"position": [0, 0.1, -0.1]
},
"head_markings_top": {
"position": [0, -0.1, 0]
},
"head_markings_glow_top": {
"position": [0, 0.1, 0]
}
}
}
}
}
@@ -402,27 +402,9 @@
"head_correction": {
"rotation": ["-math.sin(q.anim_time*90*4-90)*0.5-1", 0, 0]
},
"mane_top": {
"rotation": ["math.sin(q.anim_time*90*4-45)*4-4", 0, 0]
},
"mane_bottom": {
"rotation": ["-math.sin(q.anim_time*90*4-45)*4-4", 0, 0]
},
"tail_right": {
"rotation": ["-math.sin(q.anim_time*90*3-33*5+120)*10+5", "-math.sin(q.anim_time*90*1.5-33*5+120)*4", 0]
},
"tail_right2": {
"rotation": ["-math.sin(q.anim_time*90*3-33*6+120)*8", "math.sin(q.anim_time*90*1.5-33*6+120)*6", 0]
},
"tail_right3": {
"rotation": ["-math.sin(q.anim_time*90*3-33*7+120)*8", "math.sin(q.anim_time*90*1.5-33*4+120)*8", 0]
},
"tail_right4": {
"rotation": ["-math.sin(q.anim_time*90*3-33*8+120)*8", "math.sin(q.anim_time*90*1.5-33*3+120)*8", 0]
},
"tail_right5": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9+120)*10", "-math.sin(q.anim_time*90*1.5-33*2+120)*10", 0]
},
"tail": {
"rotation": ["-math.sin(q.anim_time*90*3-33*5-120)*10+5", "-math.sin(q.anim_time*90*1.5-33*5-120)*4", 0]
},
@@ -438,21 +420,6 @@
"tail5": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9-120)*10", "-math.sin(q.anim_time*90*1.5-33*2-120)*10", 0]
},
"tail_left": {
"rotation": ["-math.sin(q.anim_time*90*3-33*5)*10+5", "-math.sin(q.anim_time*90*1.5-33*5)*4", 0]
},
"tail_left2": {
"rotation": ["-math.sin(q.anim_time*90*3-33*6)*8", "math.sin(q.anim_time*90*1.5-33*6)*6", 0]
},
"tail_left3": {
"rotation": ["-math.sin(q.anim_time*90*3-33*7)*8", "math.sin(q.anim_time*90*1.5-33*4)*8", 0]
},
"tail_left4": {
"rotation": ["-math.sin(q.anim_time*90*3-33*8)*8", "math.sin(q.anim_time*90*1.5-33*3)*8", 0]
},
"tail_left5": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9)*10", "-math.sin(q.anim_time*90*1.5-33*2)*10", 0]
},
"leg_front_right": {
"rotation": ["-math.sin(q.anim_time*90*4-66)*3-3", 0, "-math.sin(q.anim_time*90*2+66)*0.5-math.sin(q.anim_time*90*2-45)*0.2"],
"position": [
@@ -505,15 +472,9 @@
"leg_back_left3": {
"rotation": ["-3.5-1*(math.sin(q.anim_time*90*4-90)*1+1+math.sin(q.anim_time*90*4-90)*2-2-math.sin(q.anim_time*90*4-90)*4-6)", 0, 0]
},
"tail_right6": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9+120)*6", "-math.sin(q.anim_time*90*1.5-33*2+120)*10", 0]
},
"tail6": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9-120)*6", "-math.sin(q.anim_time*90*1.5-33*2-120)*10", 0]
},
"tail_left6": {
"rotation": ["-math.sin(q.anim_time*90*3-33*9)*6", "-math.sin(q.anim_time*90*1.5-33*2)*10", 0]
},
"horn_glow_left": {
"scale": 0
},
@@ -534,6 +495,165 @@
}
}
},
"animation.tauros_paldean_blaze.ride_ground_walk": {
"loop": true,
"bones": {
"torso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"torso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
},
"hips": {
"rotation": [0, "q.r.yaw_change(0)*-5", 0]
},
"tail": {
"rotation": [0, "q.r.yaw_change(0)*-5", 0]
},
"tail2": {
"rotation": [0, "q.r.yaw_change(2)*-10", 0]
},
"tail3": {
"rotation": [0, "q.r.yaw_change(4)*-10", 0]
},
"tail4": {
"rotation": [0, "q.r.yaw_change(6)*-15", 0]
},
"tail5": {
"rotation": [0, "q.r.yaw_change(8)*-15", 0]
},
"tail6": {
"rotation": [0, "q.r.yaw_change(10)*-15", 0]
},
"ftorso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"ftorso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
}
}
},
"animation.tauros_paldean_blaze.ride_ground_run": {
"loop": true,
"bones": {
"torso": {
"rotation": [0, 0, "q.r.yaw_change(0)*-10"]
},
"torso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"torso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*15", 0]
},
"tails": {
"rotation": [0, "q.r.yaw_change(0)*-15", 0]
},
"hips": {
"rotation": [0, "q.r.yaw_change(0)*-5", 0]
},
"ftorso2": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
},
"ftorso3": {
"rotation": [0, "q.r.yaw_change(0)*7.5", 0]
}
}
},
"animation.tauros_paldean_blaze.ride_jump": {
"loop": true,
"bones": {
"body": {
"position": [0, "2+math.sin(q.anim_time*90*12)/24", 0]
},
"mane_bottom": {
"rotation": ["math.sin(q.anim_time*90*20)*2", 0, 0]
},
"leg_front_right": {
"rotation": ["-30+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_right2": {
"rotation": ["40+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_front_left": {
"rotation": ["-25+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*40", 0, 0]
},
"leg_front_left2": {
"rotation": ["40+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*30", 0, 0]
},
"leg_back_right": {
"rotation": ["5+math.sin(q.anim_time*90*20)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_right2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-90)+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left": {
"rotation": ["10+math.sin(q.anim_time*90*20-60)/2+q.r.velocity_y(0)*20", 0, 0]
},
"leg_back_left2": {
"rotation": ["-15+math.sin(q.anim_time*90*20-150)+q.r.velocity_y(0)*20", 0, 0]
},
"hips": {
"rotation": ["q.r.velocity_y(0)*-20", 0, 0]
},
"tail": {
"rotation": ["q.r.velocity_y(0)*20+math.sin(q.anim_time*90*10)*2", 0, 0]
},
"tail2": {
"rotation": ["math.sin(q.anim_time*90*10-30)*3", 0, 0]
},
"tail3": {
"rotation": ["math.sin(q.anim_time*90*10-60)*4", 0, 0]
},
"tail4": {
"rotation": ["math.sin(q.anim_time*90*10-90)*5", 0, 0]
},
"tail5": {
"rotation": ["math.sin(q.anim_time*90*10-120)*6", 0, 0]
},
"tail6": {
"rotation": ["math.sin(q.anim_time*90*10-150)*8", 0, 0]
},
"mane_right": {
"rotation": [0, "NaN-math.sin(q.anim_time*90*20-180)*2", 0]
},
"mane_left": {
"rotation": [0, "-math.sin(q.anim_time*90*20-180)*2", 0]
},
"mane_top_fur": {
"rotation": ["-math.sin(q.anim_time*90*20)*2", 0, 0]
},
"mane_middle_right": {
"rotation": ["-math.sin(q.anim_time*90*20-30)*2", 0, 0]
},
"mane_middle_left": {
"rotation": ["-math.sin(q.anim_time*90*20-90)*2", 0, 0]
},
"horn_glow_right": {
"scale": 0
},
"horn_glow_right2": {
"scale": 0
},
"horn_glow_right3": {
"scale": 0
},
"horn_glow_left": {
"scale": 0
},
"horn_glow_left2": {
"scale": 0
},
"horn_glow_left3": {
"scale": 0
}
}
},
"animation.tauros_paldean_blaze.render": {
"loop": true,
"bones": {
@@ -911,6 +1031,20 @@
"effect": "pokemon.tauros.cry"
}
}
},
"animation.tauros_paldean_blaze.glow": {
"loop": true,
"bones": {
"head_markings_top": {
"position": [0, -0.1, 0]
},
"head_markings_glow_top": {
"position": [0, 0.1, 0]
},
"head_marking_glow": {
"position": [0, 0.1, -0.1]
}
}
}
}
}
@@ -3600,28 +3600,64 @@
"left_eye": {
"position": {
"0.0": [0, 0, 0],
"0.375": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"1.9583": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"2.5": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.25": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"1.9583": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"2.5": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"right_eye": {
"position": {
"0.0": [0, 0, 0],
"0.375": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"1.9583": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"2.5": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.25": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"1.9583": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"2.5": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"closed": {
"position": {
"0.0": [0, 0, 0],
"0.375": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"1.25": {"pre": [0, 0, -0.1], "post": [0, 0, 0]},
"1.9583": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"2.5": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
"0.375": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"1.25": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
},
"1.9583": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"2.5": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"front_neck_fluff_lower": {
@@ -4413,28 +4449,64 @@
"left_eye": {
"position": {
"0.0": [0, 0, 0],
"0.8333": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"2.0": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"2.2917": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.8333": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.25": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"2.0": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"2.2917": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"right_eye": {
"position": {
"0.0": [0, 0, 0],
"0.8333": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"2.0": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"2.2917": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.8333": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.25": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"2.0": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"2.2917": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"closed": {
"position": {
"0.0": [0, 0, 0],
"0.8333": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"1.25": {"pre": [0, 0, -0.1], "post": [0, 0, 0]},
"2.0": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"2.2917": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
"0.8333": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"1.25": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
},
"2.0": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"2.2917": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"front_neck_fluff_lower": {
@@ -4575,25 +4647,49 @@
"left_pupil": {
"position": {
"0.0": [0, 0, 0],
"1.25": {"pre": [0, 0, 0], "post": [0, -0.8, 0]},
"2.2917": {"pre": [0, -0.8, 0], "post": [0, 0, 0]}
"1.25": {
"pre": [0, 0, 0],
"post": [0, -0.8, 0]
},
"2.2917": {
"pre": [0, -0.8, 0],
"post": [0, 0, 0]
}
},
"scale": {
"0.0": [1, 1, 1],
"1.25": {"pre": [1, 1, 1], "post": [1, 0.5, 1]},
"2.2917": {"pre": [1, 0.5, 1], "post": [1, 1, 1]}
"1.25": {
"pre": [1, 1, 1],
"post": [1, 0.5, 1]
},
"2.2917": {
"pre": [1, 0.5, 1],
"post": [1, 1, 1]
}
}
},
"right_pupil": {
"position": {
"0.0": [0, 0, 0],
"1.25": {"pre": [0, 0, 0], "post": [0, -0.8, 0]},
"2.2917": {"pre": [0, -0.8, 0], "post": [0, 0, 0]}
"1.25": {
"pre": [0, 0, 0],
"post": [0, -0.8, 0]
},
"2.2917": {
"pre": [0, -0.8, 0],
"post": [0, 0, 0]
}
},
"scale": {
"0.0": [1, 1, 1],
"1.25": {"pre": [1, 1, 1], "post": [1, 0.5, 1]},
"2.2917": {"pre": [1, 0.5, 1], "post": [1, 1, 1]}
"1.25": {
"pre": [1, 1, 1],
"post": [1, 0.5, 1]
},
"2.2917": {
"pre": [1, 0.5, 1],
"post": [1, 1, 1]
}
}
},
"back_neck_fluff_lower": {
@@ -5407,28 +5503,64 @@
"left_eye": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"0.7917": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"1.375": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.875": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"0.7917": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"1.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.875": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"right_eye": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"0.7917": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"1.375": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.875": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"0.7917": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"1.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.875": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"closed": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"0.7917": {"pre": [0, 0, -0.1], "post": [0, 0, 0]},
"1.375": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"1.875": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"0.7917": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
},
"1.375": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"1.875": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"front_neck_fluff_lower": {
@@ -6512,28 +6644,64 @@
"left_eye": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"0.7917": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"1.375": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.875": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"0.7917": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"1.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.875": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"right_eye": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"0.7917": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
"1.375": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.875": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"0.7917": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
},
"1.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.875": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"closed": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"0.7917": {"pre": [0, 0, -0.1], "post": [0, 0, 0]},
"1.375": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"1.875": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"0.7917": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
},
"1.375": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"1.875": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"front_neck_fluff_lower": {
@@ -7414,22 +7582,40 @@
"left_eye": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.9583": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.9583": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"right_eye": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, 0.1]},
"1.9583": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0.1]
},
"1.9583": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"closed": {
"position": {
"0.0": [0, 0, 0],
"0.5": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
"1.9583": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
"0.5": {
"pre": [0, 0, 0],
"post": [0, 0, -0.1]
},
"1.9583": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"front_neck_fluff_lower": {
@@ -7707,22 +7893,40 @@
"closed": {
"position": {
"0.0": [0, 0, -0.1],
"0.125": {"pre": [0, 0, -0.1], "post": [0, 0, -0.1]},
"0.1667": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
"0.125": {
"pre": [0, 0, -0.1],
"post": [0, 0, -0.1]
},
"0.1667": {
"pre": [0, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"left_eye": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": {"pre": [0, 0, 0.1], "post": [0, 0, 0.1]},
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.125": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0.1]
},
"0.1667": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"right_eye": {
"position": {
"0.0": [0, 0, 0.1],
"0.125": {"pre": [0, 0, 0.1], "post": [0, 0, 0.1]},
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
"0.125": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0.1]
},
"0.1667": {
"pre": [0, 0, 0.1],
"post": [0, 0, 0]
}
}
}
}
@@ -7881,6 +8085,182 @@
"effect": "pokemon.slaking.cry"
}
}
},
"animation.slaking.ride_ground": {
"loop": true,
"bones": {
"torso": {
"rotation": [0, "q.r.yaw_change(0)*20", "q.r.yaw_change(0)*-8"]
},
"upper_torso": {
"rotation": [0, "q.r.yaw_change(0)*10", 0]
},
"head": {
"rotation": [0, "q.r.yaw_change(0)*30", 0]
},
"legs": {
"rotation": [0, "q.r.yaw_change(0)*5", 0]
}
}
},
"animation.slaking.ride_jump": {
"loop": true,
"bones": {
"body": {
"position": [0, "2+math.clamp((q.r.velocity_y(0)*3),0,3)+math.sin(q.anim_time*90*12)*0.1", 0]
},
"torso": {
"rotation": ["30+q.r.velocity_y(0)*-20", 0, 0]
},
"upper_torso": {
"rotation": ["10+q.r.velocity_y(0)*10", 0, 0]
},
"right_upper_arm": {
"rotation": ["-17.5+q.r.velocity_y(0)*20", "-5+q.r.velocity_y(0)*20-math.sin(q.anim_time*90*20)/8", "-17.5+q.r.velocity_y(0)*-20"]
},
"right_lower_arm": {
"rotation": [0, "-60+q.r.velocity_y(0)*20-math.sin(q.anim_time*90*20-90)/2", 0]
},
"right_hand": {
"rotation": [0, 0, -20]
},
"right_index_finger": {
"rotation": [0, 0, -30]
},
"right_index_finger_tip": {
"rotation": [0, 0, -62.5]
},
"right_middle_finger": {
"rotation": [0, 0, -25]
},
"right_middle_finger_tip": {
"rotation": [0, 0, -75]
},
"right_ring_finger": {
"rotation": [0, 0, -32.5]
},
"right_ring_finger_tip": {
"rotation": [0, 0, -80]
},
"right_pinky_finger": {
"rotation": [0, 0, -40]
},
"right_pinky_finger_tip": {
"rotation": [0, 0, -77.5]
},
"right_thumb": {
"rotation": [0, -25, -22.5]
},
"right_thumb_tip": {
"rotation": [0, 50, 0]
},
"left_upper_arm": {
"rotation": ["q.r.velocity_y(0)*20", "5+q.r.velocity_y(0)*-20+math.sin(q.anim_time*90*20)/8", "17.5+q.r.velocity_y(0)*20"]
},
"left_lower_arm": {
"rotation": [0, "47.5+q.r.velocity_y(0)*-20+math.sin(q.anim_time*90*20-90)/2", 0]
},
"left_hand": {
"rotation": [0, 0, 20]
},
"left_index_finger": {
"rotation": [0, 0, 30]
},
"left_index_finger_tip": {
"rotation": [0, 0, 62.5]
},
"left_middle_finger": {
"rotation": [0, 0, 25]
},
"left_middle_finger_tip": {
"rotation": [0, 0, 75]
},
"left_ring_finger": {
"rotation": [0, 0, 32.5]
},
"left_ring_finger_tip": {
"rotation": [0, 0, 80]
},
"left_pinky_finger": {
"rotation": [0, 0, 40]
},
"left_pinky_finger_tip": {
"rotation": [0, 0, 77.5]
},
"left_thumb": {
"rotation": [0, 25, 22.5]
},
"left_thumb_tip": {
"rotation": [0, -50, 0]
},
"head": {
"rotation": ["math.clamp((q.r.velocity_y(0)*30),-30,-10)+math.sin(q.anim_time*90*20)/8", 0, 0]
},
"left_head_tuft": {
"rotation": [0, "-17.5+math.sin(q.anim_time*90*20)*2", 0]
},
"right_head_tuft": {
"rotation": [0, "17.5-math.sin(q.anim_time*90*20-90)*2", 0]
},
"front_neck_fluff_lower": {
"rotation": ["math.sin(q.anim_time*90*20)", 0, 0]
},
"front_neck_fluff_upper": {
"rotation": ["-2.5+math.sin(q.anim_time*90*20-90)", 0, 0]
},
"back_neck_fluff_lower": {
"rotation": ["-math.sin(q.anim_time*90*20)", 0, 0]
},
"back_neck_fluff_upper": {
"rotation": ["-math.sin(q.anim_time*90*20-90)", 0, 0]
},
"left_neck_fluff_lower": {
"rotation": [0, 0, "math.sin(q.anim_time*90*20)"]
},
"left_neck_fluff_upper": {
"rotation": [0, 0, "math.sin(q.anim_time*90*20-90)"]
},
"right_neck_fluff_lower": {
"rotation": [0, 0, "-math.sin(q.anim_time*90*20)"]
},
"right_neck_fluff_upper": {
"rotation": [0, 0, "-math.sin(q.anim_time*90*20-90)"]
},
"left_upper_leg": {
"rotation": ["-45+q.r.velocity_y(0)*30+math.sin(q.anim_time*90*20-60)/4", -17.5, 0]
},
"left_lower_leg": {
"rotation": ["55+math.clamp((q.r.velocity_y(0)*30),-30,0)+math.sin(q.anim_time*90*20-150)/2", 0, 0]
},
"left_foot": {
"rotation": [2.5, 0, 0]
},
"left_small_toes": {
"rotation": ["27.5+math.clamp((q.r.velocity_y(0)*30),-27.5,0)", 0, 0]
},
"left_big_toe": {
"rotation": ["10+math.clamp((q.r.velocity_y(0)*10),-105,0)", 0, 0]
},
"right_upper_leg": {
"rotation": ["-30+q.r.velocity_y(0)*30+math.sin(q.anim_time*90*20)/4", 15, 0]
},
"right_lower_leg": {
"rotation": ["60+math.clamp((q.r.velocity_y(0)*40),-40,0)+math.sin(q.anim_time*90*20-90)/2", 0, 0]
},
"right_foot": {
"rotation": [2.5, 0, 0]
},
"right_big_toe": {
"rotation": ["10+math.clamp((q.r.velocity_y(0)*10),-105,0)", 0, 0]
},
"right_small_toes": {
"rotation": ["27.5+math.clamp((q.r.velocity_y(0)*30),-27.5,0)", 0, 0]
},
"locator_seat_1": {
"rotation": [-40, 0, 0],
"position": [0, -5, 0]
}
}
}
}
}
@@ -7,8 +7,8 @@
"texture_width": 128,
"texture_height": 128,
"visible_bounds_width": 10,
"visible_bounds_height": 3.5,
"visible_bounds_offset": [0, 1.25, 0]
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
@@ -132,17 +132,17 @@
{
"name": "locator_seat_1",
"parent": "torso",
"pivot": [0, 18.25, 3.75],
"pivot": [0, 15.25, 3.75],
"locators": {
"seat_1": [0, 18.25, 3.75]
"seat_1": [0, 15.25, 3.75]
}
},
{
"name": "locator_seat_2",
"parent": "torso",
"pivot": [0, 15, 17],
"pivot": [0, 12, 17],
"locators": {
"seat_2": [0, 15, 17]
"seat_2": [0, 12, 17]
}
},
{
@@ -164,11 +164,6 @@
{
"name": "head",
"parent": "neck",
"pivot": [0, 4.75, -34.5]
},
{
"name": "head_ai",
"parent": "head",
"pivot": [0, 4.75, -34.5],
"cubes": [
{"origin": [-5.5, 0, -39], "size": [11, 10, 10], "inflate": 0.21, "uv": [0, 93]},
@@ -176,74 +171,48 @@
]
},
{
"name": "jaw",
"parent": "head_ai",
"pivot": [0, 1.85, -38.5],
"cubes": [
{"origin": [-4, -0.15, -41.5], "size": [8, 2, 3], "uv": [58, 35]}
]
},
{
"name": "locator_mouth",
"parent": "jaw",
"pivot": [0, 1.75, -39.2],
"name": "locator_head",
"parent": "head",
"pivot": [0, 10.25, -37],
"locators": {
"mouth": [0, 1.75, -39.2],
"special": [0, 1.75, -39.2]
"head": [0, 10.25, -37]
}
},
{
"name": "locator_item",
"parent": "jaw",
"pivot": [0, 1.75, -41.45],
"name": "locator_item_hat",
"parent": "head",
"pivot": [0, 10.25, -34.75],
"locators": {
"item": [0, 1.75, -41.45]
"item_hat": [0, 10.25, -34.75]
}
},
{
"name": "locator_jaw_lower",
"parent": "jaw",
"pivot": [0, 1.8, -41.5],
"name": "locator_face",
"parent": "head",
"pivot": [0, 4.7, -42.15],
"locators": {
"jaw2": [0, 1.8, -41.5]
"face": [0, 4.7, -42.15]
}
},
{
"name": "snout",
"parent": "head_ai",
"pivot": [0, 3.88, -39.15],
"cubes": [
{"origin": [-5, 1.75, -42.15], "size": [10, 3, 3], "uv": [6, 122]},
{"origin": [-1.5, 4.26, -41.15], "size": [3, 1, 1], "uv": [80, 35]}
]
},
{
"name": "locator_jaw_upper",
"parent": "snout",
"pivot": [0, 1.75, -40.65],
"name": "locator_item_face",
"parent": "head",
"pivot": [0, 6.2, -39.2],
"locators": {
"jaw1": [0, 1.75, -40.65]
"item_face": [0, 6.2, -39.2]
}
},
{
"name": "tooth_left",
"parent": "snout",
"pivot": [4, 2, -40.65],
"cubes": [
{"origin": [3.5, 0, -41.15], "size": [1, 2, 1], "uv": [80, 37]}
]
},
{
"name": "tooth_right",
"parent": "snout",
"pivot": [-4, 2, -40.65],
"cubes": [
{"origin": [-4.5, 0, -41.15], "size": [1, 2, 1], "uv": [84, 37]}
]
"name": "locator_horn",
"parent": "head",
"pivot": [0, 13.15, -34.75],
"locators": {
"horn": [0, 13.15, -34.75]
}
},
{
"name": "eyes",
"parent": "head_ai",
"parent": "head",
"pivot": [3.5, 6.5, -39.27]
},
{
@@ -385,43 +354,69 @@
]
},
{
"name": "locator_horn",
"parent": "head_ai",
"pivot": [0, 13.15, -34.75],
"name": "snout",
"parent": "head",
"pivot": [0, 3.88, -39.15],
"cubes": [
{"origin": [-5, 1.75, -42.15], "size": [10, 3, 3], "uv": [6, 122]},
{"origin": [-1.5, 4.26, -41.15], "size": [3, 1, 1], "uv": [80, 35]}
]
},
{
"name": "locator_jaw_upper",
"parent": "snout",
"pivot": [0, 1.75, -40.65],
"locators": {
"horn": [0, 13.15, -34.75]
"jaw1": [0, 1.75, -40.65]
}
},
{
"name": "locator_item_face",
"parent": "head_ai",
"pivot": [0, 6.2, -39.2],
"name": "tooth_left",
"parent": "snout",
"pivot": [4, 2, -40.65],
"cubes": [
{"origin": [3.5, 0, -41.15], "size": [1, 2, 1], "uv": [80, 37]}
]
},
{
"name": "tooth_right",
"parent": "snout",
"pivot": [-4, 2, -40.65],
"cubes": [
{"origin": [-4.5, 0, -41.15], "size": [1, 2, 1], "uv": [84, 37]}
]
},
{
"name": "jaw",
"parent": "head",
"pivot": [0, 1.85, -38.5],
"cubes": [
{"origin": [-4, -0.15, -41.5], "size": [8, 2, 3], "uv": [58, 35]}
]
},
{
"name": "locator_mouth",
"parent": "jaw",
"pivot": [0, 1.75, -39.2],
"locators": {
"item_face": [0, 6.2, -39.2]
"mouth": [0, 1.75, -39.2],
"special": [0, 1.75, -39.2]
}
},
{
"name": "locator_face",
"parent": "head_ai",
"pivot": [0, 4.7, -42.15],
"name": "locator_item",
"parent": "jaw",
"pivot": [0, 1.75, -41.45],
"locators": {
"face": [0, 4.7, -42.15]
"item": [0, 1.75, -41.45]
}
},
{
"name": "locator_item_hat",
"parent": "head_ai",
"pivot": [0, 10.25, -34.75],
"name": "locator_jaw_lower",
"parent": "jaw",
"pivot": [0, 1.8, -41.5],
"locators": {
"item_hat": [0, 10.25, -34.75]
}
},
{
"name": "locator_head",
"parent": "head_ai",
"pivot": [0, 10.25, -37],
"locators": {
"head": [0, 10.25, -37]
"jaw2": [0, 1.8, -41.5]
}
},
{
@@ -54,17 +54,17 @@
{
"name": "head",
"parent": "torso",
"pivot": [0, 9.5, -10.5]
"pivot": [0, 9.4, -10.7]
},
{
"name": "head_correction",
"parent": "head",
"pivot": [0, 9.5, -10.5]
"pivot": [0, 10, -11]
},
{
"name": "head_ai",
"parent": "head_correction",
"pivot": [0, 9.5, -10.5]
"pivot": [0, 10, -11]
},
{
"name": "head_angle",
@@ -99,6 +99,23 @@
{"origin": [-4, 4.53407, -24.34461], "size": [8, 3, 8], "inflate": -0.01, "pivot": [0, 6.53407, -20.34461], "rotation": [0, -45, 0], "uv": [92, 0]}
]
},
{
"name": "locator_jaw_lower",
"parent": "jaw",
"pivot": [0, 7, -22.25],
"rotation": [90, 0, 0],
"locators": {
"jaw_lower": [0, 7, -22.25]
}
},
{
"name": "locator_item",
"parent": "jaw",
"pivot": [0, 7.5, -25.5],
"locators": {
"item": [0, 7.5, -25.5]
}
},
{
"name": "left_ear",
"parent": "head_angle",
@@ -129,6 +146,17 @@
{"origin": [0, 14.53407, -27.00146], "size": [0, 2, 2], "uv": [0, 44]}
]
},
{
"name": "locator_horn",
"parent": "horn",
"pivot": [0, 17.5, -25.5],
"rotation": [-90, 0, 0],
"locators": {
"horn": [0, 17.5, -25.5],
"physical": [0, 17.5, -25.5],
"special": [0, 17.5, -25.5]
}
},
{
"name": "left_tooth",
"parent": "head_angle",
@@ -169,6 +197,14 @@
{"origin": [6.51, 10.03407, -23.25882], "size": [0, 1, 1], "inflate": 0.02, "pivot": [6.5, 10.53407, -20.75882], "rotation": [0, 45, 0], "uv": [2, 0]}
]
},
{
"name": "locator_eye_left",
"parent": "left_pupil",
"pivot": [5, 10.5, -22.25],
"locators": {
"eye3": [5, 10.5, -22.25]
}
},
{
"name": "left_eyelid",
"parent": "eyes",
@@ -260,6 +296,71 @@
{"origin": [-6.51, 10.03407, -23.25882], "size": [0, 1, 1], "inflate": 0.02, "pivot": [-6.5, 10.53407, -20.75882], "rotation": [0, -45, 0], "uv": [0, 0]}
]
},
{
"name": "locator_eye_right",
"parent": "right_pupil",
"pivot": [-5, 10.5, -22.25],
"locators": {
"eye2": [-5, 10.5, -22.25]
}
},
{
"name": "locator_mouth",
"parent": "head",
"pivot": [0, 7, -17],
"locators": {
"mouth": [0, 7, -17]
}
},
{
"name": "locator_jaw_upper",
"parent": "head",
"pivot": [0, 7.75, -20.25],
"rotation": [-90, 0, 0],
"locators": {
"jaw_upper": [0, 7.75, -20.25]
}
},
{
"name": "locator_head",
"parent": "head",
"pivot": [0, 15, -19.25],
"locators": {
"head": [0, 15, -19.25]
}
},
{
"name": "locator_item_hat",
"parent": "head",
"pivot": [0, 16, -17.25],
"locators": {
"item_hat": [0, 16, -17.25]
}
},
{
"name": "locator_face",
"parent": "head",
"pivot": [0, 10.5, -27.25],
"locators": {
"face": [0, 10.5, -27.25]
}
},
{
"name": "locator_item_face",
"parent": "head",
"pivot": [0, 10.25, -27.25],
"locators": {
"item_face": [0, 10.25, -27.25]
}
},
{
"name": "locator_seat_1",
"parent": "torso",
"pivot": [0, 19, -8],
"locators": {
"seat_1": [0, 19, -8]
}
},
{
"name": "left_front_leg",
"parent": "body",
@@ -285,6 +386,15 @@
{"origin": [9, 0, -11.5], "size": [0, 2, 2], "pivot": [9, 0, -9.5], "rotation": [0, -2.5, 0], "uv": [16, 36]}
]
},
{
"name": "locator_foot_primary2",
"parent": "left_front_foot",
"pivot": [7.5, 1, -7.1],
"rotation": [90, 0, 0],
"locators": {
"foot_primary2": [7.5, 1, -7.1]
}
},
{
"name": "right_front_leg",
"parent": "body",
@@ -310,6 +420,15 @@
{"origin": [-9, 0, -11.5], "size": [0, 2, 2], "pivot": [-9, 0, -9.5], "rotation": [0, 2.5, 0], "uv": [12, 34]}
]
},
{
"name": "locator_foot_primary1",
"parent": "right_front_foot",
"pivot": [-7.5, 1, -7.1],
"rotation": [90, 0, 0],
"locators": {
"foot_primary1": [-7.5, 1, -7.1]
}
},
{
"name": "left_back_leg",
"parent": "body",
@@ -335,6 +454,15 @@
{"origin": [8.5, 0, 2], "size": [0, 2, 2], "pivot": [8.5, 0, 4], "rotation": [0, -5, 0], "uv": [0, 36]}
]
},
{
"name": "locator_foot_secondary2",
"parent": "left_back_foot",
"pivot": [7.5, 1, 7.15],
"rotation": [90, 0, 0],
"locators": {
"foot_secondary2": [7.5, 1, 7.15]
}
},
{
"name": "right_back_leg",
"parent": "body",
@@ -359,6 +487,47 @@
{"origin": [-5.5, 0, 2], "size": [0, 2, 2], "pivot": [-5.5, 0, 4], "rotation": [0, -5, 0], "uv": [0, 8]},
{"origin": [-8.5, 0, 2], "size": [0, 2, 2], "pivot": [-8.5, 0, 4], "rotation": [0, 5, 0], "uv": [0, 6]}
]
},
{
"name": "locator_foot_secondary",
"parent": "right_back_foot",
"pivot": [-7.5, 1, 7.15],
"rotation": [90, 0, 0],
"locators": {
"foot_secondary": [-7.5, 1, 7.15]
}
},
{
"name": "locator_middle",
"parent": "body",
"pivot": [0, 12, 0],
"locators": {
"middle": [0, 12, 0]
}
},
{
"name": "locator_target",
"parent": "body",
"pivot": [0, 6.75, -31],
"locators": {
"target": [0, 6.75, -31]
}
},
{
"name": "locator_root",
"parent": "rhyhorn",
"pivot": [0, 0, 0],
"locators": {
"root": [0, 0, 0]
}
},
{
"name": "locator_top",
"parent": "rhyhorn",
"pivot": [0, 25.5, 0],
"locators": {
"top": [0, 25.5, 0]
}
}
]
}
@@ -27,16 +27,20 @@
"cubes": [
{"origin": [-8.5, 4, -11], "size": [17, 15, 21], "uv": [0, 36]},
{"origin": [-8.5, 4, -11], "size": [17, 15, 21], "inflate": 0.2, "uv": [0, 0]},
{"origin": [8.75, 9.5, -9.5], "size": [0, 10, 14], "pivot": [8.75, 12.75, -9.5], "rotation": [15, 10, 2.5], "uv": [28, 58]},
{"origin": [-8.75, 9.5, -9.5], "size": [0, 10, 14], "pivot": [-8.75, 12.75, -9.5], "rotation": [15, -10, -2.5], "uv": [0, 58]},
{"origin": [8.75, 1.75, -10.5], "size": [0, 11, 11], "pivot": [8.75, 8.75, -9.5], "rotation": [17.5, 12.5, 5], "uv": [24, 83]},
{"origin": [-8.75, 1.75, -10.5], "size": [0, 11, 11], "pivot": [-8.75, 8.75, -9.5], "rotation": [17.5, -12.5, -5], "uv": [80, 6]},
{"origin": [9.25, 4, -1.75], "size": [0, 9, 14], "pivot": [9.225, -2.25, 0.25], "rotation": [2.5, 12.5, 2.5], "uv": [0, 68]},
{"origin": [-9.25, 4, -1.75], "size": [0, 9, 14], "pivot": [-9.225, -2.25, 0.25], "rotation": [2.5, -12.5, -2.5], "uv": [56, 62]},
{"origin": [9.5, 11, -0.5], "size": [0, 8, 12], "pivot": [9.95389, 15.17057, 1.69638], "rotation": [0, 5, -5], "uv": [80, 16]},
{"origin": [-9.5, 11, -0.5], "size": [0, 8, 12], "pivot": [-9.95389, 15.17057, 1.69638], "rotation": [0, -5, 5], "uv": [0, 79]}
]
},
{
"name": "locator_seat_1",
"parent": "torso",
"pivot": [0, 19, -8],
"locators": {
"seat_1": [0, 19, -8]
}
},
{
"name": "spine",
"parent": "torso",
@@ -51,20 +55,52 @@
{"origin": [0, 19.75, 5.75], "size": [0, 3, 3], "uv": [15, 5]}
]
},
{
"name": "left_bodyplate",
"parent": "torso",
"pivot": [8.75, 12.75, -9.5],
"cubes": [
{"origin": [8.75, 9.5, -9.5], "size": [0, 10, 14], "pivot": [8.75, 12.75, -9.5], "rotation": [15, 10, 2.5], "uv": [28, 58]}
]
},
{
"name": "right_bodyplate",
"parent": "torso",
"pivot": [-8.75, 12.75, -9.5],
"cubes": [
{"origin": [-8.75, 9.5, -9.5], "size": [0, 10, 14], "pivot": [-8.75, 12.75, -9.5], "rotation": [15, -10, -2.5], "uv": [0, 58]}
]
},
{
"name": "left_legplate",
"parent": "torso",
"pivot": [8.75, 8.75, -7.5],
"cubes": [
{"origin": [8.75, 1.75, -10.5], "size": [0, 11, 11], "pivot": [8.75, 8.75, -9.5], "rotation": [17.5, 12.5, 5], "uv": [24, 83]}
]
},
{
"name": "right_legplate",
"parent": "torso",
"pivot": [-8.75, 8.75, -7.5],
"cubes": [
{"origin": [-8.75, 1.75, -10.5], "size": [0, 11, 11], "pivot": [-8.75, 8.75, -9.5], "rotation": [17.5, -12.5, -5], "uv": [80, 6]}
]
},
{
"name": "head",
"parent": "torso",
"pivot": [0, 9.5, -10.5]
"pivot": [0, 9.4, -10.7]
},
{
"name": "head_correction",
"parent": "head",
"pivot": [0, 9.5, -10.5]
"pivot": [0, 10, -11]
},
{
"name": "head_ai",
"parent": "head_correction",
"pivot": [0, 9.5, -10.5]
"pivot": [0, 10, -11]
},
{
"name": "head_angle",
@@ -99,6 +135,23 @@
{"origin": [-4, 4.53407, -24.34461], "size": [8, 3, 8], "inflate": -0.01, "pivot": [0, 6.53407, -20.34461], "rotation": [0, -45, 0], "uv": [92, 0]}
]
},
{
"name": "locator_jaw_lower",
"parent": "jaw_front",
"pivot": [0, 7, -22.25],
"rotation": [90, 0, 0],
"locators": {
"jaw_lower": [0, 7, -22.25]
}
},
{
"name": "locator_item",
"parent": "jaw_front",
"pivot": [0, 7.5, -25.5],
"locators": {
"item": [0, 7.5, -25.5]
}
},
{
"name": "left_ear",
"parent": "head_angle",
@@ -129,6 +182,17 @@
{"origin": [0, 15.03407, -27.00146], "size": [0, 3, 2], "pivot": [0, 15.78407, -26.00146], "rotation": [-7.5, 0, 0], "uv": [0, 0]}
]
},
{
"name": "locator_horn",
"parent": "horn",
"pivot": [0, 17.5, -25.5],
"rotation": [-90, 0, 0],
"locators": {
"horn": [0, 17.5, -25.5],
"physical": [0, 17.5, -25.5],
"special": [0, 17.5, -25.5]
}
},
{
"name": "left_tooth",
"parent": "head_angle",
@@ -169,6 +233,14 @@
{"origin": [6.51, 10.03407, -23.25882], "size": [0, 1, 1], "inflate": 0.02, "pivot": [6.5, 10.53407, -20.75882], "rotation": [0, 45, 0], "uv": [2, 0]}
]
},
{
"name": "locator_eye_left",
"parent": "left_pupil",
"pivot": [5, 10.5, -22.25],
"locators": {
"eye3": [5, 10.5, -22.25]
}
},
{
"name": "left_eyelid",
"parent": "eyes",
@@ -260,6 +332,63 @@
{"origin": [-6.51, 10.03407, -23.25882], "size": [0, 1, 1], "inflate": 0.02, "pivot": [-6.5, 10.53407, -20.75882], "rotation": [0, -45, 0], "uv": [0, 0]}
]
},
{
"name": "locator_eye_right",
"parent": "right_pupil",
"pivot": [-5, 10.5, -22.25],
"locators": {
"eye2": [-5, 10.5, -22.25]
}
},
{
"name": "locator_mouth",
"parent": "head_angle",
"pivot": [0, 7, -17],
"locators": {
"mouth": [0, 7, -17]
}
},
{
"name": "locator_jaw_upper",
"parent": "head_angle",
"pivot": [0, 7.75, -20.25],
"rotation": [-90, 0, 0],
"locators": {
"jaw_upper": [0, 7.75, -20.25]
}
},
{
"name": "locator_head",
"parent": "head_angle",
"pivot": [0, 15, -19.25],
"locators": {
"head": [0, 15, -19.25]
}
},
{
"name": "locator_item_hat",
"parent": "head_angle",
"pivot": [0, 16, -17.25],
"locators": {
"item_hat": [0, 16, -17.25]
}
},
{
"name": "locator_face",
"parent": "head_angle",
"pivot": [0, 10.5, -27.25],
"locators": {
"face": [0, 10.5, -27.25]
}
},
{
"name": "locator_item_face",
"parent": "head_angle",
"pivot": [0, 10.25, -27.25],
"locators": {
"item_face": [0, 10.25, -27.25]
}
},
{
"name": "left_front_leg",
"parent": "body",
@@ -285,6 +414,15 @@
{"origin": [9, 0, -11.5], "size": [0, 2, 2], "pivot": [9, 0, -9.5], "rotation": [0, -2.5, 0], "uv": [16, 36]}
]
},
{
"name": "locator_foot_primary2",
"parent": "left_front_foot",
"pivot": [7.5, 1, -7.1],
"rotation": [90, 0, 0],
"locators": {
"foot_primary2": [7.5, 1, -7.1]
}
},
{
"name": "right_front_leg",
"parent": "body",
@@ -310,6 +448,15 @@
{"origin": [-9, 0, -11.5], "size": [0, 2, 2], "pivot": [-9, 0, -9.5], "rotation": [0, 2.5, 0], "uv": [12, 34]}
]
},
{
"name": "locator_foot_primary1",
"parent": "right_front_foot",
"pivot": [-7.5, 1, -7.1],
"rotation": [90, 0, 0],
"locators": {
"foot_primary1": [-7.5, 1, -7.1]
}
},
{
"name": "left_back_leg",
"parent": "body",
@@ -335,6 +482,15 @@
{"origin": [8.5, 0, 2], "size": [0, 2, 2], "pivot": [8.5, 0, 4], "rotation": [0, -5, 0], "uv": [0, 36]}
]
},
{
"name": "locator_foot_secondary2",
"parent": "left_back_foot",
"pivot": [7.5, 1, 7.15],
"rotation": [90, 0, 0],
"locators": {
"foot_secondary2": [7.5, 1, 7.15]
}
},
{
"name": "right_back_leg",
"parent": "body",
@@ -359,6 +515,47 @@
{"origin": [-5.5, 0, 2], "size": [0, 2, 2], "pivot": [-5.5, 0, 4], "rotation": [0, -5, 0], "uv": [0, 8]},
{"origin": [-8.5, 0, 2], "size": [0, 2, 2], "pivot": [-8.5, 0, 4], "rotation": [0, 5, 0], "uv": [0, 6]}
]
},
{
"name": "locator_foot_secondary",
"parent": "right_back_foot",
"pivot": [-7.5, 1, 7.15],
"rotation": [90, 0, 0],
"locators": {
"foot_secondary": [-7.5, 1, 7.15]
}
},
{
"name": "locator_middle",
"parent": "body",
"pivot": [0, 12, 0],
"locators": {
"middle": [0, 12, 0]
}
},
{
"name": "locator_target",
"parent": "body",
"pivot": [0, 6.75, -31],
"locators": {
"target": [0, 6.75, -31]
}
},
{
"name": "locator_root",
"parent": "rhyhorn",
"pivot": [0, 0, 0],
"locators": {
"root": [0, 0, 0]
}
},
{
"name": "locator_top",
"parent": "rhyhorn",
"pivot": [0, 25.5, 0],
"locators": {
"top": [0, 25.5, 0]
}
}
]
}
@@ -90,6 +90,15 @@
{"origin": [24.5, 21, 1.5], "size": [2, 1, 1], "uv": [59, 37]}
]
},
{
"name": "locator_hand_secondary",
"parent": "left_lower_arm",
"pivot": [23.75, 22, 0],
"rotation": [0, -90, 0],
"locators": {
"hand_secondary": [23.75, 22, 0]
}
},
{
"name": "right_shoulder",
"parent": "upper_torso",
@@ -141,6 +150,23 @@
{"origin": [-26.5, 21, 1.5], "size": [2, 1, 1], "uv": [9, 0]}
]
},
{
"name": "locator_item",
"parent": "right_lower_arm",
"pivot": [-24.75, 22, 0],
"locators": {
"item": [-24.75, 22, 0]
}
},
{
"name": "locator_hand_primary",
"parent": "right_lower_arm",
"pivot": [-23.75, 22, 0],
"rotation": [0, 90, 0],
"locators": {
"hand_primary": [-23.75, 22, 0]
}
},
{
"name": "backspike2",
"parent": "upper_torso",
@@ -160,17 +186,17 @@
{
"name": "head",
"parent": "upper_torso",
"pivot": [0, 25.75, 0.5]
"pivot": [0, 26, 0]
},
{
"name": "head_correction",
"parent": "head",
"pivot": [0, 25.75, 0.5]
"pivot": [0, 26, 0]
},
{
"name": "head_ai",
"parent": "head_correction",
"pivot": [0, 25.75, 0.5]
"pivot": [0, 26, 0]
},
{
"name": "head_angle",
@@ -202,6 +228,15 @@
{"origin": [-3.5, 25, -12.5], "size": [7, 3, 3], "uv": [0, 70]}
]
},
{
"name": "locator_jaw_lower",
"parent": "jaw",
"pivot": [0, 28, -9.75],
"rotation": [90, 0, 0],
"locators": {
"jaw2": [0, 28, -9.75]
}
},
{
"name": "eyes",
"parent": "head_angle",
@@ -224,6 +259,14 @@
{"origin": [6, 30.75, -10], "size": [0, 1, 1], "inflate": 0.02, "uv": [0, 1]}
]
},
{
"name": "locator_eye_left",
"parent": "left_pupil",
"pivot": [6, 31.25, -9.5],
"locators": {
"eye_left": [6, 31.25, -9.5]
}
},
{
"name": "left_eyelid",
"parent": "eyes",
@@ -317,6 +360,14 @@
{"origin": [-6, 30.75, -10], "size": [0, 1, 1], "inflate": 0.02, "uv": [0, 0]}
]
},
{
"name": "locator_eye_right",
"parent": "right_pupil",
"pivot": [-6, 31.25, -9.5],
"locators": {
"eye_right": [-6, 31.25, -9.5]
}
},
{
"name": "left_ear",
"parent": "head_angle",
@@ -351,6 +402,16 @@
{"origin": [-2, 31.68934, -14.07538], "size": [4, 4, 4], "uv": [100, 100]}
]
},
{
"name": "locator_horn",
"parent": "horn_rotation",
"pivot": [0, 33.675, -20],
"locators": {
"horn": [0, 33.675, -20],
"special": [0, 33.675, -20],
"physical": [0, 33.675, -20]
}
},
{
"name": "backspike4",
"parent": "head_angle",
@@ -359,6 +420,63 @@
{"origin": [0, 26.5, 1.5], "size": [0, 5, 6], "pivot": [0, 31, 2.25], "rotation": [30, 0, 0], "uv": [0, 29]}
]
},
{
"name": "locator_jaw_upper",
"parent": "head_angle",
"pivot": [0, 28.25, -7.75],
"rotation": [-90, 0, 0],
"locators": {
"jaw1": [0, 28.25, -7.75]
}
},
{
"name": "locator_head",
"parent": "head_angle",
"pivot": [0, 36, -7.5],
"locators": {
"head": [0, 36, -7.5]
}
},
{
"name": "locator_item_hat",
"parent": "head_angle",
"pivot": [0, 36, -5],
"locators": {
"item_hat": [0, 36, -5]
}
},
{
"name": "locator_face",
"parent": "head_angle",
"pivot": [0, 31, -13.5],
"locators": {
"face": [0, 31, -13.5]
}
},
{
"name": "locator_item_face",
"parent": "head_angle",
"pivot": [0, 30.75, -13.5],
"locators": {
"item_face": [0, 30.75, -13.5]
}
},
{
"name": "locator_mouth",
"parent": "head_angle",
"pivot": [0, 27.5, -5.75],
"locators": {
"mouth": [0, 27.5, -5.75]
}
},
{
"name": "locator_seat_1",
"parent": "upper_torso",
"pivot": [0, 26.25, 9],
"locators": {
"seat_1": [0, 26.25, 9]
}
},
{
"name": "tail",
"parent": "torso",
@@ -392,6 +510,24 @@
{"origin": [-2, 5.25, 28.5], "size": [4, 4, 9], "uv": [89, 87]}
]
},
{
"name": "locator_tail_tip",
"parent": "tail4",
"pivot": [0, 7.25, 37.5],
"rotation": [0, 180, 0],
"locators": {
"tail_tip": [0, 7.25, 37.5]
}
},
{
"name": "locator_tail",
"parent": "tail3",
"pivot": [0, 7.25, 25],
"rotation": [0, 180, 0],
"locators": {
"tail": [0, 7.25, 25]
}
},
{
"name": "backspike",
"parent": "torso",
@@ -436,6 +572,15 @@
{"origin": [10, -0.25, -13], "size": [2, 2, 3], "pivot": [10, 0.75, -10], "rotation": [0, -5, 0], "uv": [29, 78]}
]
},
{
"name": "locator_foot_secondary",
"parent": "left_foot",
"pivot": [9, 1, -5.75],
"rotation": [90, 0, 0],
"locators": {
"foot_secondary": [9, 1, -5.75]
}
},
{
"name": "right_upper_leg",
"parent": "body",
@@ -471,6 +616,47 @@
{"origin": [-8, -0.25, -13], "size": [2, 2, 3], "pivot": [-8, 0.75, -10], "rotation": [0, -5, 0], "uv": [77, 0]},
{"origin": [-12, -0.25, -13], "size": [2, 2, 3], "pivot": [-10, 0.75, -10], "rotation": [0, 5, 0], "uv": [26, 57]}
]
},
{
"name": "locator_foot_primary",
"parent": "right_foot",
"pivot": [-9, 1, -5.75],
"rotation": [90, 0, 0],
"locators": {
"foot_primary": [-9, 1, -5.75]
}
},
{
"name": "locator_middle",
"parent": "body",
"pivot": [0, 17, -2],
"locators": {
"middle": [0, 17, -2]
}
},
{
"name": "locator_target",
"parent": "body",
"pivot": [0, 17, -16],
"locators": {
"target": [0, 17, -16]
}
},
{
"name": "locator_top",
"parent": "rhydon",
"pivot": [0, 38, 0],
"locators": {
"top": [0, 38, 0]
}
},
{
"name": "locator_root",
"parent": "rhydon",
"pivot": [0, 0, 0],
"locators": {
"root": [0, 0, 0]
}
}
]
}

Some files were not shown because too many files have changed in this diff Show More