Compare commits

..
Author SHA1 Message Date
JPAKx4 9163046b45 Merge branch 'main' into irl_date_time
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
2025-08-08 21:03:49 -05:00
JPAK 06732e89fd Update Changelog and error handling. 2025-04-22 14:27:40 -05:00
JPAK 9e8d6a4f39 Add an option for String timezones to "date_of" and "date_local_time" functions. 2025-04-22 12:33:45 -05:00
JPAK 662efa55c6 Fix imports. 2025-04-22 12:10:39 -05:00
JPAK efb9e538f3 Merge branch 'main' into irl_date_time_molang
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/Category.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2025-04-22 12:03:37 -05:00
JPAK 42c711cfc7 Fix config and add langs for config. 2025-02-07 14:35:59 -06:00
JPAK 681d5f8bcc Merge remote-tracking branch 'origin/irl_date_time' into irl_date_time_molang
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/config/Category.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2025-02-07 12:27:49 -06:00
JordanandJPAK a52d8452b4 Revert "Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition."
This reverts commit b0be0b2dca
2025-02-07 12:23:13 -06:00
JPAK e65298b587 Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition. 2025-02-07 12:23:13 -06:00
JPAK f8c09b8e26 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2025-02-07 12:23:11 -06:00
JPAK 5d1019afd5 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2025-02-07 12:22:28 -06:00
JPAK eb9bd116b2 Merge remote-tracking branch 'origin/irl_date_time' into irl_date_time_molang
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2024-12-29 14:28:06 -06:00
JPAK 70bf4d6654 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2024-12-29 14:14:43 -06:00
Jordan 080a5b9c74 Revert "Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition."
This reverts commit b0be0b2dca
2024-12-27 19:45:08 +00:00
JPAK b0be0b2dca Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition. 2024-12-27 13:35:41 -06:00
JPAK d8300436c9 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2024-12-26 22:25:15 -06:00
127 changed files with 1857 additions and 13548 deletions
+12 -8
View File
@@ -40,9 +40,7 @@
- Ground type Pokémon are immune to lightning damage
- Added functionality to Everstone when held by a Pokémon; suppresses evolution notification and hides evolve button in summary interface.
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
- Galarica Nut bushes now generate on beaches.
- Some Pokémon now pitch their bodies in the direction they're moving.
- Added `/runmolang <molang> [<npc>|<player>|<pokemon>]` command that executes a MoLang expression with the provided options as environment variables, as well as the entity (as `q.entity`) that executed the command.
- Galarica Nut bushes now generate on beaches
### Pokémon Added
@@ -196,6 +194,17 @@
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
- Added the `is_standing_on_blocks(depth, blocks...)` MoLang function, which returns whether the specified entity is standing on a specific block or set of blocks. Example usage: `q.is_standing_on_blocks(2, minecraft:sand)`
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added molang particle queries for getting distance to targeted entities
- Added debug renderer for poseable entity locators
- Added time zone as a config option
- Added an optional MoValue to either use system timezone when set as true, or use the supplied string as a timezone for MoLang functions `date_local_time` and `date_of`. If the timezone supplied fails to parse this function now returns `0.0`.
### Additions
- Added time zone as a config option
- Added optional boolean to use time zone for MoLang function "date_local_time" and "date_of"
- Made Blocks of Gold count as Big Nuggets when held by a Pokémon (for Fling functionality)
- Players can now eat Sweet and Tart Apples, Whipped Dreams, and the Alcremie Sweets.
- Updated Sweet and Tart Apple sprites
@@ -242,11 +251,6 @@
- Offset in EmitterShape now ignores scale to be more like Blockbench by default. You can get this behaviour back by adding `"scale": true` in the `attachment_options` property in most EmitterShapes.
- Not specifying a dex in `/pokedex printcalculations {player} {dex}` will now print the National Dex statistics instead of showing all dexes. `/pokedex printcalculations {player} all` is how to view all dex statistics in one command.
- Removed Braised Vivichoke
- Updated Pokémon state icons shown in party interfaces
- MoLang `face` function can now be run on any `PosableEntity` (Like Pokémon!) instead of just NPCs.
- MoLang `run_action_effect` now works on Pokémon.
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
- Added entity (as `q.entity`) that executed the command to the `executemolangscript` command.
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -9,6 +9,15 @@
package com.cobblemon.mod.common;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import java.util.function.Function;
public interface OrientationControllable {
OrientationController getOrientationController();
@@ -1,18 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
import org.joml.Vector3f;
public interface PlayerDuck {
void setDriverInput(Vector3f driverInput);
Vector3f getDriverInput();
void setLastSentDriverInput(Vector3f driverInput);
Vector3f getLastSentDriverInput();
}
@@ -8,44 +8,17 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
import com.cobblemon.mod.common.api.riding.Seat;
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Mixin;
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;
@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity implements OrientationControllable {
public LivingEntityMixin(EntityType<?> entityType, Level level) {
super(entityType, level);
}
@Unique
private final OrientationController cobblemon$orientationController = new OrientationController((LivingEntity) (Object) this);
public OrientationController getOrientationController() {
return cobblemon$orientationController;
}
public abstract class LivingEntityMixin {
@Inject(method = "onEffectRemoved", at = @At(value = "TAIL"))
private void cobblemon$onEffectRemoved(MobEffectInstance effect, CallbackInfo ci) {
@@ -55,67 +28,4 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
}
}
@Override
public @NotNull HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
PokemonClientDelegate delegate = (PokemonClientDelegate) pokemonEntity.getDelegate();
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
Vec3 eyePosition = cobblemon$getEyePosition(partialTicks, pokemonEntity, locatorOffset);
Vec3 viewVector = this.getViewVector(partialTicks);
Vec3 viewDistanceVector = eyePosition.add(viewVector.x * hitDistance, viewVector.y * hitDistance, viewVector.z * hitDistance);
return this.level()
.clip(
new ClipContext(
eyePosition, viewDistanceVector, ClipContext.Block.OUTLINE, hitFluids ? net.minecraft.world.level.ClipContext.Fluid.ANY : net.minecraft.world.level.ClipContext.Fluid.NONE, this
)
);
}
return super.pick(hitDistance, partialTicks, hitFluids);
}
@Unique
private Vec3 cobblemon$getEyePosition(float partialTicks, PokemonEntity pokemonEntity, Vec3 locatorOffset) {
OrientationController controller = this.getOrientationController();
float currEyeHeight = this.getEyeHeight();
Vec3 offset = locatorOffset.add(pokemonEntity.position());
if (controller.isActive() && controller.getOrientation() != null) {
Quaternionf orientation = controller.getRenderOrientation(partialTicks);
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f)));
offset.add(rotatedEyeHeight);
}
return offset;
}
@Inject(method = "tick", at = @At("HEAD"))
private void cobblemon$updateRenderOrientation(CallbackInfo ci) {
this.cobblemon$orientationController.tick();
}
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (cobblemon$orientationController.getActive()) {
this.absMoveTo(x, y, z);
this.setYRot(yaw % 360.0f);
this.setXRot(pitch % 360.0f);
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
} else {
this.absMoveTo(x, y, z);
this.absRotateTo(yaw, pitch);
}
}
}
@@ -18,7 +18,6 @@ import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.api.storage.party.PartyStore;
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
@@ -48,7 +47,6 @@ import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@@ -63,12 +61,7 @@ import java.util.*;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck {
@Unique
private Vector3f cobblemon$driverInput;
@Unique
private Vector3f cobblemon$lastSentDriverInput;
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable {
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@@ -286,25 +279,4 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
public OrientationController getOrientationController() {
return cobblemon$orientationController;
}
@Override
public void setDriverInput(Vector3f driverInput) {
cobblemon$driverInput = driverInput;
}
@Override
public Vector3f getDriverInput() {
return cobblemon$driverInput;
}
@Override
public void setLastSentDriverInput(Vector3f lastSentDriverInput) {
cobblemon$lastSentDriverInput = lastSentDriverInput;
}
@Override
public Vector3f getLastSentDriverInput() {
return cobblemon$lastSentDriverInput;
}
}
@@ -12,11 +12,9 @@ import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.net.NetworkPacket;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.mixin.accessor.ChunkMapAccessor;
import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -28,7 +26,6 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -58,7 +55,6 @@ public abstract class ServerEntityMixin {
private void cobblemon$sendChanges(CallbackInfo ci) {
cobblemon$sendOrientationChanges();
cobblemon$sendRidingStateChanges();
cobblemon$sendDriverInput();
}
private void cobblemon$sendRidingStateChanges() {
@@ -86,19 +82,6 @@ public abstract class ServerEntityMixin {
cobblemon$broadcast(new ClientboundUpdateOrientationPacket(currOrientation, currActive, entity.getId()));
}
private void cobblemon$sendDriverInput() {
if (!(this.entity instanceof ServerPlayer serverPlayer)) return;
if(!(serverPlayer.getVehicle() instanceof PokemonEntity)) return;
Vector3f driverInput = ((PlayerDuck)serverPlayer).getDriverInput();
if(driverInput == null) return;
// If no change in input is detected then don't send a new packet
Vector3f lastSentDriverInput = ((PlayerDuck)serverPlayer).getLastSentDriverInput();
if (driverInput.equals(lastSentDriverInput)) return;
((PlayerDuck)serverPlayer).setLastSentDriverInput(driverInput);
cobblemon$broadcast(new ClientboundUpdateDriverInputPacket(driverInput, entity.getId()));
}
private void cobblemon$broadcast(NetworkPacket<?> packet) {
if (!(entity.level() instanceof ServerLevel level)) return;
ChunkMap chunkMap = level.getChunkSource().chunkMap;
@@ -114,4 +97,5 @@ public abstract class ServerEntityMixin {
}
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.network.ServerPlayerConnection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@@ -19,7 +18,4 @@ import java.util.Set;
public interface TrackedEntityAccessor {
@Accessor("seenBy")
Set<ServerPlayerConnection> getSeenBy();
@Accessor("serverEntity")
ServerEntity getServerEntity();
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.model.HumanoidModel;
@@ -60,9 +59,9 @@ public class HumanoidModelMixin {
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof AbstractClientPlayer) {
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
return behaviour.shouldRotateRiderHead(settings, state, pokemonEntity);
return behaviour.shouldRotatePlayerHead(settings, state, pokemonEntity);
});
if (!shouldRotatePlayerHead) {
@@ -74,9 +73,7 @@ public class HumanoidModelMixin {
}
}
if (!(entity instanceof AbstractClientPlayer player)) return;
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, player, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, (AbstractClientPlayer) entity, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.entity.MountedLivingEntityRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntityRenderer.class)
public class LivingEntityRendererMixin<T extends LivingEntity, M extends EntityModel<T>> {
@Inject(
method = "setupRotations",
at = @At("HEAD")
)
private void cobblemon$modifyRoll(LivingEntity entity, PoseStack poseStack, float bob, float yBodyRot, float partialTick, float scale, CallbackInfo ci) {
if (entity.isPassenger()) {
var vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
MountedLivingEntityRenderer.INSTANCE.render(entity, pokemonEntity, poseStack, bob, yBodyRot, partialTick, scale);
}
}
}
}
@@ -17,10 +17,8 @@ import com.cobblemon.mod.common.client.render.MatrixWrapper;
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
@@ -30,7 +28,6 @@ import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
@@ -86,23 +83,6 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
}
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateDriverInputRideTick(CallbackInfo ci) {
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
// Gather driver Input
float vertInput = this.jumping ? 1.0f : (((Player)this).isShiftKeyDown() ? -1.0f : 0.0f);
Vector3f driverInput = new Vector3f(Math.signum(this.xxa), vertInput, Math.signum(this.zza));
// Check if player input has changed. If it has then update the last sent and send it
Vector3f lastSentDriverInput = ((PlayerDuck)this).getLastSentDriverInput();
if (driverInput.equals(lastSentDriverInput)) return;
((PlayerDuck)this).setLastSentDriverInput(driverInput);
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(driverInput));
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
var playerVehicle = player.getVehicle();
@@ -147,7 +127,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
return super.pick(hitDistance, partialTicks, hitFluids);
}
Vec3 locatorOffset = locator != null ? new Vec3(locator.getMatrix().getTranslation(new Vector3f())) : Vec3.ZERO;
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
OrientationController controller = this.getOrientationController();
@@ -9,10 +9,12 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
@@ -53,4 +55,17 @@ public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractC
playerModel.crouching = false;
}
}
@Inject(
method = "setupRotations(Lnet/minecraft/client/player/AbstractClientPlayer;Lcom/mojang/blaze3d/vertex/PoseStack;FFFF)V",
at = @At("HEAD")
)
private void cobblemon$modifyRoll(AbstractClientPlayer player, PoseStack poseStack, float a, float b, float partialTicks, float i, CallbackInfo ci) {
if (player.isPassenger()) {
var vehicle = player.getVehicle();
if (vehicle instanceof PokemonEntity entity) {
MountedPlayerRenderer.INSTANCE.render(player, entity, poseStack, a, b, partialTicks, i);
}
}
}
}
@@ -59,7 +59,6 @@ object CobblemonCommands {
ApplyPlayerTextureCommand.register(dispatcher)
RenameBoxCommand.register(dispatcher)
ChangeBoxWallpaperCommand.register(dispatcher)
RunMolangCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
SpectateBattleCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
@@ -41,7 +41,6 @@ import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHand
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.riding.ClientboundUpdateDriverInputHandler
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
@@ -84,7 +83,6 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityP
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
@@ -175,7 +173,6 @@ import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPa
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
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
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
@@ -226,7 +223,6 @@ import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkH
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.riding.DismountPokemonPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
import com.cobblemon.mod.common.net.serverhandling.storage.BenchMoveHandler
@@ -469,7 +465,6 @@ object CobblemonNetwork {
// Riding
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
list.add(PacketRegisterInfo(ClientboundUpdateDriverInputPacket.ID, ClientboundUpdateDriverInputPacket::decode, ClientboundUpdateDriverInputHandler))
return list
}
@@ -576,7 +571,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
// Cooking
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
@@ -38,7 +38,6 @@ import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.NPCProvider
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
import com.cobblemon.mod.common.api.npc.NPCClasses
import com.cobblemon.mod.common.api.npc.configuration.interaction.DialogueNPCInteractionConfiguration
import com.cobblemon.mod.common.api.npc.configuration.interaction.ScriptNPCInteractionConfiguration
@@ -144,6 +143,12 @@ import net.minecraft.world.level.levelgen.Heightmap
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.*
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
@@ -290,16 +295,44 @@ object MoLangFunctions {
DoubleValue(System.currentTimeMillis())
},
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
"date_local_time" to java.util.function.Function { _ ->
val time = System.currentTimeMillis()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
"date_local_time" to java.util.function.Function { params ->
val useTimeZone = params.get<MoValue>(0)
var timezone: ZoneId? = null
if (useTimeZone is DoubleValue){
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
} else if (useTimeZone is StringValue){
try {
timezone = ZoneId.of(useTimeZone.value)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
return@Function DoubleValue.ZERO
}
}
val date: LocalDateTime = if (timezone != null) LocalDateTime.now(timezone) else LocalDateTime.now()
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(date)
StringValue(formatted)
},
"date_of" to java.util.function.Function { params ->
val time = params.getDouble(0).toLong()
val useTimeZone = params.get<MoValue>(1)
var timezone: ZoneId? = null
if (useTimeZone is DoubleValue){
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
} else if (useTimeZone is StringValue){
try {
timezone = ZoneId.of(useTimeZone.value)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
return@Function DoubleValue.ZERO
}
}
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
val instant: Instant = date.toInstant()
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(LocalDateTime.ofInstant(instant,
timezone ?: ZoneOffset.systemDefault()
))
StringValue(formatted)
},
"date_is_after" to java.util.function.Function { params ->
@@ -661,7 +694,7 @@ object MoLangFunctions {
map
}
)
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ stack, registryAccess ->
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
val holder = itemRegistry.wrapAsHolder(stack.item)
@@ -680,7 +713,7 @@ object MoLangFunctions {
}
)
val entityFunctions: MutableList<(Entity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val entityFunctions: MutableList<(Entity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
@@ -708,9 +741,6 @@ object MoLangFunctions {
val owner = entity.owner
return@put owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
}
map.put("delta_movement") {
return@put listOf(entity.deltaMovement.x, entity.deltaMovement.y, entity.deltaMovement.z).asArrayValue(::DoubleValue)
}
if (entity is MoLangScriptingEntity) {
map.put("add_callback") { params ->
@@ -855,12 +885,14 @@ object MoLangFunctions {
}
map.put("is_standing_on_blocks") { params ->
val depth = params.getDouble(0).toInt()
val blockStrings: MutableSet<String> = mutableSetOf()
val blocks: MutableSet<Block> = mutableSetOf()
for (blockIndex in 1..<params.params.size) {
blockStrings.add(params.getString(blockIndex))
val blockString = params.getString(blockIndex)
val block = BuiltInRegistries.BLOCK.get(blockString.asIdentifierDefaultingNamespace("minecraft"))
blocks.add(block)
}
return@put if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
return@put if (entity.isStandingOn(blocks, depth)) DoubleValue.ONE else DoubleValue.ZERO
}
if (entity is PosableEntity) {
map.put("play_animation") { params ->
@@ -886,10 +918,6 @@ object MoLangFunctions {
return@put DoubleValue.ONE
}
}
map.put("face") { params -> ObjectValue(ReferenceDialogueFaceProvider(entity.id, params.getBooleanOrNull(0) != false))
}
}
if (entity is Schedulable) {
map.put("run_molang_after") { params ->
@@ -923,7 +951,7 @@ object MoLangFunctions {
}
)
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val livingEntityFunctions: MutableList<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_living_entity") { DoubleValue.ONE }
@@ -1059,6 +1087,7 @@ object MoLangFunctions {
map.put("name") { StringValue(npc.name.string) }
map.put("level") { DoubleValue(npc.level) }
map.put("has_aspect") { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
map.put("face") { params -> ObjectValue(ReferenceDialogueFaceProvider(npc.id, params.getBooleanOrNull(0) != false)) }
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() } }
@@ -1664,8 +1693,6 @@ object MoLangFunctions {
map.put("get_alt_pose") { StringValue(pokemonEntity.getAltPose()) }
map.put("is_gliding") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("gliding"))) }
map.put("is_sprinting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("sprinting"))) }
map.put("is_drifting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("drifting"))) }
map.put("is_powered_drifting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("powered_drifting"))) }
map.put("in_air") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("in_air"))) }
map.put("is_wild") { DoubleValue(pokemonEntity.ownerUUID == null) }
map.put("is_in_party") { DoubleValue(pokemonEntity.pokemon.storeCoordinates.get()?.store is PartyStore) }
@@ -1683,34 +1710,6 @@ object MoLangFunctions {
map.put("pasture_conflict_enabled") {
DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT))
}
map.put("run_action_effect") { params ->
val runtime = MoLangRuntime().setup()
runtime.environment.cloneFrom(params.environment)
runtime.withQueryValue("entity", pokemonEntity.struct)
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
if (actionEffect != null) {
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(TargetsProvider(pokemonEntity)),
runtime = runtime,
level = pokemonEntity.level()
)
// This was taken from NPC run_action_effect but this variable isn't used, so I just commented it out.
// pokemonEntity.actionEffect = context
pokemonEntity.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
pokemonEntity.brain.setActiveActivityIfPossible(CobblemonActivities.ACTION_EFFECT)
actionEffect.run(context).thenRun {
val pokemonActionEffect = pokemonEntity.brain.getMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT).orElse(null)
if (pokemonActionEffect == context && pokemonEntity.brain.isActive(CobblemonActivities.ACTION_EFFECT)) {
pokemonEntity.brain.eraseMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT)
//pokemonEntity.actionEffect = null
}
}
return@put DoubleValue.ONE
}
return@put DoubleValue.ZERO
}
map
}
)
@@ -2021,7 +2020,7 @@ object MoLangFunctions {
}
)
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ props ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -2079,7 +2078,7 @@ object MoLangFunctions {
}
)
val evolutionFunctions: MutableList<(Evolution) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val evolutionFunctions: MutableList<(Evolution) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ evolution ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -10,12 +10,14 @@ package com.cobblemon.mod.common.api.orientation
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.LivingEntity
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.asin
import kotlin.math.sign
open class OrientationController(val entity: LivingEntity) {
@@ -132,8 +132,6 @@ object CobblemonPermissions {
@JvmStatic
val ABANDON_MULTITEAM = this.create("${COMMAND_PREFIX}abandonmultiteam", PermissionLevel.NONE)
@JvmStatic
val RUN_MOLANG = this.create("${COMMAND_PREFIX}runmolang", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val COBBLEMON_CONFIG_RELOAD = this.create("${COMMAND_PREFIX}cobblemonconfig.reload", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -87,7 +87,7 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
fun shouldRotatePokemonHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
fun shouldRotateRiderHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
@@ -23,19 +22,15 @@ object RidingBehaviours {
init {
register(BirdBehaviour.KEY, BirdBehaviour())
register(BirdNoRollBehaviour.KEY, BirdNoRollBehaviour())
register(DolphinBehaviour.KEY, DolphinBehaviour())
register(HorseBehaviour.KEY, HorseBehaviour())
register(BoatBehaviour.KEY, BoatBehaviour())
register(GliderBehaviour.KEY, GliderBehaviour())
register(HelicopterBehaviour.KEY, HelicopterBehaviour())
register(JetBehaviour.KEY, JetBehaviour())
register(JetNoRollBehaviour.KEY, JetNoRollBehaviour())
register(BurstBehaviour.KEY, BurstBehaviour())
register(VehicleBehaviour.KEY, VehicleBehaviour())
register(MinekartBehaviour.KEY, MinekartBehaviour())
register(HoverBehaviour.KEY, HoverBehaviour())
register(RocketBehaviour.KEY, RocketBehaviour())
register(CompositeBehaviour.KEY, CompositeBehaviour())
}
@@ -120,9 +120,9 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
return behaviour.canJump(settings, state, vehicle, driver)
}
override fun shouldRotateRiderHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
override fun shouldRotatePlayerHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
if (!isActive(settings, state, vehicle)) return false
return behaviour.shouldRotateRiderHead(settings, state, vehicle)
return behaviour.shouldRotatePlayerHead(settings, state, vehicle)
}
override fun shouldRotatePokemonHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
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.RideLoopSound
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -24,6 +25,7 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
@@ -105,6 +107,14 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
upForce -= 0.3
}
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
//Only limit altitude if altitude is not infinite
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
return velocity
@@ -119,7 +129,12 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
var glideSpeedChange = 0.0
val currSpeed = state.rideVelocity.get().length()
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
@@ -449,12 +464,12 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
return false
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity
): Boolean {
return false
return true
}
override fun getRideSounds(
@@ -1,518 +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.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
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
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.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
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 org.joml.Vector3f
import kotlin.math.*
class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState> {
companion object {
val KEY = cobblemonResource("air/birdnoroll")
}
override val key: ResourceLocation = KEY
override fun getRidingStyle(settings: BirdNoRollSettings, state: BirdNoRollState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<BirdNoRollSettings, BirdNoRollState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
override fun isActive(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun pose(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
return state.rideVelocity.get().length().toFloat()
}
override fun rotation(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
var yVel = 0.0
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
//Perform ride velocity update
calculateRideSpaceVel(settings, state, vehicle, driver)
//Translate ride space velocity to world space velocity.
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
upForce += -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
forwardForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
}
//Bring the ride out of the sky when stamina is depleted.
if (state.stamina.get() <= 0.0) {
upForce -= 0.3
}
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
return velocity
}
/*
* Calculates the change in the ride space vector due to player input and ride state
*/
fun calculateRideSpaceVel(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity, driver: Player) {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
var glideSpeedChange = 0.0
val currSpeed = state.rideVelocity.get().length()
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
//make sure it can't exceed top speed
val forwardInput = when {
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
activeInput = true
}
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
//Base glide speed change on current pitch of the ride.
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
glideSpeedChange = glideSpeedChange * 0.25
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
if (glideSpeedChange <= 0.0) {
//Ensures that a propelling force is still able to be applied when
//climbing in height
if (driver.zza <= 0) {
//speed decrease should be 2x speed increase?
//state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
lerp( newVelocity.z, 0.0,glideSpeedChange * -0.0166 * 2 )
)
}
} else {
// only add to the speed if it hasn't exceeded the current
//glide angles maximum amount of speed that it can give.
//state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
min(newVelocity.z + ((0.0166 * 2) * glideSpeedChange), glideTopSpeed)
)
}
}
//Vertical movement based on driver input.
val vertTopSpeed = topSpeed / 2.0
val vertInput = when {
driver.jumping && state.rideVelocity.get().length() < 0.3 -> 1.0
driver.isShiftKeyDown && state.rideVelocity.get().length() < 0.3 -> -1.0
else -> 0.0
}
if (vertInput != 0.0 && state.stamina.get() > 0.0) {
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
newVelocity.z)
activeInput = true
}
else {
newVelocity = Vec3(
newVelocity.x,
lerp(newVelocity.y, 0.0, vertTopSpeed / 20.0),
newVelocity.z)
}
//Check if the ride should be gliding
if (driver.isLocalPlayer) {
if (activeInput && state.stamina.get() > 0.0) {
state.gliding.set(false)
} else {
state.gliding.set(true)
}
}
//air resistance
if( abs(newVelocity.z) > 0) {
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.0015 * newVelocity.z.sign, newVelocity.z))
}
state.rideVelocity.set(newVelocity)
}
override fun angRollVel(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//limit rolling based on handling and current speed.
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
//TODO: Tie handling into yaw rate
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
val rotMin = 15.0
val rotLimit = max(handling * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
//val pitchedYawDelt = yawDeltaDeg - trueYawDelt
controller.applyGlobalYaw(trueYawDelt.toFloat())
//Calculate correcting roll force.
val correctionRate = 5.0
if(abs(controller.roll) > rotLimit && state.rideVelocity.get().length() < 0.2) {
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + ((abs(controller.roll) - rotLimit) / 180 - rotLimit) * controller.roll.sign * correctionRate * deltaTime * 0.01
)
} else {
state.currRollCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
)
}
//yaw, pitch, roll
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
}
override fun rotationOnMouseXY(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//TODO: hook up handling in parity with normal bird
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * ( 1.0 - min(sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
var pitchRot = yInput
// Pitch up globally
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
//yaw, pitch, roll
return Vec3.ZERO
}
/*
* Normalizes the given value between a min and a max.
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
* and 1.0 represents x is at or above it.
*/
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
require(max > min) { "max must be greater than min" }
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
}
override fun canJump(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
//Must I ensure that topspeed is greater than minimum?
val normalizedGlideSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
}
override fun useAngVelSmoothing(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
if (state.gliding.get()) {
return cobblemonResource("gliding")
}
return cobblemonResource("no_pose")
}
override fun inertia(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun turnOffOnGround(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotateRiderHead(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun getRideSounds(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: BirdNoRollSettings) = BirdNoRollState()
}
class BirdNoRollSettings : RidingBehaviourSettings {
override val key = BirdNoRollBehaviour.KEY
var infiniteAltitude: Expression = "false".asExpression()
private set
var infiniteStamina: Expression = "false".asExpression()
private set
//max y level for the ride
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(glidespeedExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteAltitude = buffer.readExpression()
infiniteStamina = buffer.readExpression()
glidespeedExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
}
}
class BirdNoRollState : RidingBehaviourState() {
var gliding = ridingState(false, Side.CLIENT)
var lastGlide = ridingState(-100L, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeFloat(stamina.get())
buffer.writeBoolean(gliding.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
gliding.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
super.reset()
gliding.set(false, forced = true)
lastGlide.set(-100L, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
}
override fun toString(): String {
return "BirdNoRollState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
}
override fun copy() = BirdNoRollState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.gliding.set(this.gliding.get(), forced = true)
it.lastGlide.set(this.lastGlide.get(), forced = true)
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is BirdNoRollState) return false
if (previous.gliding.get() != gliding.get()) return true
return super.shouldSync(previous)
}
}
@@ -184,7 +184,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
return false
}
override fun shouldRotateRiderHead(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
override fun shouldRotatePlayerHead(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
@@ -12,9 +12,9 @@ import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
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 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
@@ -269,11 +269,7 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
return false
}
override fun shouldRotateRiderHead(
settings: HelicopterSettings,
state: RidingBehaviourState,
vehicle: PokemonEntity
): Boolean {
override fun shouldRotatePlayerHead(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
return false
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
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.RideLoopSound
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -451,7 +452,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
return false
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -137,6 +138,14 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
//If stamina has run out then initiate forced glide down.
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
//Only limit altitude if altitude is not infinite
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(0.0, upForce, forwardForce)
return velocity
@@ -153,11 +162,17 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
val speed = state.rideVelocity.get().length()
//Give no altitude limit if at max jump stat.
val pushingHeightLimit = if (vehicle.runtime.resolveBoolean(settings.infiniteStamina)) false
else (vehicle.y >= altitudeLimit && vehicle.xRot <= 0)
//speed up and slow down based on input
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f && !pushingHeightLimit) {
//modify acceleration to be slower when at closer speeds to top speed
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
state.rideVelocity.set(
@@ -167,7 +182,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
)
)
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f || pushingHeightLimit)) {
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
@@ -367,7 +382,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
return false
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: JetSettings,
state: JetState,
vehicle: PokemonEntity
@@ -412,6 +427,9 @@ class JetSettings : RidingBehaviourSettings {
var infiniteStamina: Expression = "false".asExpression()
private set
var infiniteAltitude: Expression = "false".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
@@ -433,6 +451,7 @@ class JetSettings : RidingBehaviourSettings {
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
@@ -446,6 +465,7 @@ class JetSettings : RidingBehaviourSettings {
minSpeed = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
infiniteAltitude = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
@@ -481,6 +501,8 @@ class JetState : RidingBehaviourState() {
it.currSpeed.set(currSpeed.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
// it.currMouseXForce.set(currMouseXForce.get(), forced = true)
// it.currMouseYForce.set(currMouseYForce.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -1,477 +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.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import net.minecraft.resources.ResourceLocation
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 JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
companion object {
val KEY = cobblemonResource("air/jetnoroll")
}
override val key = KEY
override fun getRidingStyle(settings: JetNoRollSettings, state: JetNoRollState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<JetNoRollSettings, JetNoRollState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
override fun isActive(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
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: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity, driver: Player): Float {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
//retrieve minSpeed
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
state.stamina.set(1.0f)
return state.rideVelocity.get().length().toFloat()
}
//TODO: Move these functions to a riding util class.
/*
* Normalizes the current speed between minSpeed and maxSpeed.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
*/
private fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
}
override fun rotation(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
var upForce = 0.0
var forwardForce = 0.0
val controller = (driver as? OrientationControllable)?.orientationController
//Calculate ride space velocity
calculateRideSpaceVel(settings, state, vehicle, driver)
//Translate ride space velocity to world space velocity.
if (controller != null) {
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
}
//If stamina has run out then initiate forced glide down.
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
val velocity = Vec3(0.0, upForce, forwardForce)
return velocity
}
/*
* Calculates the change in the ride space vector due to player input and ride state
*/
private fun calculateRideSpaceVel(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
val speed = state.rideVelocity.get().length()
//speed up and slow down based on input
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
//modify acceleration to be slower when at closer speeds to top speed
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
)
)
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel) / 4), minSpeed)
)
)
} else if (driver.zza < 0.0 && speed > minSpeed) {
//modify deccel to be slower when at closer speeds to minimum speed
val deccelMod = max((normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
//Decelerate currently always a constant half of max acceleration.
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel * deccelMod) / 2), minSpeed)
)
)
}
}
override fun angRollVel(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
// //Cap at a rate of 5fps so frame skips dont lead to huge jumps
// val cappedDeltaTime = min(deltaTime, 0.2)
//
// //Get handling in degrees per second
// val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
//
// //Base the change off of deltatime.
// var handlingYaw = yawRotRate * (cappedDeltaTime)
//
// //apply stamina debuff if applicable
// handlingYaw *= if (state.stamina.get() > 0.0) 1.0 else 0.5
//
// //A+D to yaw
// val yawForce = driver.xxa * handlingYaw * -1
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
//TODO: figure out a cleaner solution to this issue of large jumps when skipping frames or lagging
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
val cappedDeltaTime = min(deltaTime, 0.2)
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
// Accumulate the mouse input
state.currMouseXForce.set((state.currMouseXForce.get() + (0.0015 * mouseX * invertRoll)).coerceIn(-1.0, 1.0))
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY * invertPitch)).coerceIn(-1.0, 1.0))
//Get handling in degrees per second
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
//convert it to delta time
handling *= (cappedDeltaTime)
//apply stamina debuff if applicable
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
var pitchRot = handling * state.currMouseYForce.get()
// Yaw
val yawRot = handling * 0.5 * state.currMouseXForce.get()
val controller = (driver as OrientationControllable).orientationController
controller.applyGlobalYaw(yawRot.toFloat())
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
state.currMouseYForce.set(0.0)
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
val mouseRotation = Vec3(0.0, pitchRot, 0.0)
// Have accumulated input begin decay when no input detected
if (abs(mouseX) == 0.0) {
// Have decay on roll be much stronger.
state.currMouseXForce.set(lerp(state.currMouseXForce.get(), 0.0, 0.02))
}
if (mouseY == 0.0) {
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.005))
}
//yaw, pitch, roll
return Vec3.ZERO
}
override fun canJump(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
//Must I ensure that topspeed is greater than minimum?
val normalizedSpeed = normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed)
//TODO: Determine if this should be based on max possible speed instead of top speed.
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
}
override fun useAngVelSmoothing(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun useRidingAltPose(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Double {
return 1.0
}
override fun shouldRoll(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun turnOffOnGround(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotateRiderHead(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun getRideSounds(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: JetNoRollSettings) = JetNoRollState()
override fun damageOnCollision(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
impactVec: Vec3
): Boolean {
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
}
}
class JetNoRollSettings : RidingBehaviourSettings {
override val key = JetNoRollBehaviour.KEY
var gravity: Expression = "0".asExpression()
private set
var minSpeed: Expression = "1.2".asExpression()
private set
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
private set
// Make configurable by json
var infiniteStamina: Expression = "false".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.8, 1.0)".asExpression()
private set
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
}
}
class JetNoRollState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.CLIENT)
var currMouseXForce = ridingState(0.0, Side.CLIENT)
var currMouseYForce = ridingState(0.0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeDouble(currSpeed.get())
buffer.writeFloat(stamina.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
currSpeed.set(buffer.readDouble(), forced = true)
}
override fun reset() {
super.reset()
currSpeed.set(0.0, forced = true)
currMouseXForce.set(0.0, forced = true)
currMouseYForce.set(0.0, forced = true)
}
override fun copy() = JetNoRollState().also {
it.currSpeed.set(currSpeed.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
// it.currMouseXForce.set(currMouseXForce.get(), forced = true)
// it.currMouseYForce.set(currMouseYForce.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is JetNoRollState) return false
if (previous.currSpeed != currSpeed) return true
return super.shouldSync(previous)
}
}
@@ -1,566 +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.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
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
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.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 org.joml.Matrix3f
import kotlin.math.*
class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
companion object {
val KEY = cobblemonResource("air/rocket")
}
override val key = KEY
override fun getRidingStyle(settings: RocketSettings, state: RocketState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<RocketSettings, RocketState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ ->
return@PoseOption state.boosting.get()
})
override fun isActive(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
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: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): PoseType {
return this.poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Float {
// Use this as a "tick" function and check to see if the driver is "boosting"
if(vehicle.level().isClientSide) {
//If the forward key is not held then it cannot be boosting
if(Minecraft.getInstance().options.keyUp.isDown()) {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
//If on the previous tick the boost key was held then don't change if the ride is boosting
if(state.boostIsToggleable.get() && boostKeyPressed) {
//flip the boosting state if boost key is pressed
state.boosting.set(!state.boosting.get())
}
//If the boost key is not held then next tick boosting is toggleable
state.boostIsToggleable.set(!boostKeyPressed)
} else {
//Turn off boost and reset boost params
state.boostIsToggleable.set(true)
state.boosting.set(false)
state.canSpeedBurst.set(true)
}
}
return state.rideVelocity.get().length().toFloat()
}
override fun updatePassengerRotation(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
return
}
override fun clampPassengerRotation(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
val lookYawLimit = 90.0f
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
val ticksBeforeCorrection = 40
if(state.prevCamYRot.get() == driver.yRot){
state.ticksNoInput.set(state.ticksNoInput.get() + 1)
} else {
state.ticksNoInput.set(0)
}
val correctionForce = if(state.ticksNoInput.get() >= ticksBeforeCorrection && f != 0.0f && state.boosting.get()) 1f * min(sqrt(lookYawLimit / (abs(f))), 10.0f) else 0.0f
driver.yRotO += g - f
driver.yRot = driver.yRot + g - f //- ( clamp(f.sign * correctionForce, -abs(f), abs(f)) )
//driver.yRot = driver.yRot + g - f - ( min )
state.prevCamYRot.set(driver.yRot)
driver.setYHeadRot(driver.yRot)
}
override fun rotation(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
var newMomentum = state.turnMomentum.get().toDouble()
// Grab turningAcceleration and divide 20 twice to get
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0f) / 20.0f * 2
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val maxTurnMomentum = 80.0f / 20.0f
// Base boost stats off of normal turning stats
val boostMaxTurnMomentum = maxTurnMomentum * 0.1f
val boostTurnInput = turnInput * 0.15f
if(state.boosting.get()) {
if(driver.xxa != 0.0f && abs(newMomentum + turnInput) < (boostMaxTurnMomentum)) { //If max momentum will not be exceeded then modulate
newMomentum += boostTurnInput
} else {
newMomentum = lerp(newMomentum, 0.0, 0.05)
}
} else {
if(driver.xxa == 0.0f) { //If no turning input then lerp to 0
newMomentum = lerp(newMomentum, 0.0, 0.05)
} else if(abs(newMomentum + turnInput) < maxTurnMomentum) { //If max momentum will not be exceeded then modulate
newMomentum += turnInput
}
}
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
}
override fun velocity(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
val retVel = calculateRideSpaceVel(settings, state, vehicle, driver)
state.rideVelocity.set(retVel)
return retVel
}
private fun calculateRideSpaceVel(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 2
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.5 * 2
var newVelocity = vehicle.deltaMovement
// Align the direction of movement with the world coordinate space so as not to modify it through turning
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.boosting.get()) {
//make sure it can't exceed top speed
val forwardInput = when {
vehicle.deltaMovement.horizontalDistance() > topSpeed && (driver.zza.sign == newVelocity.z.sign.toFloat()) -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
} else if ((state.stamina.get() > 0.0 && state.boosting.get())) {
val boostSpeed = topSpeed * 5
val boostAccel = if(newVelocity.length() < boostSpeed) accel * 3 else 0.0
val forwardInput = 1.0f
var burst = 0.0f
if(state.canSpeedBurst.get()) {
burst = 0.25f
state.canSpeedBurst.set(false)
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
newVelocity.z + (boostAccel * forwardInput.toDouble()) + burst)
}
//Vertical movement based on driver input.
val maxVertSpeed = 0.5f
var vertInput = 0.0
//If boosting then don't lose altitude
//otherwise propel up or fall
if (state.boosting.get() && !driver.jumping && vehicle.deltaMovement.y > (-maxVertSpeed * 0.25)) {
vertInput = -0.7
newVelocity = Vec3(
newVelocity.x,
newVelocity.y + accel * vertInput,
newVelocity.z)
} else if(state.boosting.get() && vehicle.deltaMovement.y < maxVertSpeed) {
vertInput = when {
driver.jumping -> 2.0
else -> 0.0
}
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * 0.5 * vertInput)),
newVelocity.z)
} else {
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
vertInput = 1.0
// Reset falldistance if upward motion is detected
vehicle.resetFallDistance()
} else {
vertInput = -2.5
}
if (state.stamina.get() > 0.0) {
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * vertInput)),
newVelocity.z)
}
}
return newVelocity
}
/*
* Normalizes the current speed between minSpeed and maxSpeed.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
*/
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
}
override fun angRollVel(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
//Smooth out mouse input.
val smoothingSpeed = 4
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
//yaw, pitch, roll
return Vec3(0.0, yInput, xInput)
}
override fun canJump(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Float {
//Retrieve stamina from state and use it to set the "stamina bar"
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Float {
if (state.boosting.get()) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//val exceededSpeed = max(vehicle.deltaMovement.length() - topSpeed, 0.0)
//TODO: Remove this magic number and get a better comparison for boost top speed
val normalizedBoostSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, topSpeed * 3)
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
} else {
return 1.0f
}
}
override fun useAngVelSmoothing(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Double {
return 1.0
}
override fun shouldRoll(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun turnOffOnGround(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun dismountOnShift(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotateRiderHead(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun getRideSounds(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: RocketSettings) = RocketState()
}
class RocketSettings : RidingBehaviourSettings {
override val key = RocketBehaviour.KEY
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3) * 0.25".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', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
private set
// Controls the acceleration of the turning speed since the max turning speed is locked at 60 degrees/second
// So this is a range of 60 degrees/second per second to 20. Meaning at max stats when holding a direction it
// takes a full second to be turning at max speed.
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
}
}
class RocketState : RidingBehaviourState() {
var boosting = ridingState(false, Side.BOTH)
var boostIsToggleable = ridingState(false, Side.BOTH)
var canSpeedBurst = ridingState(false, Side.BOTH)
var prevCamYRot: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
var ticksNoInput: SidedRidingState<Int> = ridingState(0, Side.CLIENT)
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
override fun reset() {
super.reset()
boosting.set(false, forced = true)
boostIsToggleable.set(false, forced = true)
canSpeedBurst.set(true, forced = true)
prevCamYRot.set(0.0f, forced = true)
ticksNoInput.set(0, forced = true)
turnMomentum.set(0.0f, forced = true)
}
override fun copy() = RocketState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.boosting.set(this.boosting.get(), forced = true)
it.boostIsToggleable.set(this.boosting.get(), forced = true)
it.canSpeedBurst.set(this.canSpeedBurst.get(), forced = true)
it.prevCamYRot.set(this.prevCamYRot.get(), forced = true)
it.ticksNoInput.set(this.ticksNoInput.get(), forced = true)
it.turnMomentum.set(this.turnMomentum.get(), forced = true)
}
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(boosting.get())
buffer.writeBoolean(boostIsToggleable.get())
buffer.writeBoolean(canSpeedBurst.get())
buffer.writeFloat(prevCamYRot.get())
buffer.writeInt(ticksNoInput.get())
buffer.writeFloat(turnMomentum.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
boosting.set(buffer.readBoolean(), forced = true)
boostIsToggleable.set(buffer.readBoolean(), forced = true)
canSpeedBurst.set(buffer.readBoolean(), forced = true)
prevCamYRot.set(buffer.readFloat(), forced = true)
ticksNoInput.set(buffer.readInt(), forced = true)
turnMomentum.set(buffer.readFloat(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is RocketState) return false
if (previous.boosting.get() != boosting.get()) return true
if (previous.boostIsToggleable.get() != boostIsToggleable.get()) return true
if (previous.canSpeedBurst.get() != canSpeedBurst.get()) return true
if (previous.prevCamYRot.get() != prevCamYRot.get()) return true
if (previous.ticksNoInput.get() != ticksNoInput.get()) return true
if (previous.turnMomentum.get() != turnMomentum.get()) return true
return super.shouldSync(previous)
}
}
@@ -107,13 +107,13 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
}
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: CompositeSettings,
state: CompositeState,
vehicle: PokemonEntity
): Boolean {
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
behaviour.shouldRotateRiderHead(behaviourSettings, behaviourState, vehicle)
behaviour.shouldRotatePlayerHead(behaviourSettings, behaviourState, vehicle)
}
}
@@ -45,7 +45,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
): Boolean {
if (state.activeBehaviour.get() == defaultSettings.key) return false
if (!vehicle.onGround()) return false
if (state.lastTransition.get() + 5 >= vehicle.level().gameTime) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
return defaultBehaviour.isActive(defaultSettings, state.defaultBehaviourState, vehicle)
}
@@ -58,7 +58,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
): Boolean {
if (state.activeBehaviour.get() == alternativeSettings.key) return false
if (!driver.jumping) return false
if (state.lastTransition.get() + 5 >= vehicle.level().gameTime) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
return alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, vehicle)
}
@@ -83,15 +83,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
// Use this as a "tick" function and calculate sprinting and inAir state here
state.sprinting.set(driver.isSprinting)
inAirCheck(state, vehicle)
return state.rideVelocity.get().length().toFloat()
}
fun inAirCheck(
state: HorseState,
vehicle: PokemonEntity,
) {
// Check both vertical movement and if there are blocks below.
val posBelow = vehicle.blockPosition().below()
val blockStateBelow = vehicle.level().getBlockState(posBelow)
@@ -100,10 +92,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// val level = vehicle.level()
// val toesBox = vehicle.boundingBox.move(0.0, -0.1, 0.0)
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
return state.rideVelocity.get().length().toFloat()
}
override fun updatePassengerRotation(
@@ -111,7 +107,26 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
state: HorseState,
vehicle: PokemonEntity,
driver: LivingEntity
) { return }
) {
//Take the inverse so that it cancels out how
//much the entity rotates.
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val maxYawDiff = 90.0f
//Normalize the current rotation diff
val rotMod = Mth.wrapDegrees(driver.yRot - vehicle.yRot) / maxYawDiff
val rotAmount = 10.0f
//Take the inverse so that you turn more at higher speeds
val normSpeed = 1.0f - 0.5f*normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
}
override fun clampPassengerRotation(
settings: HorseSettings,
@@ -134,9 +149,9 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
var turnAmount = calcRotAmount(settings, state, vehicle, driver)
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount * 0.5f )
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount )
}
@@ -154,9 +169,9 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * 3
val walkHandlingBoost = 5
val walkHandling = handling * walkHandlingBoost
//In degrees per second
val walkHandling = 140.0 * 2
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * 2
val maxYawDiff = 90.0f
@@ -170,14 +185,13 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
//Take the inverse so that you turn less at higher speeds
val normSpeed = 1.0f
val normSpeed = 1.0f // = 1.0f - 0.5f*normalizeVal(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
// TurnRate should always be quick if not sprinting
val invRelSpeed = (inverseLerp(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 1.0f) * -1.0f
val turnRate = ((handling.toFloat() / 20.0f) * max(walkHandlingBoost * invRelSpeed, 1.0)).toFloat()
val turnRate = if(state.sprinting.get()) (handling.toFloat() / 20.0f) else (walkHandling.toFloat() / 20.0f)
//Ensure you only ever rotate as much difference as there is between the angles.
val turnSpeed = turnRate * rotDiffMod * normSpeed
val turnSpeed = turnRate * rotDiffMod * normSpeed
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
return rotAmount
@@ -204,16 +218,16 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
// Check to see if the ride should be walking or sprinting
val walkSpeed = getWalkSpeed(vehicle)
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 1.3
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val topSpeed = if(state.sprinting.get()) rideTopSpeed else walkSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 2
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.length())
//var newVelocity = vehicle.deltaMovement.normalize()
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
@@ -232,14 +246,11 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
activeInput = true
}
// Gravity logic
//Gravity logic
if (vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
} else {
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
//to do some testing and get other's opinions
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.8
val gravity = (9.8 / ( 20.0)) * 0.2
val terminalVel = 2.0
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
@@ -251,34 +262,27 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.03 * newVelocity.z.sign, newVelocity.z))
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
//Jump the thang!
if (driver.jumping && vehicle.onGround() && canJump) {
if (driver.jumping && vehicle.onGround()) {
val jumpForce = 1.0
val velMag = newVelocity.length()
val horz = state.rideVelocity.get().horizontalDistance()
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
val test = 0.0
//Ensure this doesn't add unwanted forward velocity
//val mag = if(newVelocity.length() < rideTopSpeed) newVelocity.length() else rideTopSpeed
//newVelocity = newVelocity.normalize().scale(mag)
}
//Zero out lateral velocity possibly picked up from a controller transition
newVelocity = Vec3(0.0, newVelocity.y, newVelocity.z)
return newVelocity
}
/*
* lerps the current val between minVal and maxVal.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minVal and 1.0 represents maxVal.
* Normalizes the current speed between minSpeed and maxSpeed.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
*/
private fun inverseLerp(currVal: Double, minVal: Double, maxVal: Double): Double {
require(maxVal > minVal) { "minVal must be greater than maxVal" }
return ((currVal - minVal) / (maxVal - minVal)).coerceIn(0.0, 1.0)
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
}
private fun getWalkSpeed( vehicle: PokemonEntity ): Double {
@@ -428,7 +432,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
return false
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: HorseSettings,
state: HorseState,
vehicle: PokemonEntity
@@ -492,6 +496,7 @@ class HorseSettings : RidingBehaviourSettings {
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
}
}
class HorseState : RidingBehaviourState() {
@@ -1,563 +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.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
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
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
import net.minecraft.resources.ResourceLocation
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 org.joml.Matrix3f
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
companion object {
val KEY = cobblemonResource("land/minekart")
}
override val key = KEY
override fun getRidingStyle(settings: MinekartSettings, state: MinekartState): RidingStyle {
return RidingStyle.LAND
}
val poseProvider = PoseProvider<MinekartSettings, MinekartState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
})
// val poseProvider = PoseProvider<MinekartSettings, MinekartState>(PoseType.STAND)
// .with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Boolean {
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: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Float {
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit)
//TODO: should this be by how much turn has accumulated or simply time turning... or both?
val boostPerSecond = 20.0f
val boostLoss = 40.0f
if (driver.jumping && driver.zza > 0.0f) {
if (!state.drifting.get()) {
if (driver.xxa != 0.0f) {
state.clockwiseDrift.set(driver.xxa.sign > 0.0)
state.turnMomentum.set(0.0f)
state.drifting.set(true)
state.boost.set(0.0f)
} else {
//TODO: Add jumping logic
}
} else {
state.boost.set((state.boost.get() + boostPerSecond / 20.0f).coerceIn(0.0f, boostMax))
}
} else {
if(driver.zza <= 0.0 && state.boost.get() != 0.0f){
state.boost.set(0.0f)
} else if( state.boost.get() > 0.0f ) {
state.boost.set(max(state.boost.get() - boostLoss, 0.0f))
}
state.drifting.set(false)
}
inAirCheck(state, vehicle)
return driver.deltaMovement.length().toFloat()
}
fun inAirCheck(
state: MinekartState,
vehicle: PokemonEntity,
) {
// Check both vertical movement and if there are blocks below.
val posBelow = vehicle.blockPosition().below()
val blockStateBelow = vehicle.level().getBlockState(posBelow)
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
}
override fun rotation(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
var newMomentum = state.turnMomentum.get().toDouble()
//TODO: maybe tie this to max turnspeeds rather than the turn acceleration
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val driftInput = turnInput * 0.25f
val lowSpeedTurnBoost = (inverseLerp(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 2.0f) * -1.0f
val forcedDriftTurnMomentum = (80.0f / 20.0f) * lowSpeedTurnBoost
val maxTurnMomentum = (60.0f / 20.0f) * lowSpeedTurnBoost
val maxDriftMomentum = forcedDriftTurnMomentum * 0.6f
if (!state.drifting.get()){
// perform non drift turning
if(abs(newMomentum) <= maxTurnMomentum && turnInput != 0.0 ) {
newMomentum += turnInput
newMomentum = min(max(newMomentum, maxTurnMomentum * -1.0), maxTurnMomentum.toDouble())
} else {
newMomentum = lerp(newMomentum, 0.0, 0.15)
}
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
} else {
// perform drift turning
if(abs(newMomentum) <= maxDriftMomentum && driftInput != 0.0 ) {
newMomentum += driftInput
newMomentum = min(max(newMomentum, maxDriftMomentum * -1.0), maxDriftMomentum.toDouble())
} else {
newMomentum = lerp(newMomentum, 0.0, 0.15)
}
val signedDriftTurn = if(state.clockwiseDrift.get()) forcedDriftTurnMomentum * -1.0f else forcedDriftTurnMomentum
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat() + signedDriftTurn.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat() + signedDriftTurn.toFloat())
}
}
override fun velocity(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
return state.rideVelocity.get()
}
private fun calculateRideSpaceVel(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
// Check to see if the ride should be walking or sprinting
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.7 else rideTopSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
val driftAngle = 20.0f
//Align the direction of this vector with the vehicles current heading.
var newVelocity = vehicle.deltaMovement
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
// rotate the angle of force if drifting
if (state.drifting.get()) {
val signedDriftAngle = if(state.clockwiseDrift.get()) driftAngle else driftAngle * -1.0f
newVelocity = newVelocity.yRot(signedDriftAngle.toRadians())
}
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.inAir.get()) {
//make sure it can't exceed top speed
var forwardInput = when {
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z <= 0.0 -> 0.0
else -> driver.zza.sign
}
if (!state.drifting.get() && state.boost.get() != 0.0f) {
forwardInput = 2.0;
}
// If drifting we need more acceleration due to the constant turn
val driftAccelBoost = if (state.drifting.get()) 1.5 else 1.0
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble() * driftAccelBoost)))
}
// Gravity logic
if (vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
} else {
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
//to do some testing and get other's opinions
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.25 * 3.0
val terminalVel = 2.0
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
}
//ground Friction
var friction = 0.003 * 2.0
if (state.drifting.get()) {friction *= 0.5}
if (!state.inAir.get()) {
newVelocity = newVelocity.subtract(
min(friction , abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
min(friction , abs(newVelocity.z)) * newVelocity.z.sign
)
// TEST FOR LATERAL SLIP OR WHATEVER
//TODO: calc lateral slip and friction better!
if (!state.drifting.get()) {
newVelocity = newVelocity.subtract(
min(friction * 10, abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
0.0,
)
}
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
// val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
// //Jump the thang!
// if (driver.jumping && vehicle.onGround() && canJump) {
// val jumpForce = 0.5
//
// newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
//
// }
// unrotate the angle of force if drifting
if (state.drifting.get()) {
val inverseSignedDriftAngle = if(state.clockwiseDrift.get()) driftAngle * -1.0f else driftAngle
newVelocity = newVelocity.yRot(inverseSignedDriftAngle.toRadians())
}
return newVelocity
}
/*
* lerps the current val between minVal and maxVal.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minVal and 1.0 represents maxVal.
*/
private fun inverseLerp(currVal: Double, minVal: Double, maxVal: Double): Double {
require(maxVal > minVal) { "minVal must be greater than maxVal" }
return ((currVal - minVal) / (maxVal - minVal)).coerceIn(0.0, 1.0)
}
override fun angRollVel(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
//Might need to add the smoothing here for default.
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
}
override fun canJump(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump)
}
override fun setRideBar(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Float {
return 0.0f
}
override fun jumpForce(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Float {
var fov = 1.0f
if(!state.drifting.get() && state.boost.get() != 0.0f) {
//TODO: is this too abrupt? Possibly caluclate differently?
fov = 1.2f
}
return fov
}
override fun useAngVelSmoothing(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
when {
state.drifting.get() -> return cobblemonResource("drifting")
}
return cobblemonResource("no_pose")
}
override fun inertia(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Double {
return 1.0
}
override fun shouldRoll(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Boolean {
return false
}
override fun turnOffOnGround(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun dismountOnShift(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotateRiderHead(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return true
}
override fun getRideSounds(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: MinekartSettings) = MinekartState()
}
class MinekartSettings : RidingBehaviourSettings {
override val key = MinekartBehaviour.KEY
var canJump = "true".asExpression()
private set
var boostLimit = "100.0f".asExpression()
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".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', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(boostLimit)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
boostLimit = buffer.readExpression()
}
}
class MinekartState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.BOTH)
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
var drifting = ridingState(false, Side.CLIENT)
var clockwiseDrift = ridingState(false, Side.CLIENT)
val boost: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
override fun reset() {
super.reset()
currSpeed.set(0.0, forced = true)
deltaRotation.set(Vec2.ZERO, forced = true)
drifting.set(false, forced = true)
clockwiseDrift.set(false, forced = true)
boost.set(0.0f, forced = true)
turnMomentum.set(0.0f, forced = true)
inAir.set(false, forced = true)
}
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(drifting.get())
buffer.writeBoolean(clockwiseDrift.get())
buffer.writeFloat(boost.get())
buffer.writeFloat(turnMomentum.get())
buffer.writeBoolean(inAir.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
drifting.set(buffer.readBoolean(), forced = true)
clockwiseDrift.set(buffer.readBoolean(), forced = true)
boost.set(buffer.readFloat(), forced = true)
turnMomentum.set(buffer.readFloat(), forced = true)
inAir.set(buffer.readBoolean(), forced = true)
}
override fun toString(): String {
return "MinekartState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
}
override fun copy() = MinekartState().also {
it.rideVelocity.set(rideVelocity.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.currSpeed.set(currSpeed.get(), forced = true)
it.deltaRotation.set(deltaRotation.get(), forced = true)
it.drifting.set(drifting.get(), forced = true)
it.clockwiseDrift.set(clockwiseDrift.get(), forced = true)
it.boost.set(boost.get(), forced = true)
it.turnMomentum.set(turnMomentum.get(), forced = true)
it.inAir.set(inAir.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is MinekartState) return false
if (previous.drifting.get() != drifting.get()) return true
if (previous.clockwiseDrift.get() != clockwiseDrift.get()) return true
if (previous.boost.get() != boost.get()) return true
if (previous.turnMomentum.get() != turnMomentum.get()) return true
if (previous.inAir.get() != inAir.get()) return true
return super.shouldSync(previous)
}
}
@@ -8,8 +8,7 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
@@ -17,12 +16,10 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
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
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
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -31,15 +28,29 @@ 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.abs
import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sign
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
companion object {
val KEY = cobblemonResource("land/vehicle")
val MAX_TOP_SPEED = 1.0 // 20 bl/s
val MIN_TOP_SPEED = 0.35 // 7 bl/s
//val MIN_SPEED = 0.25 // 5 bl/s
//Accel will lie between 1.0 second and 5.0 seconds
val MAX_ACCEL = (MAX_TOP_SPEED) / (20*3) //3.0 second to max speed
val MIN_ACCEL = (MAX_TOP_SPEED) / (20*8) // 8 seconds to max speed
//Can rotate 90 degrees
val MAX_HANDLING = 90.0
val MIN_HANDLING = 30.0
val MAX_YAW_HANDLING = 16.0
val MIN_YAW_HANDLING = 8.0
}
override val key = KEY
@@ -49,12 +60,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
}
val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
})
// val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
// .with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
.with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
@@ -81,38 +87,18 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.AIR, MIN_TOP_SPEED, MAX_TOP_SPEED)
val accel = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.AIR, MIN_ACCEL, MAX_ACCEL)
if (driver.isShiftKeyDown) {
state.drifting.set(true)
if(driver.zza != 0.0f) {
state.poweredDrifting.set(true)
} else {
state.poweredDrifting.set(false)
}
} else {
state.drifting.set(false)
state.poweredDrifting.set(false)
//speed up and slow down based on input
if (driver.zza > 0.0 && state.currSpeed.get() < topSpeed) {
state.currSpeed.set(min(state.currSpeed.get() + accel , topSpeed))
} else if (driver.zza < 0.0 && state.currSpeed.get() > 0.0) {
//Decelerate is now always a constant half of max acceleration.
state.currSpeed.set(max(state.currSpeed.get() - (MAX_ACCEL / 2), 0.0))
}
inAirCheck(state, vehicle)
return driver.deltaMovement.length().toFloat()
}
fun inAirCheck(
state: VehicleState,
vehicle: PokemonEntity,
) {
// Check both vertical movement and if there are blocks below.
val posBelow = vehicle.blockPosition().below()
val blockStateBelow = vehicle.level().getBlockState(posBelow)
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
return state.currSpeed.get().toFloat()
}
override fun rotation(
@@ -121,38 +107,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
var newMomentum = state.turnMomentum.get().toDouble()
// Grab turningAcceleration and divide 20 twice to get
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val maxTurnMomentum = 80.0f / 20.0f
// Base boost stats off of normal turning stats
val driftMaxTurnMomentum = maxTurnMomentum * 3.0f
var driftTurnInput = turnInput * 0.4f
if(state.drifting.get() || state.inAir.get()) {
if( newMomentum < driftMaxTurnMomentum) {
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr))
newMomentum += driftTurnInput
}
newMomentum = lerp(newMomentum, 0.0, 0.03)
if( abs(newMomentum) < 0.05 ) {newMomentum = 0.0}
} else {
if(abs(newMomentum) <= maxTurnMomentum && turnInput != 0.0 ) {
newMomentum += turnInput
newMomentum.coerceIn(-maxTurnMomentum.toDouble(), maxTurnMomentum.toDouble())
} else {
newMomentum = lerp(newMomentum, 0.0, 0.15)
}
}
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
val rotationDegrees = driver.xxa * vehicle.runtime.resolveFloat(settings.rotationSpeed)
val rotation = Vec2(driver.xRot, vehicle.yRot - rotationDegrees)
state.deltaRotation.set(Vec2(rotation.x - driver.rotationVector.x, rotation.y - vehicle.rotationVector.y))
return rotation
}
override fun velocity(
@@ -162,106 +120,18 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
driver: Player,
input: Vec3
): Vec3 {
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
return state.rideVelocity.get()
}
private fun calculateRideSpaceVel(
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
// Check to see if the ride should be walking or sprinting
//val walkSpeed = getWalkSpeed(vehicle)
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.5 else rideTopSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = Vec3.ZERO
if (!state.drifting.get()) {
newVelocity = vehicle.deltaMovement
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
} else {
state
newVelocity = vehicle.deltaMovement
// Align the direction of movement with the world coordinate space so as not to modify it through turning
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
val runtime = vehicle.runtime
val driveFactor = runtime.resolveFloat(settings.driveFactor)
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(settings.reverseDriveFactor)
}
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.inAir.get()) {
//make sure it can't exceed top speed
val forwardInput = when {
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z <= 0.0 -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
activeInput = true
if (driver.xxa != 0F && g.absoluteValue < runtime.resolveFloat(settings.minimumSpeedToTurn)) {
driver.xxa = 0F
}
val velocity = Vec3(0.0 /* Maybe do drifting here later */, 0.0, g.toDouble() * state.currSpeed.get())
// Gravity logic
if (vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
} else {
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
//to do some testing and get other's opinions
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.25 * 3.0
val terminalVel = 2.0
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
}
//ground Friction
var friction = 0.003 * 2.0
if (state.drifting.get()) {friction *= 0.1}
if (!state.inAir.get()) {
newVelocity = newVelocity.subtract(
min(friction , abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
min(friction , abs(newVelocity.z)) * newVelocity.z.sign
)
// TEST FOR LATERAL SLIP OR WHATEVER
//TODO: calc lateral slip and friction better!
if (!state.drifting.get()) {
newVelocity = newVelocity.subtract(
min(friction * 10, abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
0.0,
)
}
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
//Jump the thang!
if (driver.jumping && vehicle.onGround() && canJump) {
val jumpForce = 0.5
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
}
return newVelocity
return velocity
}
override fun angRollVel(
@@ -319,7 +189,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
val runtime = vehicle.runtime
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = settings.jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
}
override fun gravity(
@@ -328,7 +201,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
return regularGravity
}
override fun rideFovMultiplier(
@@ -354,15 +227,11 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
when {
state.poweredDrifting.get() -> return cobblemonResource("powered_drifting")
state.drifting.get() -> return cobblemonResource("drifting")
}
return cobblemonResource("no_pose")
}
override fun inertia(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Double {
return 1.0
return 0.5
}
override fun shouldRoll(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
@@ -393,12 +262,12 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
return false
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity
): Boolean {
return true
return false
}
override fun getRideSounds(
@@ -418,6 +287,9 @@ class VehicleSettings : RidingBehaviourSettings {
var canJump = "true".asExpression()
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var speed = "0.3".asExpression()
private set
@@ -427,40 +299,28 @@ class VehicleSettings : RidingBehaviourSettings {
var reverseDriveFactor = "0.25".asExpression()
private set
var minimumSpeedToTurn = "0.1".asExpression()
private set
var rotationSpeed = "45/20".asExpression()
private set
var lookYawLimit = "101".asExpression()
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".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', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.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()
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(jumpVector[0])
buffer.writeExpression(jumpVector[1])
buffer.writeExpression(jumpVector[2])
buffer.writeExpression(speed)
buffer.writeExpression(driveFactor)
buffer.writeExpression(reverseDriveFactor)
buffer.writeExpression(minimumSpeedToTurn)
buffer.writeExpression(rotationSpeed)
buffer.writeExpression(lookYawLimit)
}
@@ -468,9 +328,15 @@ class VehicleSettings : RidingBehaviourSettings {
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
jumpVector = listOf(
buffer.readExpression(),
buffer.readExpression(),
buffer.readExpression()
)
speed = buffer.readExpression()
driveFactor = buffer.readExpression()
reverseDriveFactor = buffer.readExpression()
minimumSpeedToTurn = buffer.readExpression()
rotationSpeed = buffer.readExpression()
lookYawLimit = buffer.readExpression()
}
@@ -479,36 +345,11 @@ class VehicleSettings : RidingBehaviourSettings {
class VehicleState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.BOTH)
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
var drifting = ridingState(false, Side.CLIENT)
var poweredDrifting = ridingState(false, Side.CLIENT)
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
override fun reset() {
super.reset()
currSpeed.set(0.0, forced = true)
deltaRotation.set(Vec2.ZERO, forced = true)
drifting.set(false, forced = true)
poweredDrifting.set(false, forced = true)
turnMomentum.set(0.0f, forced = true)
inAir.set(false, forced = true)
}
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(drifting.get())
buffer.writeBoolean(poweredDrifting.get())
buffer.writeFloat(turnMomentum.get())
buffer.writeBoolean(inAir.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
drifting.set(buffer.readBoolean(), forced = true)
poweredDrifting.set(buffer.readBoolean(), forced = true)
turnMomentum.set(buffer.readFloat(), forced = true)
inAir.set(buffer.readBoolean(), forced = true)
}
override fun toString(): String {
@@ -520,18 +361,5 @@ class VehicleState : RidingBehaviourState() {
it.stamina.set(stamina.get(), forced = true)
it.currSpeed.set(currSpeed.get(), forced = true)
it.deltaRotation.set(deltaRotation.get(), forced = true)
it.drifting.set(drifting.get(), forced = true)
it.poweredDrifting.set(poweredDrifting.get(), forced = true)
it.turnMomentum.set(turnMomentum.get(), forced = true)
it.inAir.set(inAir.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is VehicleState) return false
if (previous.drifting.get() != drifting.get()) return true
if (previous.poweredDrifting.get() != poweredDrifting.get()) return true
if (previous.turnMomentum.get() != turnMomentum.get()) return true
if (previous.inAir.get() != inAir.get()) return true
return super.shouldSync(previous)
}
}
@@ -330,7 +330,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead)
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: BoatSettings,
state: BoatState,
vehicle: PokemonEntity
@@ -215,7 +215,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
return false
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: BurstSettings,
state: BurstState,
vehicle: PokemonEntity
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
@@ -20,7 +19,6 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
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.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -31,9 +29,7 @@ import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin
import kotlin.math.sqrt
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
companion object {
@@ -47,7 +43,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
val poseProvider = PoseProvider<DolphinSettings, DolphinState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.deltaMovement.length() > 0.1 })
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
@@ -165,59 +161,11 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//TODO:hook up handling
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//Interpret mouse input
val smoothingSpeed = 4.0
//Might need to add the smoothing here for default.
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
val rollRot = xInput
val pitchRot = yInput
// Roll
// if (abs(controller.pitch + rollRot) >= 89.5 ) {
// mouseXSmoother.reset()
// } else {
//
// }
controller.rotateRoll(rollRot.toFloat())
// Yaw
// TODO:move all magic numbers to handling expression
val yawSpeed = handling * deltaTime * 0.25
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 1.0 - min(sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
// Pitch
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
mouseYSmoother.reset()
} else {
//controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
controller.rotatePitch(pitchRot.toFloat())
}
//yaw, pitch, roll
return Vec3.ZERO
}
/*
* Normalizes the given value between a min and a max.
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
* and 1.0 represents x is at or above it.
*/
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
require(max > min) { "max must be greater than min" }
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
}
override fun canJump(
@@ -306,7 +254,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
return false
}
override fun shouldRotateRiderHead(
override fun shouldRotatePlayerHead(
settings: DolphinSettings,
state: DolphinState,
vehicle: PokemonEntity
@@ -347,12 +295,6 @@ class DolphinSettings : RidingBehaviourSettings {
var strafeFactor = "0.2".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
@@ -50,7 +50,7 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
this.looping = true
this.delay = 0
this.volume = 0.1f
this.attenuation = SoundInstance.Attenuation.LINEAR // This class performs its own attenuation
this.attenuation = SoundInstance.Attenuation.NONE // This class performs its own attenuation
if (this.isPassenger) this.relative = true
}
@@ -16,7 +16,6 @@ import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.sign
/**
* Class to handle the updating and containment of ride data used in animation.
@@ -34,7 +33,6 @@ class RidingAnimationData(val ride: PokemonEntity)
var rotSpring: Vec3Spring = Vec3Spring()
var rotDeltaSpring: Vec3Spring = Vec3Spring()
var localVelocitySpring: Vec3Spring = Vec3Spring()
var driverInputSpring: Vec3Spring = Vec3Spring()
fun update() {
val activeRide = ride.hasControllingPassenger()
@@ -138,14 +136,6 @@ class RidingAnimationData(val ride: PokemonEntity)
/******************************************************
* Update springs
*****************************************************/
val driver = ride.controllingPassenger
val vertInput = when {
driver != null && driver.jumping-> 1.0
driver != null && driver.isShiftKeyDown -> -1.0
else -> 0.0
}
val input = if(driver != null) Vec3(driver.xxa.toDouble() * -1, vertInput, driver.zza.toDouble()) else Vec3.ZERO
driverInputSpring.update(input, stiffness * 2, damping)
rotDeltaSpring.update(angDelta, stiffness, damping)
velocitySpring.update(currentVelocity, stiffness, damping)
rotSpring.update(currRot, stiffness, damping)
@@ -19,15 +19,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.render.models.blockbench.ExcludedLabels
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BimanualSwingAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PitchTiltAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PunchAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.QuadrupedWalkAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.SingleBoneLookAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.*
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
@@ -125,22 +117,6 @@ object ClientMoLangFunctions {
ObjectValue(anim)
}
},
"pitch_tilt" to Function {
model -> Function { params ->
val bone = params.getStringOrNull(0) ?: "root"
val maxChangePerTick = params.getDoubleOrNull(1) ?: 1.5F
val minPitch = params.getDoubleOrNull(2) ?: -45F
val maxPitch = params.getDoubleOrNull(3) ?: 45F
ObjectValue(
PitchTiltAnimation(
bone = model.getPart(bone),
minPitch = minPitch.toFloat(),
maxPitch = maxPitch.toFloat(),
maxChangePerTick = maxChangePerTick.toFloat()
)
)
}
},
"look" to Function {
model -> Function { params ->
val boneName = params.getString(0)
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
@@ -134,34 +133,25 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
// Configure what controls to show for each behaviour
when (currentBehaviourKey) {
BirdBehaviour.KEY -> {}
BoatBehaviour.KEY -> {}
BurstBehaviour.KEY -> {}
DolphinBehaviour.KEY -> {}
GliderBehaviour.KEY -> {}
HelicopterBehaviour.KEY -> {}
HorseBehaviour.KEY -> {
showVerticalMouse = false
disableHorizontalMovementKeys = true
showSneakKey = false
}
HoverBehaviour.KEY -> {
showVerticalMouse = false
}
BoatBehaviour.KEY -> {}
GliderBehaviour.KEY -> {}
HelicopterBehaviour.KEY -> {}
JetBehaviour.KEY -> {
showMovementKeys = false
showSneakKey = false
showJumpKey = false
}
MinekartBehaviour.KEY -> {
showVerticalMouse = false
showHorizontalMouse = false
showSneakKey = false
}
RocketBehaviour.KEY -> {
showVerticalMouse = false
showHorizontalMouse = false
}
BurstBehaviour.KEY -> {}
VehicleBehaviour.KEY -> {}
HoverBehaviour.KEY -> {
showVerticalMouse = false
}
}
val centerX = minecraft.window.guiScaledWidth / 2
@@ -719,7 +719,6 @@ class PCGUI(
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
}
if (storageWidget.isHovered && mouseX < (storageWidget.x + StorageWidget.SCREEN_WIDTH)) this.storageWidget.box -= verticalAmount.toInt()
if (wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
return super.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
}
@@ -1,29 +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.riding
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
import net.minecraft.client.Minecraft
import net.minecraft.client.player.RemotePlayer
object ClientboundUpdateDriverInputHandler : ClientNetworkPacketHandler<ClientboundUpdateDriverInputPacket> {
override fun handle(packet: ClientboundUpdateDriverInputPacket, client: Minecraft) {
client.executeIfPossible {
val level = client.level ?: return@executeIfPossible
val entity = level.getEntity(packet.entityId)
if (entity is RemotePlayer) {
entity.xxa = packet.driverInput.x
entity.zza = packet.driverInput.z
entity.jumping = packet.driverInput.y == 1.0f
entity.isShiftKeyDown = packet.driverInput.y == -1.0f
}
}
}
}
@@ -1,70 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.entity
import com.cobblemon.mod.common.api.riding.Rideable
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.util.Mth
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.phys.Vec3
import org.joml.AxisAngle4f
import org.joml.Vector3f
object MountedLivingEntityRenderer {
fun render(
entity: LivingEntity,
pokemon: PokemonEntity,
stack: PoseStack,
bob: Float,
yBodyRot: Float,
partialTicks: Float,
i: Float
) {
if (entity.vehicle !is Rideable) return
val matrix = stack.last().pose()
val seatIndex = pokemon.passengers.indexOf(entity)
val seat = pokemon.seats[seatIndex]
val delegate = pokemon.delegate as PokemonClientDelegate
val locator = delegate.locatorStates[seat.locator]
//Positions player
if (locator != null) {
MountedPokemonAnimationRenderController.setup(pokemon, partialTicks)
//Undo seat position
val playerPos = Vec3(
Mth.lerp(partialTicks.toDouble(), entity.xOld, entity.x),
Mth.lerp(partialTicks.toDouble(), entity.yOld, entity.y),
Mth.lerp(partialTicks.toDouble(), entity.zOld, entity.z),
)
val entityPos = Vec3(
Mth.lerp(partialTicks.toDouble(), pokemon.xOld, pokemon.x),
Mth.lerp(partialTicks.toDouble(), pokemon.yOld, pokemon.y),
Mth.lerp(partialTicks.toDouble(), pokemon.zOld, pokemon.z),
)
matrix.translate(playerPos.subtract(entityPos).toVector3f().negate())
matrix.translate(locator.matrix.getTranslation(Vector3f()))
val offset = Vector3f(0f, entity.bbHeight / 2, 0f).mul(-1f)
matrix.rotate(locator.matrix.getRotation(AxisAngle4f()))
matrix.rotate(Axis.YP.rotationDegrees(180 + yBodyRot))
matrix.translate(offset)
matrix.translate(Vector3f(0f, 0.35f, 0f))
}
}
}
@@ -607,7 +607,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
setDefault()
// Applies any of the state's queued actions.
state.preRender()
state.renderMarkers.clear()
// Performs a check that the current pose is correct and returns back which pose we should be applying. Even if
// a change of pose is necessary, if it's going to gradually transition there then we're still going to keep
// applying our current pose until that process is done.
@@ -759,6 +758,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
yRot = Mth.lerp(state.getPartialTicks(), entity.yBodyRotO, entity.yBodyRot)
yRot = 180f - Mth.wrapDegrees(yRot)
}
//For some reason locators are positioned upside-down so this fixes that
matrixStack.mulPose(Axis.ZP.rotationDegrees(180f))
} else if (entity is EmptyPokeBallEntity) {
scale = 0.7F
yRot = Mth.lerp(state.getPartialTicks(), entity.yRot, entity.yRotO)
@@ -774,9 +775,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
matrixStack.scale(scale, scale, scale)
matrixStack.mulPose(Axis.YP.rotationDegrees(yRot))
// Entities' locators are positioned upside-down so this fixes that
if(entity != null) matrixStack.mulPose(Axis.ZP.rotationDegrees(180f))
locatorAccess.update(matrixStack, entity, scale, state.locatorStates, isRoot = true)
}
@@ -131,14 +131,6 @@ abstract class PosableState : Schedulable {
/** A list of items to be rendered on a PosableModel during an animation */
val animationItems = mutableMapOf<String, ItemStack>()
/** Just some numbers that might be getting arbitrarily set and referenced. */
val numbers = mutableMapOf<String, Float>()
/**
* Markers that have been added to the state this render, cleared when animation is about to start.
* This is used to hand basic state from one animation to another.
*/
val renderMarkers = mutableSetOf<String>()
/** All of the MoLang functions that can be expose current riding data. */
val ridingFunctions = QueryStruct(hashMapOf())
.addFunction("pitch") { params ->
@@ -263,36 +255,6 @@ abstract class PosableState : Schedulable {
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0))
}
.addFunction("input_right") { params ->
val lookBackTick = params.getInt(0)
val pokemon = getEntity() as? PokemonEntity ?: return@addFunction DoubleValue(0.0)
val partialTickZVel = pokemon.ridingAnimationData.driverInputSpring.getInterpolated(currentPartialTicks.toDouble(), lookBackTick).x
val topSpeed = 1.0//pokemon.riding.getController(pokemon)?.getStat(pokemon, RidingStat.SPEED)
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0) * -1.0)
}
.addFunction("input_up") { params ->
val lookBackTick = params.getInt(0)
val pokemon = getEntity() as? PokemonEntity ?: return@addFunction DoubleValue(0.0)
val partialTickZVel = pokemon.ridingAnimationData.driverInputSpring.getInterpolated(currentPartialTicks.toDouble(), lookBackTick).y
val topSpeed = 1.0//pokemon.riding.getController(pokemon)?.getStat(pokemon, RidingStat.SPEED)
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0))
}
.addFunction("input_forward") { params ->
val lookBackTick = params.getInt(0)
val pokemon = getEntity() as? PokemonEntity ?: return@addFunction DoubleValue(0.0)
val partialTickZVel = pokemon.ridingAnimationData.driverInputSpring.getInterpolated(currentPartialTicks.toDouble(), lookBackTick).z
val topSpeed = 1.0//pokemon.riding.getController(pokemon)?.getStat(pokemon, RidingStat.SPEED)
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0))
}
/** All of the MoLang functions that can be applied to something with this state. */
@@ -326,31 +288,6 @@ abstract class PosableState : Schedulable {
}
}
}
.addFunction("recurring_sound") { params ->
if (params.get<MoValue>(0) !is StringValue) {
return@addFunction Unit
}
//param 0 - sound
//param 1 - offset
//param 2 -
val curr_time = reusableAnimTime.value
val entity = getEntity()
val soundEvent = SoundEvent.createVariableRangeEvent(params.getString(0).asIdentifierDefaultingNamespace())
val offset = params.getDouble(1)
val loopLength = params.getDouble(2)
if (soundEvent != null) {
val volume = 1F
val pitch = 1F
if( )
if (entity != null) {
if (!entity.isSilent) {
entity.level().playLocalSound(entity, soundEvent, entity.soundSource, volume, pitch)
}
} else {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, volume, pitch))
}
}
}
.addFunction("render_item") { params ->
if (params.get<MoValue>(0) !is StringValue) return@addFunction Unit
@@ -10,12 +10,15 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.isBattling
import com.cobblemon.mod.common.util.isDusk
import com.cobblemon.mod.common.util.isStandingOn
import com.cobblemon.mod.common.util.isStandingOnRedSand
import com.cobblemon.mod.common.util.isStandingOnSand
import com.cobblemon.mod.common.util.isStandingOnSandOrRedSand
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
@@ -54,9 +57,9 @@ class PoseAdapter(
addCondition("isUnderWater") { entity, expectedValue -> entity.isUnderWater == expectedValue }
// Kept for compatibility
addCondition("isStandingOnRedSand") { entity, expectedValue -> entity.isStandingOn(setOf("minecraft:red_sand")) == expectedValue }
addCondition("isStandingOnSand") { entity, expectedValue -> entity.isStandingOn(setOf("minecraft:sand")) == expectedValue }
addCondition("isStandingOnSandOrRedSand") { entity, expectedValue -> entity.isStandingOn(setOf("minecraft:sand", "minecraft:red_sand")) == expectedValue }
addCondition("isStandingOnRedSand") { entity, expectedValue -> entity.isStandingOnRedSand() == expectedValue }
addCondition("isStandingOnSand") { entity, expectedValue -> entity.isStandingOnSand() == expectedValue }
addCondition("isStandingOnSandOrRedSand") { entity, expectedValue -> entity.isStandingOnSandOrRedSand() == expectedValue }
addCondition("isDusk") { entity, expectedValue -> entity.isDusk() == expectedValue }
@@ -1,83 +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.render.models.blockbench.animation
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.addRotation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.util.Mth
/**
* Animation used to tilt the targeted bone of the entity by a pitch that is proportionate
* to the direction the entity is moving. This is used to make birds and fish tilt upwards
* or downwards based on their motion.
*
* @author Hiroku
* @since August 9th, 2025
*/
class PitchTiltAnimation(
val bone: Bone,
val maxChangePerTick: Float = 1F,
val minPitch: Float = -45F,
val maxPitch: Float = 45F,
) : PoseAnimation() {
companion object {
const val PREVIOUS_ANGLE = "previous_pitch_tilt_angle"
const val PITCHED_TILT = "has_pitched_tilt"
}
override fun setupAnim(
context: RenderContext,
model: PosableModel,
state: PosableState,
limbSwing: Float,
limbSwingAmount: Float,
ageInTicks: Float,
headYaw: Float,
headPitch: Float,
intensity: Float
) {
val entity = context.entity ?: return
if (entity.passengers.isNotEmpty()) {
return
}
val delta = entity.deltaMovement
// The angleOfMotion should be 0 in the case that horizontal movement is 1 and vertical movement is 0.
// It should be the angle in degrees that becomes higher as the entity's motion becomes more upwards, and negative as the entity's motion becomes more downwards
val vertical = delta.y
val horizontal = delta.horizontalDistance()
val angleOfMotion = if (horizontal == 0.0) {
when {
vertical > 0 -> maxPitch
vertical < 0 -> minPitch
else -> 0F
}
} else {
Mth.atan2(vertical, horizontal).toDegrees()
}.coerceIn(minPitch, maxPitch)
val maxChangePerTick = this.maxChangePerTick * intensity
val lastAngle = state.numbers[PREVIOUS_ANGLE] ?: 0F
val interpolatedAngle = if (angleOfMotion - lastAngle > maxChangePerTick) {
lastAngle + maxChangePerTick
} else if (angleOfMotion - lastAngle < -maxChangePerTick) {
lastAngle - maxChangePerTick
} else {
angleOfMotion
}
bone.addRotation(0, -interpolatedAngle.toRadians())
state.numbers[PREVIOUS_ANGLE] = interpolatedAngle
state.renderMarkers += PITCHED_TILT
}
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.client.render.models.blockbench.animation
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.addRotation
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PitchTiltAnimation.Companion.PITCHED_TILT
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PitchTiltAnimation.Companion.PREVIOUS_ANGLE
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation.Companion.X_AXIS
@@ -65,10 +63,7 @@ class SingleBoneLookAnimation(
val head = bone ?: return
val pitch = pitchMultiplier * headPitch.coerceIn(minPitch, maxPitch)
val yaw = yawMultiplier * headYaw.coerceIn(minYaw, maxYaw)
// If PitchTiltAnimation was applied then we should counteract that tilt. It does kinda
// assume that the tilt was on the body bone, though.
val counterTiltDegrees = if (PITCHED_TILT in state.renderMarkers) { state.numbers[PREVIOUS_ANGLE] ?: 0 } else 0F
head.addRotation(X_AXIS, (pitch.toRadians() * intensity) + counterTiltDegrees.toRadians())
head.addRotation(X_AXIS, pitch.toRadians() * intensity)
head.addRotation(Y_AXIS, yaw.toRadians() * intensity)
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PitchTiltAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
@@ -87,7 +86,6 @@ class PidgeyModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
PitchTiltAnimation(bone = getPart("body"), maxChangePerTick = 1F),
singleBoneLook(),
bedrock("pidgey", "air_idle")
)
@@ -105,7 +103,6 @@ class PidgeyModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
PitchTiltAnimation(bone = getPart("body"), maxChangePerTick = 1F),
singleBoneLook(),
bedrock("pidgey", "air_fly")
)
@@ -15,7 +15,9 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.isBattling
import com.cobblemon.mod.common.util.isStandingOn
import com.cobblemon.mod.common.util.isStandingOnRedSand
import com.cobblemon.mod.common.util.isStandingOnSand
import com.cobblemon.mod.common.util.isStandingOnSandOrRedSand
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
@@ -61,7 +63,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
sleep = registerPose(
poseName = "sleep",
poseType = PoseType.SLEEP,
condition = { it.getEntity()?.isStandingOn(setOf("minecraft:sand")) != true },
condition = { it.getEntity()?.isStandingOnSand() != true },
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
redsand.createTransformation().withVisibility(visibility = false)
@@ -75,7 +77,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
sleepredsand = registerPose(
poseName = "sleepsand",
poseType = PoseType.SLEEP,
condition = { it.getEntity()?.isStandingOn(setOf("minecraft:red_sand")) == true },
condition = { it.getEntity()?.isStandingOnRedSand() == true },
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
redsand.createTransformation().withVisibility(visibility = false)
@@ -89,7 +91,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.UI_POSES + PoseType.STATIONARY_POSES,
condition = { !it.isBattling && it.getEntity()?.isStandingOn(setOf("minecraft:sand", "minecraft:red_sand")) != true },
condition = { !it.isBattling && it.getEntity()?.isStandingOnSandOrRedSand() != true },
quirks = arrayOf(blink, idlequirk),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
@@ -104,7 +106,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
condition = { it.getEntity()?.isStandingOn(setOf("minecraft:sand", "minecraft:red_sand")) != true },
condition = { it.getEntity()?.isStandingOnSandOrRedSand() != true },
quirks = arrayOf(blink),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
@@ -120,7 +122,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
poseName = "standingsand",
poseTypes = PoseType.STATIONARY_POSES,
quirks = arrayOf(blink, idlequirk),
condition = { !it.isBattling && it.getEntity()?.isStandingOn(setOf("minecraft:sand")) == true },
condition = { !it.isBattling && it.getEntity()?.isStandingOnSand() == true },
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
redsand.createTransformation().withVisibility(visibility = false)
@@ -135,7 +137,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
poseName = "standingredsand",
poseTypes = PoseType.STATIONARY_POSES,
quirks = arrayOf(blink, idlequirk),
condition = { !it.isBattling && it.getEntity()?.isStandingOn(setOf("minecraft:red_sand")) == true },
condition = { !it.isBattling && it.getEntity()?.isStandingOnRedSand() == true },
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
redsand.createTransformation().withVisibility(visibility = false)
@@ -149,7 +151,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
walksand = registerPose(
poseName = "walksand",
poseTypes = PoseType.MOVING_POSES,
condition = { it.getEntity()?.isStandingOn(setOf("minecraft:sand")) == true },
condition = { it.getEntity()?.isStandingOnSand() == true },
quirks = arrayOf(blink),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
@@ -163,7 +165,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
walkredsand = registerPose(
poseName = "walkredsand",
poseTypes = PoseType.MOVING_POSES,
condition = { it.getEntity()?.isStandingOn(setOf("minecraft:red_sand")) == true },
condition = { it.getEntity()?.isStandingOnRedSand() == true },
quirks = arrayOf(blink),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
@@ -177,7 +179,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
battleidle = registerPose(
poseName = "battleidle",
poseTypes = PoseType.STATIONARY_POSES,
condition = { it.isBattling && it.getEntity()?.isStandingOn(setOf("minecraft:sand", "minecraft:red_sand")) != true },
condition = { it.isBattling && it.getEntity()?.isStandingOnSandOrRedSand() != true },
quirks = arrayOf(blink),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
@@ -192,7 +194,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
battleidlesand = registerPose(
poseName = "battleidlesand",
poseTypes = PoseType.STATIONARY_POSES,
condition = { it.getEntity()?.isStandingOn(setOf("minecraft:sand")) == true && it.isBattling },
condition = { it.getEntity()?.isStandingOnSand() == true && it.isBattling },
quirks = arrayOf(blink),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = true),
@@ -207,7 +209,7 @@ class HippowdonModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
battleidleredsand = registerPose(
poseName = "battleidleredsand",
poseTypes = PoseType.STATIONARY_POSES,
condition = { it.getEntity()?.isStandingOn(setOf("minecraft:red_sand")) == true && it.isBattling },
condition = { it.getEntity()?.isStandingOnRedSand() == true && it.isBattling },
quirks = arrayOf(blink),
transformedParts = arrayOf(
sand.createTransformation().withVisibility(visibility = false),
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class GolurkModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("golurk")
override val head = getPart("head")
override var portraitScale = 3.0F
override var portraitTranslation = Vec3(-0.3, 5.4, 0.0)
override var profileScale = 0.4F
override var profileTranslation = Vec3(0.0, 1.15, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
override val cryAnimation = CryProvider { bedrockStateful("golurk", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("golurk", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 20,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("golurk", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 20,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("golurk", "ground_walk")
)
)
sleep = registerPose(
poseName = "sleep",
transformTicks = 20,
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("golurk", "sleep"))
)
}
override fun getFaintAnimation(state: PosableState): ActiveAnimation? {
return if (state.isNotPosedIn(sleep)) bedrockStateful("golurk", "faint") else null
}
}
@@ -405,6 +405,7 @@ object VaryingModelRepository {
inbuilt("lombre", :: LombreModel)
inbuilt("ludicolo", :: LudicoloModel)
inbuilt("golett", :: GolettModel)
inbuilt("golurk", :: GolurkModel)
inbuilt("stantler", :: StantlerModel)
inbuilt("sneasel", :: SneaselModel)
inbuilt("weavile", :: WeavileModel)
@@ -36,26 +36,16 @@ import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity.Companion.SPAWN_DIRECTION
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.isLookingAt
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.DoubleRange
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import kotlin.math.PI
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sqrt
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font.DisplayMode
import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.*
import net.minecraft.client.renderer.entity.EntityRendererProvider
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.client.renderer.entity.MobRenderer
@@ -67,11 +57,8 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import org.joml.Math
import org.joml.Matrix4f
import org.joml.Quaternionf
import org.joml.Vector3f
import org.joml.Vector4f
import org.joml.*
import kotlin.math.*
class PokemonRenderer(
context: EntityRendererProvider.Context
@@ -140,7 +127,7 @@ class PokemonRenderer(
clientDelegate
)
}
if (entity.platform != PlatformType.NONE) {
if(entity.platform != PlatformType.NONE) {
drawPlatform(
poseMatrix,
entity,
@@ -157,7 +144,8 @@ class PokemonRenderer(
if (entity.passengers.isNotEmpty()) {
renderRiding(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
} else {
}
else {
super.render(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
}
@@ -194,7 +182,7 @@ class PokemonRenderer(
val controller = rollable.orientationController
poseMatrix.pushPose()
if (!DISABLE_ROLLING_DEBUG && controller.active) {
if(!DISABLE_ROLLING_DEBUG && controller.active){
val matrix = poseMatrix.last().pose()
val yaw = Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot)
val center = Vector3f(0f, entity.bbHeight/2, 0f)
@@ -1,100 +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.command
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.commandLang
import com.cobblemon.mod.common.util.entity
import com.cobblemon.mod.common.util.permission
import com.cobblemon.mod.common.util.player
import com.cobblemon.mod.common.util.string
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.Commands.literal
import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
object RunMolangCommand {
private const val NAME = "runmolang"
private const val MOLANG = "molang"
private const val PLAYER = "player"
private const val NPC = "npc"
private const val POKEMON = "pokemon"
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
dispatcher.register(literal(NAME)
.permission(CobblemonPermissions.RUN_MOLANG)
.then(
argument(MOLANG, StringArgumentType.string())
.executes { execute(it, it.string(MOLANG), null, null, null) }
.then(argument(PLAYER, EntityArgument.player())
.executes { execute(it, it.string(MOLANG), it.player(PLAYER), null, null) }
.then(argument(NPC, EntityArgument.entity())
.executes { execute(it, it.string(MOLANG), it.player(PLAYER), it.entity(
NPC
), null) }
.then(argument(POKEMON, EntityArgument.entity())
.executes { execute(it, it.string(MOLANG), it.player(PLAYER), it.entity(
NPC
), it.entity(POKEMON)) }
)
)
.then(argument(POKEMON, EntityArgument.entity())
.executes { execute(it, it.string(MOLANG), it.player(PLAYER), null, it.entity(
POKEMON
)) }
)
)
.then(argument(NPC, EntityArgument.entity())
.executes { execute(it, it.string(MOLANG), null, it.entity(NPC), null) }
.then(argument(POKEMON, EntityArgument.entity())
.executes { execute(it, it.string(MOLANG), null, it.entity(NPC), it.entity(
POKEMON
)) }
)
)
.then(argument(POKEMON, StringArgumentType.string())
.executes { execute(it, it.string(MOLANG), null, null, it.entity(POKEMON)) }
)
)
)
}
private fun execute(context: CommandContext<CommandSourceStack>, molang: String, player: ServerPlayer?, npc: Entity?, pokemon: Entity? = null): Int {
try {
val runtime = MoLangRuntime().setup()
val entity = context.source.entity
(npc as? NPCEntity)?.let { runtime.withQueryValue("npc", npc.struct) }
(pokemon as? PokemonEntity)?.let { runtime.withQueryValue("pokemon", pokemon.struct) }
player?.let { runtime.withQueryValue("player", player.asMoLangValue()) }
entity?.let { runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue()) }
val out = molang.asExpressionLike().resolve(runtime)
context.source.sendSuccess({ commandLang("molang.out", out.asString()) }, true)
} catch (e: Exception) {
e.printStackTrace()
}
return Command.SINGLE_SUCCESS
}
}
@@ -10,20 +10,20 @@ package com.cobblemon.mod.common.command
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.entity
import com.cobblemon.mod.common.util.permission
import com.cobblemon.mod.common.util.player
import com.cobblemon.mod.common.util.resourceLocation
import com.cobblemon.mod.common.util.uuid
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.context.CommandContext
import java.util.UUID
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.Commands.literal
@@ -31,7 +31,6 @@ import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.commands.arguments.ResourceLocationArgument
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
object RunMolangScriptCommand {
private const val NAME = "runmolangscript"
@@ -48,38 +47,38 @@ object RunMolangScriptCommand {
.executes { execute(it, it.resourceLocation(SCRIPT), null, null, null) }
.then(argument(PLAYER, EntityArgument.player())
.executes { execute(it, it.resourceLocation(SCRIPT), it.player(PLAYER), null, null) }
.then(argument(NPC, EntityArgument.entity())
.executes { execute(it, it.resourceLocation(SCRIPT), it.player(PLAYER), it.entity(NPC), null) }
.then(argument(POKEMON, EntityArgument.entity())
.executes { execute(it, it.resourceLocation(SCRIPT), it.player(PLAYER), it.entity(NPC), it.entity(POKEMON)) }
.then(argument(NPC, StringArgumentType.string())
.executes { execute(it, it.resourceLocation(SCRIPT), it.player(PLAYER), it.uuid(NPC), null) }
.then(argument(POKEMON, StringArgumentType.string())
.executes { execute(it, it.resourceLocation(SCRIPT), it.player(PLAYER), it.uuid(NPC), it.uuid(POKEMON)) }
)
)
.then(argument(POKEMON, EntityArgument.entity())
.executes { execute(it, it.resourceLocation(SCRIPT), it.player(PLAYER), null, it.entity(POKEMON)) }
.then(argument(POKEMON, StringArgumentType.string())
.executes { execute(it, it.resourceLocation(SCRIPT), it.player(PLAYER), null, it.uuid(POKEMON)) }
)
)
.then(argument(NPC, EntityArgument.entity())
.executes { execute(it, it.resourceLocation(SCRIPT), null, it.entity(NPC), null) }
.then(argument(POKEMON, EntityArgument.entity())
.executes { execute(it, it.resourceLocation(SCRIPT), null, it.entity(NPC), it.entity(POKEMON)) }
.then(argument(NPC, StringArgumentType.string())
.executes { execute(it, it.resourceLocation(SCRIPT), null, it.uuid(NPC), null) }
.then(argument(POKEMON, StringArgumentType.string())
.executes { execute(it, it.resourceLocation(SCRIPT), null, it.uuid(NPC), it.uuid(POKEMON)) }
)
)
.then(argument(POKEMON, EntityArgument.entity())
.executes { execute(it, it.resourceLocation(SCRIPT), null, null, it.entity(POKEMON)) }
.then(argument(POKEMON, StringArgumentType.string())
.executes { execute(it, it.resourceLocation(SCRIPT), null, null, it.uuid(POKEMON)) }
)
)
)
}
private fun execute(context: CommandContext<CommandSourceStack>, scriptId: ResourceLocation, player: ServerPlayer?, npc: Entity?, pokemon: Entity? = null): Int {
private fun execute(context: CommandContext<CommandSourceStack>, scriptId: ResourceLocation, player: ServerPlayer?, npcId: UUID?, pokemonId: UUID? = null): Int {
try {
val runtime = MoLangRuntime().setup()
val entity = context.source.entity
val npc = npcId?.let { context.source.level.getEntity(it) as? NPCEntity }
val pokemon = pokemonId?.let { context.source.level.getEntity(it) as? NPCEntity }
(npc as? NPCEntity)?.let { runtime.withQueryValue("npc", npc.struct) }
(pokemon as? PokemonEntity)?.let { runtime.withQueryValue("pokemon", pokemon.struct) }
npc?.let { runtime.withQueryValue("npc", npc.struct) }
pokemon?.let { runtime.withQueryValue("pokemon", pokemon.struct) }
player?.let { runtime.withQueryValue("player", player.asMoLangValue()) }
entity?.let { runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue()) }
CobblemonScripts.run(scriptId, runtime)
} catch (e: Exception) {
@@ -18,6 +18,7 @@ enum class Category(val lang: String) {
Pokedex("pokedex"),
Storage("storage"),
Starter("starter"),
System("system"),
Interface("interface"),
Riding("riding"),
Debug("debug"),
@@ -16,8 +16,11 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.pokeball.catching.calculators.CobblemonCaptureCalculator
import com.cobblemon.mod.common.util.adapters.CaptureCalculatorAdapter
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
import com.cobblemon.mod.common.util.adapters.ZoneIdAdapter
import com.google.gson.GsonBuilder
import com.google.gson.annotations.SerializedName
import java.time.ZoneId
import java.time.ZoneOffset
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
@@ -29,6 +32,7 @@ class CobblemonConfig {
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(CaptureCalculator::class.java, CaptureCalculatorAdapter)
.registerTypeHierarchyAdapter(ZoneId::class.java, ZoneIdAdapter)
.create()
}
@@ -263,6 +267,9 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Pokedex, lang = "hide_unimplemented_pokemon_in_the_pokedex")
var hideUnimplementedPokemonInThePokedex = false
@CobblemonConfigField(Category.System, lang = "time_zone_id")
var timeZoneId: ZoneId = ZoneOffset.systemDefault()
@CobblemonConfigField(Category.Interface, lang = "summary_pokemon_follow_cursor")
var summaryPokemonFollowCursor = true
@@ -52,6 +52,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
import com.cobblemon.mod.common.api.riding.events.SelectDriverEvent
import com.cobblemon.mod.common.api.riding.sound.RideLoopSound
import com.cobblemon.mod.common.api.riding.sound.RideSoundManager
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.riding.util.RidingAnimationData
@@ -100,18 +101,10 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolut
import com.cobblemon.mod.common.pokemon.feature.StashHandler
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import com.mojang.serialization.Codec
import com.mojang.serialization.Dynamic
import java.util.Optional
import java.util.UUID
import java.util.concurrent.CompletableFuture
import kotlin.math.PI
import kotlin.math.ceil
import net.minecraft.client.Minecraft
import net.minecraft.core.BlockPos
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.CompoundTag
@@ -160,13 +153,15 @@ import net.minecraft.world.item.ItemUtils
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.gameevent.GameEvent
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import kotlin.math.min
import java.util.*
import java.util.concurrent.CompletableFuture
import kotlin.math.PI
import kotlin.math.ceil
@Suppress("unused")
open class PokemonEntity(
@@ -306,7 +301,6 @@ open class PokemonEntity(
.also {
it.environment.query.addFunction("passenger_count") { DoubleValue(passengers.size.toDouble()) }
it.environment.query.addFunction("ride_velocity") { DoubleValue(getRideVelocity().length()) }
it.environment.query.addFunction("driver_input") { DoubleValue(min(ridingAnimationData.driverInputSpring.value.length(),1.0)) }
it.environment.query.addFunction("get_ride_stats") { params ->
val rideStat = RidingStat.valueOf(params.getString(0).uppercase())
val rideStyle = RidingStyle.valueOf(params.getString(1).uppercase())
@@ -566,12 +560,9 @@ open class PokemonEntity(
isPokemonFlying = flyDist - flyDistO > 0.005F
isPokemonWalking = walkDist - walkDistO > 0.005F
if (passengers.isNotEmpty() && level().isClientSide) {
if (passengers.isNotEmpty()) {
rideSoundManager.tick()
ridingAnimationData.update()
} else if (!passengers.isNotEmpty() && level().isClientSide)
{
rideSoundManager.stop()
}
flyDistO = flyDist
@@ -1077,10 +1068,22 @@ open class PokemonEntity(
player.playSound(SoundEvents.MOOSHROOM_MILK, 1.0f, 1.0f)
// if the Mooshtank ate a Flower beforehand
if (pokemon.lastFlowerFed != ItemStack.EMPTY && pokemon.aspects.any { it.contains("mooshtank-brown") }) {
SuspiciousEffectHolder.tryGet(pokemon.lastFlowerFed.item)?.let {
when (pokemon.lastFlowerFed.item) {
Items.ALLIUM -> MobEffects.FIRE_RESISTANCE to 80
Items.AZURE_BLUET -> MobEffects.BLINDNESS to 160
Items.BLUE_ORCHID, Items.DANDELION -> MobEffects.SATURATION to 7
Items.CORNFLOWER -> MobEffects.JUMP to 120
Items.LILY_OF_THE_VALLEY -> MobEffects.POISON to 240
Items.OXEYE_DAISY -> MobEffects.REGENERATION to 160
Items.POPPY, Items.TORCHFLOWER -> MobEffects.NIGHT_VISION to 100
Items.PINK_TULIP, Items.RED_TULIP, Items.WHITE_TULIP, Items.ORANGE_TULIP -> MobEffects.WEAKNESS to 180
Items.WITHER_ROSE -> MobEffects.WITHER to 160
CobblemonItems.PEP_UP_FLOWER -> MobEffects.LEVITATION to 160
else -> null
}?.let {
// modify the suspicious stew with the effect
val susStewStack = Items.SUSPICIOUS_STEW.defaultInstance.copy()
susStewStack.set(DataComponents.SUSPICIOUS_STEW_EFFECTS, it.suspiciousEffects)
val susStewStack = Items.SUSPICIOUS_STEW.defaultInstance
//SuspiciousStewItem.addEffectsToStew(susStewStack, listOf(StewEffect(it.first, it.second)))
val susStewEffect = ItemUtils.createFilledResult(itemStack, player, susStewStack)
//give player modified Suspicious Stew
player.setItemInHand(hand, susStewEffect)
@@ -1113,7 +1116,7 @@ open class PokemonEntity(
itemStack.`is`(Items.WITHER_ROSE) ||
itemStack.`is`(CobblemonItems.PEP_UP_FLOWER)
) {
if (pokemon.aspects.any { it.contains("mooshtank-brown") }) {
if (pokemon.aspects.any { it.contains("mooshtank") }) {
player.playSound(SoundEvents.MOOSHROOM_EAT, 1.0f, 1.0f)
pokemon.lastFlowerFed = itemStack
return InteractionResult.sidedSuccess(level().isClientSide)
@@ -1763,8 +1766,8 @@ open class PokemonEntity(
}
// Rotate velocity vector to face the current y rotation
val f = Mth.sin(this.yRot.toRadians())
val g = Mth.cos(this.yRot.toRadians())
val f = Mth.sin(this.yRot * 0.017453292f)
val g = Mth.cos(this.yRot * 0.017453292f)
val v = Vec3(
inp.x * g.toDouble() - inp.z * f.toDouble(),
inp.y,
@@ -2125,9 +2128,8 @@ open class PokemonEntity(
}
}
this.yRotO = this.yRot
val rotation = behaviour.rotation(settings, state, this, driver)
this.yRotO = this.yRot
setRot(rotation.y, rotation.x)
this.yHeadRot = this.yRot
this.yBodyRot = this.yRot
@@ -1,48 +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.orientation
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket.Companion.ID
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readMatrix3f
import com.cobblemon.mod.common.util.writeMatrix3f
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.entity.player.Player
import org.joml.Matrix3f
import org.joml.Vector3f
/**
* Packet sent from the server to update the current input from
* the driver of a ridden pokemon. This data is needed for client
* side animation data updates found in RidingAnimationData
*
* @author Jackowes
* @since August 1st, 2025
*/
class ClientboundUpdateDriverInputPacket internal constructor(
val driverInput: Vector3f,
val entityId: Int
) : NetworkPacket<ClientboundUpdateDriverInputPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVarInt(entityId)
buffer.writeVector3f(driverInput)
}
companion object {
val ID = cobblemonResource("s2c_update_driver_input")
fun decode(buffer: RegistryFriendlyByteBuf): ClientboundUpdateDriverInputPacket {
val entityId = buffer.readVarInt()
val driverInput = buffer.readVector3f()
return ClientboundUpdateDriverInputPacket(driverInput, entityId)
}
}
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.server.riding
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
import org.joml.Vector3f
class ServerboundUpdateDriverInputPacket internal constructor(
val driverInput: Vector3f
) : NetworkPacket<ServerboundUpdateDriverInputPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVector3f(driverInput)
}
companion object {
val ID = cobblemonResource("c2s_update_driver_input")
fun decode(buffer: RegistryFriendlyByteBuf): ServerboundUpdateDriverInputPacket {
val driverInput = buffer.readVector3f()
return ServerboundUpdateDriverInputPacket(driverInput)
}
}
}
@@ -1,28 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.serverhandling.riding
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.duck.PlayerDuck
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object DriverInputPacketHandler : ServerNetworkPacketHandler<ServerboundUpdateDriverInputPacket> {
override fun handle(
packet: ServerboundUpdateDriverInputPacket,
server: MinecraftServer,
player: ServerPlayer
) {
if (player is PlayerDuck) {
player.setDriverInput(packet.driverInput)
}
}
}
@@ -93,13 +93,7 @@ class SentOutState() : ActivePokemonState() {
this.dimension = entity.level().dimension()
}
override fun getIcon(pokemon: Pokemon): ResourceLocation {
val isBeingRidden = (pokemon.entity?.countPlayerPassengers() ?: 0) > 0
val icon = if (isBeingRidden) "mounted" else "released"
return cobblemonResource("textures/gui/party/party_icon_${icon}.png")
}
override fun getIcon(pokemon: Pokemon) = cobblemonResource("textures/gui/party/party_icon_released.png")
override fun writeToNBT(nbt: CompoundTag) = null
override fun writeToJSON(json: JsonObject) = null
@@ -16,5 +16,4 @@ import net.minecraft.commands.arguments.ResourceLocationArgument
fun CommandContext<CommandSourceStack>.player(argumentName: String = "player") = EntityArgument.getPlayer(this, argumentName)
fun CommandContext<CommandSourceStack>.string(argumentName: String) = this.getArgument(argumentName, String::class.java)
fun CommandContext<CommandSourceStack>.uuid(argumentName: String) = this.getArgument(argumentName, String::class.java).asUUID
fun CommandContext<CommandSourceStack>.resourceLocation(argumentName: String) = ResourceLocationArgument.getId(this, argumentName)
fun CommandContext<CommandSourceStack>.entity(argumentName: String) = EntityArgument.getEntity(this, argumentName)
fun CommandContext<CommandSourceStack>.resourceLocation(argumentName: String) = ResourceLocationArgument.getId(this, argumentName)
@@ -10,22 +10,20 @@ package com.cobblemon.mod.common.util
import com.google.common.collect.ImmutableMap
import com.mojang.serialization.Dynamic
import com.mojang.serialization.JavaOps
import java.util.ArrayDeque
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.min
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.tags.TagKey
import net.minecraft.util.ExtraCodecs
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.BooleanOp
@@ -181,21 +179,25 @@ fun Entity.isDusk(): Boolean {
return time in 12000..13000
}
fun Entity.isStandingOn(blocks: Set<String>, depth: Int = 2): Boolean {
fun Entity.isStandingOnSand(): Boolean {
return isStandingOn(setOf(Blocks.SAND))
}
fun Entity.isStandingOnRedSand(): Boolean {
return isStandingOn(setOf(Blocks.RED_SAND))
}
fun Entity.isStandingOnSandOrRedSand(): Boolean {
return isStandingOn(setOf(Blocks.SAND, Blocks.RED_SAND))
}
fun Entity.isStandingOn(blocks: Set<Block>, depth: Int = 2): Boolean {
for (currentDepth in 1..depth) {
val bellowBlockPos = blockPosition().below(currentDepth)
val blockState = level().getBlockState(bellowBlockPos)
if (blockState.isAir || !blockState.isCollisionShapeFullBlock(level(), bellowBlockPos)) continue
val elementOrTags = ExtraCodecs.TAG_OR_ELEMENT_ID.listOf().decode(JavaOps.INSTANCE, blocks).result().get().first
elementOrTags.forEach {
if (it.tag) {
if (blockState.blockHolder.`is`(TagKey.create(Registries.BLOCK, it.id))) return true
} else {
if (blockState.blockHolder.`is`(it.id)) return true
}
}
if (blocks.contains(blockState.block)) return true
}
return false
@@ -19,8 +19,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.RunStrategy
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.*
@@ -39,19 +37,15 @@ import java.lang.reflect.Type
object RidingBehaviourSettingsAdapter : JsonDeserializer<RidingBehaviourSettings?> {
val types: MutableMap<ResourceLocation, Class<out RidingBehaviourSettings>> = mutableMapOf(
BirdBehaviour.KEY to BirdSettings::class.java,
BirdNoRollBehaviour.KEY to BirdNoRollSettings::class.java,
DolphinBehaviour.KEY to DolphinSettings::class.java,
HorseBehaviour.KEY to HorseSettings::class.java,
BoatBehaviour.KEY to BoatSettings::class.java,
GliderBehaviour.KEY to GliderSettings::class.java,
HelicopterBehaviour.KEY to HelicopterSettings::class.java,
JetBehaviour.KEY to JetSettings::class.java,
JetNoRollBehaviour.KEY to JetNoRollSettings::class.java,
BurstBehaviour.KEY to BurstSettings::class.java,
VehicleBehaviour.KEY to VehicleSettings::class.java,
MinekartBehaviour.KEY to MinekartSettings::class.java,
HoverBehaviour.KEY to HoverSettings::class.java,
RocketBehaviour.KEY to RocketSettings::class.java,
CompositeBehaviour.KEY to CompositeSettings::class.java,
/*
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.util.adapters
import com.cobblemon.mod.common.Cobblemon
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
import java.time.ZoneId
import java.time.ZoneOffset
/**
* A type adapter for [ZoneOffset]s.
* This will never fail to deserialize and will instead default to the System's default time zone and log an error message.
*
* @author JPAK
* @since December 26, 2024
*/
object ZoneIdAdapter: JsonSerializer<ZoneId>, JsonDeserializer<ZoneId> {
override fun serialize(
src: ZoneId,
typeOfSrc: Type,
context: JsonSerializationContext
): JsonElement {
return JsonPrimitive(src.id)
}
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): ZoneId {
return try {
ZoneId.of(json.asString)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Failed to load ZoneId from the ID {}, defaulting to the system time zone", json.asString)
ZoneOffset.systemDefault()
}
}
}
@@ -17503,10 +17503,6 @@
"3.1667": {
"effect": "animation.leather.wing_flap.medium"
}
},
"timeline": {
"1.125": "q.has_entity ? { q.sound('animation.chitin.wing_flap.medium'); };",
"3.1667": "q.has_entity ? { q.particle('cobblemon:ambient_shiny_sparkle', 'root'); };"
}
},
"animation.charizard.air_fly": {
@@ -18795,10 +18791,6 @@
"3.0833": {
"effect": "animation.leather.wing_flap.medium"
}
},
"timeline": {
"1.0833": "q.particle('cobblemon:ambient_shiny_sparkle', 'root');",
"3.0833": "q.sound('animation.chitin.wing_flap.medium');"
}
},
"animation.charizard.air_glide_TEST": {
@@ -6,9 +6,9 @@
"bones": {
"body": {
"rotation": [
"45*math.clamp(q.riding.velocity_forward(0)* 0.5 + q.riding.input_forward(10), -1.0, 1.0)",
"45*q.riding.velocity_forward(0)",
"0 - Math.Sin(((q.anim_time + 0.1) * 180 * 0.5) * 1) * 5",
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 + 45*math.clamp(q.riding.velocity_right(0)* 0.5 + q.riding.input_right(10)* 0.5,-1.0,1.0)"
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 + 45*q.riding.velocity_right(0)"
],
"position": ["Math.Sin((q.anim_time * 180 * 0.5) * 1) * 1.5", "7+Math.Sin((q.anim_time * 90 * 0.5) * 1) * 1.5", 0]
},
@@ -50,9 +50,9 @@
"bones": {
"body": {
"rotation": [
"2.5 + 45*math.clamp(q.riding.velocity_forward(0) + q.riding.input_forward(5),-1.0,1.0)",
"2.5 + 45*q.riding.velocity_forward(0)",
"- Math.Sin(((q.anim_time + 0.1) * 180 * 0.5) * 1) * 5",
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 - 45*math.clamp(q.riding.velocity_right(0) + q.riding.right(5),-1.0,1.0)"
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 - 45*q.riding.velocity_right(0)"
],
"position": ["Math.Sin((q.anim_time * 180 * 0.5) * 1) * 1.5", "7+Math.Sin((q.anim_time * 90 * 0.5) * 1) * 1.5", 0]
},
@@ -407,7 +407,7 @@
]
},
{
"name": "locator_seat_2",
"name": "locator_seat_1",
"parent": "body",
"pivot": [0, 0, 0],
"locators": {
@@ -4,11 +4,11 @@
{
"description": {
"identifier": "geometry.golurk",
"texture_width": 512,
"texture_height": 256,
"texture_width": 128,
"texture_height": 128,
"visible_bounds_width": 7,
"visible_bounds_height": 7.5,
"visible_bounds_offset": [0, 3.25, 0]
"visible_bounds_height": 4.5,
"visible_bounds_offset": [0, 1.75, 0]
},
"bones": [
{
@@ -18,743 +18,342 @@
{
"name": "body",
"parent": "golurk",
"pivot": [0, 61, 0]
},
{
"name": "shoulder_pivot",
"parent": "body",
"pivot": [0, 68.5, 0]
"pivot": [0, 38, 0]
},
{
"name": "torso",
"parent": "shoulder_pivot",
"pivot": [0, 44.5, 0],
"parent": "body",
"pivot": [0, 21.5, 0],
"cubes": [
{"origin": [-18, 45, -18], "size": [36, 32, 36], "uv": [0, 0]},
{"origin": [-18, 51, -18], "size": [36, 24, 7], "inflate": -0.25, "uv": [170, 194]}
]
},
{
"name": "chest_glow_red",
"parent": "torso",
"pivot": [0, 63, -14.5],
"cubes": [
{"origin": [-18, 51, -18], "size": [36, 24, 7], "inflate": -0.3, "uv": [170, 225]}
]
},
{
"name": "chest_glow_off",
"parent": "torso",
"pivot": [0, 63, -14.5],
"cubes": [
{"origin": [-18, 51, -18], "size": [36, 24, 7], "inflate": -0.3, "uv": [170, 163]}
]
},
{
"name": "waist",
"parent": "torso",
"pivot": [0, 45.25, 0]
},
{
"name": "skirt",
"parent": "waist",
"pivot": [0, 45.25, 0],
"cubes": [
{"origin": [-14, 41, -14], "size": [28, 8, 28], "uv": [0, 96]},
{"origin": [-14, 41, -0.5], "size": [28, 3, 1], "inflate": 0.01, "pivot": [0, 42.5, 0], "rotation": [0, -90, 0], "uv": [109, 192]},
{"origin": [-14, 41, -0.5], "size": [28, 3, 1], "inflate": 0.01, "uv": [109, 197]},
{"origin": [-17, 34, -17], "size": [34, 7, 34], "uv": [0, 212]},
{"origin": [-17, 34, -0.5], "size": [34, 7, 1], "inflate": 0.01, "pivot": [0, 37.5, 0], "rotation": [0, -90, 0], "uv": [177, 145]},
{"origin": [-17, 34, -0.5], "size": [34, 7, 1], "inflate": 0.01, "uv": [177, 154]}
]
},
{
"name": "fire_waist_base",
"parent": "skirt",
"pivot": [0, 34.05, 0],
"cubes": [
{"origin": [-12, 34.05, -12], "size": [24, 0, 24], "uv": [244, 1]}
]
},
{
"name": "fire_waist",
"parent": "skirt",
"pivot": [0, 38.25, 0]
},
{
"name": "fire_waist_interior",
"parent": "fire_waist",
"pivot": [0, 38.25, 0],
"cubes": [
{"origin": [-16, 4.25, 0], "size": [32, 34, 0], "pivot": [0, 32.25, 0], "rotation": [0, 45, 0], "uv": [447, 35]}
]
},
{
"name": "fire_waist_interior2",
"parent": "fire_waist",
"pivot": [0, 38.25, 0],
"cubes": [
{"origin": [0, 4.25, -16], "size": [0, 34, 32], "pivot": [0, 32.25, 0], "rotation": [0, 45, 0], "uv": [447, -31]}
]
},
{
"name": "fire_waist_front",
"parent": "fire_waist",
"pivot": [0, 34, -12],
"rotation": [-10, 0, 0],
"cubes": [
{"origin": [-16, 8.25, -12], "size": [32, 30, 0], "uv": [317, 1]}
]
},
{
"name": "fire_waist_back",
"parent": "fire_waist",
"pivot": [0, 34, 12],
"rotation": [10, 0, 0],
"cubes": [
{"origin": [-16, 8.25, 12], "size": [32, 30, 0], "uv": [317, 32]}
]
},
{
"name": "fire_waist_right",
"parent": "fire_waist",
"pivot": [-12, 34, 0],
"rotation": [0, 0, 10],
"cubes": [
{"origin": [-12, 8.25, -16], "size": [0, 30, 32], "uv": [382, -31]}
]
},
{
"name": "fire_waist_left",
"parent": "fire_waist",
"pivot": [12, 34, 0],
"rotation": [0, 0, -10],
"cubes": [
{"origin": [12, 8.25, -16], "size": [0, 30, 32], "uv": [382, 0]}
]
},
{
"name": "bandage",
"parent": "torso",
"pivot": [-3.8, 64.6, -19],
"rotation": [0, 0, 45],
"cubes": [
{"origin": [-11.8, 60.6, -20], "size": [16, 8, 2], "uv": [119, 132]}
{"origin": [-9, 19, -9], "size": [18, 5, 18], "uv": [0, 40]},
{"origin": [-7.5, 24, -7.5], "size": [15, 2, 15], "uv": [54, 40]},
{"origin": [-10, 26, -10], "size": [20, 20, 20], "uv": [0, 0]},
{"origin": [-8, 33, -11], "size": [11, 5, 1], "pivot": [-1.5, 35.5, -10.5], "rotation": [0, 0, 45], "uv": [98, 11]}
]
},
{
"name": "head",
"parent": "torso",
"pivot": [0, 76, 0],
"pivot": [0.5, 46, 0.5],
"cubes": [
{"origin": [-5, 74, -5], "size": [10, 10, 10], "uv": [136, 142]},
{"origin": [-1.5, 82, -1], "size": [4, 5, 2], "pivot": [1, 84.5, 0.5], "rotation": [0, 0, 10], "uv": [19, 20]},
{"origin": [-5.5, 84, 0], "size": [4, 3, 0], "pivot": [1, 84.5, 0.5], "rotation": [0, 0, 10], "uv": [26, 27]}
{"origin": [-3, 44.75, -3], "size": [6, 6, 6], "uv": [83, 99]},
{"origin": [-4.5, 49.5, 0.5], "size": [6, 5, 0], "pivot": [1, 50.5, 0.5], "rotation": [0, 0, 10], "uv": [47, 63]}
]
},
{
"name": "eyes",
"name": "eye_left",
"parent": "head",
"pivot": [0, 80, -5.01]
"pivot": [1.4, 47.5, -2.51],
"cubes": [
{"origin": [0.65, 46.5, -3.01], "size": [1, 3, 1], "uv": [15, 12]},
{"origin": [1.15, 46.5, -3.01], "size": [1, 1, 1], "uv": [4, 18]}
]
},
{
"name": "eye_left",
"parent": "eyes",
"pivot": [2, 80, -5.01],
"name": "eyelid_left",
"parent": "eye_left",
"pivot": [1.4, 47.5, -2.41],
"cubes": [
{"origin": [1, 78, -5.01], "size": [2, 4, 0], "uv": [32, 0]},
{"origin": [3, 78, -5.01], "size": [1, 2, 0], "uv": [24, 2]}
{"origin": [0.65, 46.5, -2.91], "size": [1, 3, 1], "inflate": 0.001, "uv": [15, 0]},
{"origin": [1.15, 46.5, -2.91], "size": [1, 1, 1], "inflate": 0.001, "uv": [0, 18]}
]
},
{
"name": "eye_right",
"parent": "eyes",
"pivot": [-2, 80, -5.01],
"cubes": [
{"origin": [-3, 78, -5.01], "size": [2, 4, 0], "uv": [28, 0]}
]
},
{
"name": "eyes_red",
"parent": "head",
"pivot": [0, 80, -4.91]
},
{
"name": "eye_red_left",
"parent": "eyes_red",
"pivot": [2, 80, -4.91],
"pivot": [-1.65, 48.25, -2.51],
"cubes": [
{"origin": [1, 78, -4.91], "size": [2, 4, 0], "uv": [32, 4]},
{"origin": [3, 78, -4.91], "size": [1, 2, 0], "uv": [2, 2]}
{"origin": [-1.65, 46.5, -3.01], "size": [1, 3, 1], "uv": [0, 12]}
]
},
{
"name": "eye_red_right",
"parent": "eyes_red",
"pivot": [-2, 80, -4.91],
"name": "eyelid_right",
"parent": "eye_right",
"pivot": [-1.15, 48, -2.41],
"cubes": [
{"origin": [-3, 78, -4.91], "size": [2, 4, 0], "uv": [28, 4]}
]
},
{
"name": "eyes_off",
"parent": "head",
"pivot": [0, 80, -4.91]
},
{
"name": "eye_off_left",
"parent": "eyes_off",
"pivot": [2, 80, -4.91],
"cubes": [
{"origin": [1, 78, -4.91], "size": [2, 4, 0], "uv": [32, 8]},
{"origin": [3, 78, -4.91], "size": [1, 2, 0], "uv": [2, 22]}
]
},
{
"name": "eye_off_right",
"parent": "eyes_off",
"pivot": [-2, 80, -4.91],
"cubes": [
{"origin": [-3, 78, -4.91], "size": [2, 4, 0], "uv": [28, 8]}
]
},
{
"name": "shoulder_left",
"parent": "torso",
"pivot": [25.6, 67, 0],
"cubes": [
{"origin": [19.1, 76.4, -2.5], "size": [9, 5, 5], "pivot": [25.6, 68.4, 0], "rotation": [0, 0, 45], "uv": [102, 236]},
{"origin": [22.1, 81.4, -2.5], "size": [6, 5, 5], "pivot": [25.6, 68.4, 0], "rotation": [0, 0, 45], "uv": [177, 132]},
{"origin": [16.1, 59.4, -8.5], "size": [17, 17, 17], "pivot": [25.6, 68.4, 0], "rotation": [0, 0, 45], "uv": [68, 132]},
{"origin": [19.1, 62.4, -10.5], "size": [11, 11, 2], "pivot": [25.6, 68.4, -2], "rotation": [0, 0, 45], "uv": [0, 109]},
{"origin": [19.1, 62.4, 8.5], "size": [11, 11, 2], "pivot": [25.6, 68.4, 17], "rotation": [0, 0, 45], "uv": [46, 191]}
]
},
{
"name": "shoulder_glow_red_left",
"parent": "shoulder_left",
"pivot": [25.6, 68.4, -10.4],
"cubes": [
{"origin": [20.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [25.6, 68.4, -1.9], "rotation": [0, 0, 45], "uv": [200, 122]}
]
},
{
"name": "shoulder_glow_off_left",
"parent": "shoulder_left",
"pivot": [25.6, 68.4, -10.4],
"cubes": [
{"origin": [20.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [25.6, 68.4, -1.9], "rotation": [0, 0, 45], "uv": [200, 131]}
{"origin": [-1.65, 46.5, -2.91], "size": [1, 3, 1], "inflate": 0.001, "uv": [0, 0]}
]
},
{
"name": "arm_left",
"parent": "shoulder_left",
"pivot": [24.1, 59.4, 0],
"cubes": [
{"origin": [20.1, 50.4, -3], "size": [8, 14, 6], "uv": [108, 0]}
]
"parent": "torso",
"pivot": [13.6, 44, 0]
},
{
"name": "forearm_left",
"name": "arm_left2",
"parent": "arm_left",
"pivot": [24.1, 52.4, 0],
"pivot": [16.1, 43.9, 0],
"cubes": [
{"origin": [16.6, 47.4, -8], "size": [15, 5, 16], "uv": [62, 191]},
{"origin": [13.6, 39.9, -10], "size": [21, 8, 20], "uv": [0, 68]}
]
},
{
"name": "fire_hand_base_left",
"parent": "forearm_left",
"pivot": [24.1, 39.95, 0],
"cubes": [
{"origin": [16.1, 39.95, -8], "size": [16, 0, 16], "uv": [326, 124], "mirror": true}
]
},
{
"name": "fire_hand_left",
"parent": "forearm_left",
"pivot": [24.1, 42.15, 0]
},
{
"name": "fire_hand_interior_left1",
"parent": "fire_hand_left",
"pivot": [24.1, 44.15, 0],
"cubes": [
{"origin": [12.6, 17.15, 0], "size": [23, 25, 0], "pivot": [24.1, 38.15, 0], "rotation": [0, -45, 0], "uv": [465, 150], "mirror": true}
]
},
{
"name": "fire_hand_interior_left2",
"parent": "fire_hand_left",
"pivot": [24.1, 44.15, 0],
"cubes": [
{"origin": [24.1, 17.15, -11.5], "size": [0, 25, 23], "pivot": [24.1, 38.15, 0], "rotation": [0, -45, 0], "uv": [465, 101], "mirror": true}
]
},
{
"name": "fire_hand_front_left",
"parent": "fire_hand_left",
"pivot": [24.1, 39.9, -8],
"rotation": [-10, 0, 0],
"cubes": [
{"origin": [13.1, 20.15, -8], "size": [22, 22, 0], "uv": [375, 124], "mirror": true}
]
},
{
"name": "fire_hand_back_left",
"parent": "fire_hand_left",
"pivot": [24.1, 39.9, 8],
"rotation": [10, 0, 0],
"cubes": [
{"origin": [13.1, 20.15, 8], "size": [22, 22, 0], "uv": [375, 147], "mirror": true}
]
},
{
"name": "fire_hand_outer_left",
"parent": "fire_hand_left",
"pivot": [32.1, 39.9, 0],
"rotation": [0, 0, -10],
"cubes": [
{"origin": [32.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 102], "mirror": true}
]
},
{
"name": "fire_hand_inner_left",
"parent": "fire_hand_left",
"pivot": [16.1, 39.9, 0],
"rotation": [0, 0, 10],
"cubes": [
{"origin": [16.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 125], "mirror": true}
{"origin": [11.1, 40.9, 4.5], "size": [7, 7, 2], "pivot": [13.6, 45.4, -1], "rotation": [0, 0, 45], "uv": [107, 17]},
{"origin": [4.6, 41.4, -2.5], "size": [5, 5, 5], "pivot": [13.6, 45.4, 0], "rotation": [0, 0, 45], "uv": [102, 106]},
{"origin": [11.1, 40.9, -6.5], "size": [7, 7, 2], "pivot": [13.6, 45.4, -1], "rotation": [0, 0, 45], "uv": [29, 63]},
{"origin": [9.6, 39.4, -4.5], "size": [10, 10, 9], "pivot": [13.6, 45.4, 0], "rotation": [0, 0, 45], "uv": [0, 63]},
{"origin": [16.6, 41.4, -2.5], "size": [9, 5, 5], "uv": [99, 42]}
]
},
{
"name": "hand_left",
"parent": "forearm_left",
"pivot": [24.1, 39.9, 0],
"parent": "arm_left2",
"pivot": [24.85, 44, 0],
"cubes": [
{"origin": [19.6, 27.9, -8], "size": [9, 12, 16], "inflate": 0.02, "uv": [186, 0]},
{"origin": [27.6, 28.9, -3.3], "size": [3, 9, 9], "pivot": [26.1, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [102, 218]}
{"origin": [26.6, 38.4, -5.5], "size": [5, 11, 11], "uv": [38, 68]},
{"origin": [23.6, 39.4, -4.5], "size": [3, 9, 9], "uv": [0, 96]}
]
},
{
"name": "hand_glow_red_left",
"name": "hand_left2",
"parent": "hand_left",
"pivot": [30.5, 34.9, 0.7],
"pivot": [31.85, 42.5, 0],
"cubes": [
{"origin": [30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [238, 99]}
{"origin": [32.25, 46.9, -2.75], "size": [5, 1, 5], "pivot": [34.5, 47.4, 0], "rotation": [0, -45, 0], "uv": [101, 99]},
{"origin": [31.1, 40.9, -4.5], "size": [7, 6, 9], "uv": [27, 90]}
]
},
{
"name": "hand_glow_off_left",
"parent": "hand_left",
"pivot": [30.5, 34.9, 0.7],
"name": "finger_left",
"parent": "hand_left2",
"pivot": [38.1, 46.4, -3.5],
"cubes": [
{"origin": [30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [238, 106]}
{"origin": [38.1, 44.4, -4.5], "size": [3, 2, 2], "inflate": 0.001, "uv": [92, 32]}
]
},
{
"name": "finger_thumb_left",
"parent": "hand_left",
"pivot": [19.6, 34.9, -8],
"rotation": [-12.5, 0, 25],
"name": "finger_left2",
"parent": "finger_left",
"pivot": [41.1, 46.4, -3.5],
"cubes": [
{"origin": [16.6, 29.9, -10.5], "size": [6, 9, 7], "uv": [108, 20]}
{"origin": [41.1, 44.4, -4.5], "size": [2, 2, 2], "uv": [60, 94]}
]
},
{
"name": "finger_thumb_left2",
"parent": "finger_thumb_left",
"pivot": [19.1, 29.9, -10.5],
"name": "finger_left3",
"parent": "hand_left2",
"pivot": [38.1, 46.4, -1.25],
"cubes": [
{"origin": [17.1, 23.9, -10.5], "size": [5, 6, 5], "uv": [0, 68]}
{"origin": [38.1, 44.4, -2.25], "size": [3, 2, 2], "inflate": 0.001, "uv": [86, 84]}
]
},
{
"name": "finger_index_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, -6],
"name": "finger_left4",
"parent": "finger_left3",
"pivot": [41.1, 46.4, -1.25],
"cubes": [
{"origin": [21.6, 21.9, -8], "size": [6, 6, 4], "inflate": 0.015, "uv": [132, 122]}
{"origin": [41.1, 44.4, -2.25], "size": [3, 2, 2], "uv": [27, 86]}
]
},
{
"name": "finger_index_left2",
"parent": "finger_index_left",
"pivot": [27.1, 21.9, -6],
"name": "finger_left5",
"parent": "hand_left2",
"pivot": [38.1, 46.4, 1],
"cubes": [
{"origin": [22.1, 15.9, -8], "size": [5, 6, 4], "uv": [154, 96]}
{"origin": [38.1, 44.4, 0], "size": [3, 2, 2], "inflate": 0.001, "uv": [27, 82]}
]
},
{
"name": "finger_middle_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, -2],
"name": "finger_left6",
"parent": "finger_left5",
"pivot": [41.1, 46.4, 1],
"cubes": [
{"origin": [21.6, 21.9, -4], "size": [6, 6, 4], "inflate": 0.01, "uv": [221, 29]}
{"origin": [41.1, 44.4, 0], "size": [2, 2, 2], "uv": [90, 36]}
]
},
{
"name": "finger_middle_left2",
"parent": "finger_middle_left",
"pivot": [27.1, 21.9, -2],
"name": "finger_left7",
"parent": "hand_left2",
"pivot": [38.1, 46.4, 3.25],
"cubes": [
{"origin": [22.1, 15.9, -4], "size": [5, 6, 4], "uv": [62, 78]}
{"origin": [38.1, 44.4, 2.25], "size": [2, 2, 2], "inflate": 0.001, "uv": [0, 86]}
]
},
{
"name": "finger_ring_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, 2],
"name": "finger_left8",
"parent": "finger_left7",
"pivot": [40.1, 46.4, 3.25],
"cubes": [
{"origin": [21.6, 21.9, 0], "size": [6, 6, 4], "inflate": 0.015, "uv": [172, 122]}
{"origin": [40.1, 44.4, 2.25], "size": [2, 2, 2], "uv": [0, 82]}
]
},
{
"name": "finger_ring_left2",
"parent": "finger_ring_left",
"pivot": [27.1, 21.9, 2],
"name": "finger_left9",
"parent": "hand_left2",
"pivot": [35.6, 43.15, -3.5],
"cubes": [
{"origin": [22.1, 15.9, 0], "size": [5, 6, 4], "uv": [62, 68]}
{"origin": [35.1, 41.65, -5.5], "size": [3, 3, 3], "inflate": 0.001, "uv": [50, 90]}
]
},
{
"name": "finger_pinky_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, 6],
"name": "finger_left10",
"parent": "finger_left9",
"pivot": [38.1, 43.15, -5.5],
"cubes": [
{"origin": [21.6, 21.9, 4], "size": [6, 6, 4], "inflate": 0.01, "uv": [226, 96]}
]
},
{
"name": "finger_pinky_left2",
"parent": "finger_pinky_left",
"pivot": [27.1, 21.9, 6],
"cubes": [
{"origin": [22.1, 16.9, 4], "size": [5, 5, 4], "uv": [2, 79]}
]
},
{
"name": "hand_fire_left",
"parent": "forearm_left",
"pivot": [24, 40.25, 0]
},
{
"name": "shoulder_right",
"parent": "torso",
"pivot": [-25.6, 67, 0],
"cubes": [
{"origin": [-28.1, 76.4, -2.5], "size": [9, 5, 5], "pivot": [-25.6, 68.4, 0], "rotation": [0, 0, -45], "uv": [0, 236]},
{"origin": [-28.1, 81.4, -2.5], "size": [6, 5, 5], "pivot": [-25.6, 68.4, 0], "rotation": [0, 0, -45], "uv": [155, 132]},
{"origin": [-33.1, 59.4, -8.5], "size": [17, 17, 17], "pivot": [-25.6, 68.4, 0], "rotation": [0, 0, -45], "uv": [0, 132]},
{"origin": [-30.1, 62.4, -10.5], "size": [11, 11, 2], "pivot": [-25.6, 68.4, -2], "rotation": [0, 0, -45], "uv": [0, 96]},
{"origin": [-30.1, 62.4, 8.5], "size": [11, 11, 2], "pivot": [-25.6, 68.4, 17], "rotation": [0, 0, -45], "uv": [51, 132]}
]
},
{
"name": "shoulder_glow_red_right",
"parent": "shoulder_right",
"pivot": [-25.6, 68.4, -10.4],
"cubes": [
{"origin": [-29.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [-25.6, 68.4, -1.9], "rotation": [0, 0, -45], "uv": [218, 122]}
]
},
{
"name": "shoulder_glow_off_right",
"parent": "shoulder_right",
"pivot": [-25.6, 68.4, -10.4],
"cubes": [
{"origin": [-29.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [-25.6, 68.4, -1.9], "rotation": [0, 0, -45], "uv": [218, 131]}
{"origin": [38.1, 41.65, -5.5], "size": [3, 3, 3], "uv": [89, 0]}
]
},
{
"name": "arm_right",
"parent": "shoulder_right",
"pivot": [-24.1, 59.4, 0],
"cubes": [
{"origin": [-28.1, 50.4, -3], "size": [8, 14, 6], "uv": [0, 0]}
]
"parent": "torso",
"pivot": [-13.6, 44, 0]
},
{
"name": "forearm_right",
"name": "arm_right2",
"parent": "arm_right",
"pivot": [-24.1, 52.4, 0],
"pivot": [-16.1, 43.9, 0],
"cubes": [
{"origin": [-31.6, 47.4, -8], "size": [15, 5, 16], "uv": [0, 191]},
{"origin": [-34.6, 39.9, -10], "size": [21, 8, 20], "uv": [82, 68]}
]
},
{
"name": "fire_hand_base_right",
"parent": "forearm_right",
"pivot": [-24.1, 39.95, 0],
"cubes": [
{"origin": [-32.1, 39.95, -8], "size": [16, 0, 16], "uv": [326, 71]}
]
},
{
"name": "fire_hand_right",
"parent": "forearm_right",
"pivot": [-24.1, 42.15, 0]
},
{
"name": "fire_hand_interior_right1",
"parent": "fire_hand_right",
"pivot": [-24.1, 44.15, 0],
"cubes": [
{"origin": [-35.6, 17.15, 0], "size": [23, 25, 0], "pivot": [-24.1, 38.15, 0], "rotation": [0, 45, 0], "uv": [465, 97]}
]
},
{
"name": "fire_hand_interior_right2",
"parent": "fire_hand_right",
"pivot": [-24.1, 44.15, 0],
"cubes": [
{"origin": [-24.1, 17.15, -11.5], "size": [0, 25, 23], "pivot": [-24.1, 38.15, 0], "rotation": [0, 45, 0], "uv": [465, 48]}
]
},
{
"name": "fire_hand_front_right",
"parent": "fire_hand_right",
"pivot": [-24.1, 39.9, -8],
"rotation": [-10, 0, 0],
"cubes": [
{"origin": [-35.1, 20.15, -8], "size": [22, 22, 0], "uv": [375, 71]}
]
},
{
"name": "fire_hand_back_right",
"parent": "fire_hand_right",
"pivot": [-24.1, 39.9, 8],
"rotation": [10, 0, 0],
"cubes": [
{"origin": [-35.1, 20.15, 8], "size": [22, 22, 0], "uv": [375, 94]}
]
},
{
"name": "fire_hand_outer_right",
"parent": "fire_hand_right",
"pivot": [-32.1, 39.9, 0],
"rotation": [0, 0, 10],
"cubes": [
{"origin": [-32.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 49]}
]
},
{
"name": "fire_hand_inner_right",
"parent": "fire_hand_right",
"pivot": [-16.1, 39.9, 0],
"rotation": [0, 0, -10],
"cubes": [
{"origin": [-16.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 72]}
{"origin": [-25.6, 41.4, -2.5], "size": [9, 5, 5], "uv": [99, 32]},
{"origin": [-19.6, 39.4, -4.5], "size": [10, 10, 9], "pivot": [-13.6, 45.4, 0], "rotation": [0, 0, -45], "uv": [60, 0]},
{"origin": [-18.1, 40.9, -6.5], "size": [7, 7, 2], "pivot": [-13.6, 45.4, -1], "rotation": [0, 0, -45], "uv": [0, 40]},
{"origin": [-9.6, 41.4, -2.5], "size": [5, 5, 5], "pivot": [-13.6, 45.4, 0], "rotation": [0, 0, -45], "uv": [44, 105]},
{"origin": [-18.1, 40.9, 4.5], "size": [7, 7, 2], "pivot": [-13.6, 45.4, -1], "rotation": [0, 0, -45], "uv": [0, 49]}
]
},
{
"name": "hand_right",
"parent": "forearm_right",
"pivot": [-24.1, 39.9, 0],
"parent": "arm_right2",
"pivot": [-24.85, 44, 0],
"cubes": [
{"origin": [-28.6, 27.9, -8], "size": [9, 12, 16], "inflate": 0.02, "uv": [136, 0]},
{"origin": [-30.6, 28.9, -3.3], "size": [3, 9, 9], "pivot": [-26.1, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [0, 218]}
{"origin": [-31.6, 38.4, -5.5], "size": [5, 11, 11], "uv": [61, 57]},
{"origin": [-26.6, 39.4, -4.5], "size": [3, 9, 9], "uv": [59, 92]}
]
},
{
"name": "hand_glow_red_right",
"name": "hand_right2",
"parent": "hand_right",
"pivot": [-30.5, 34.9, 0.7],
"pivot": [-31.85, 42.5, 0],
"cubes": [
{"origin": [-30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [-26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [224, 99]}
{"origin": [-37.25, 46.9, -2.75], "size": [5, 1, 5], "pivot": [-34.5, 47.4, 0], "rotation": [0, 45, 0], "uv": [0, 12]},
{"origin": [-38.1, 40.9, -4.5], "size": [7, 6, 9], "uv": [88, 84]}
]
},
{
"name": "hand_glow_off_right",
"parent": "hand_right",
"pivot": [-30.5, 34.9, 0.7],
"name": "finger_right",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, -3.5],
"cubes": [
{"origin": [-30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [-26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [224, 106]}
{"origin": [-41.1, 44.4, -4.5], "size": [3, 2, 2], "inflate": 0.001, "uv": [80, 23]}
]
},
{
"name": "finger_thumb_right",
"parent": "hand_right",
"pivot": [-19.6, 34.9, -8],
"rotation": [-12.5, 0, -25],
"name": "finger_right2",
"parent": "finger_right",
"pivot": [-41.1, 46.4, -3.5],
"cubes": [
{"origin": [-22.6, 29.9, -10.5], "size": [6, 9, 7], "uv": [0, 20]}
{"origin": [-43.1, 44.4, -4.5], "size": [2, 2, 2], "uv": [0, 67]}
]
},
{
"name": "finger_thumb_right2",
"parent": "finger_thumb_right",
"pivot": [-19.1, 29.9, -10.5],
"name": "finger_right3",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, -1.25],
"cubes": [
{"origin": [-22.1, 23.9, -10.5], "size": [5, 6, 5], "uv": [82, 68]}
{"origin": [-41.1, 44.4, -2.25], "size": [3, 2, 2], "inflate": 0.001, "uv": [80, 19]}
]
},
{
"name": "finger_index_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, -6],
"name": "finger_right4",
"parent": "finger_right3",
"pivot": [-41.1, 46.4, -1.25],
"cubes": [
{"origin": [-27.6, 21.9, -8], "size": [6, 6, 4], "inflate": 0.015, "uv": [112, 122]}
{"origin": [-44.1, 44.4, -2.25], "size": [3, 2, 2], "uv": [38, 72]}
]
},
{
"name": "finger_index_right2",
"parent": "finger_index_right",
"pivot": [-27.1, 21.9, -6],
"name": "finger_right5",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, 1],
"cubes": [
{"origin": [-27.1, 15.9, -8], "size": [5, 6, 4], "uv": [136, 96]}
{"origin": [-41.1, 44.4, 0], "size": [3, 2, 2], "inflate": 0.001, "uv": [59, 63]}
]
},
{
"name": "finger_middle_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, -2],
"name": "finger_right6",
"parent": "finger_right5",
"pivot": [-41.1, 46.4, 1],
"cubes": [
{"origin": [-27.6, 21.9, -4], "size": [6, 6, 4], "inflate": 0.01, "uv": [177, 29]}
{"origin": [-43.1, 44.4, 0], "size": [2, 2, 2], "uv": [0, 63]}
]
},
{
"name": "finger_middle_right2",
"parent": "finger_middle_right",
"pivot": [-27.1, 21.9, -2],
"name": "finger_right7",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, 3.25],
"cubes": [
{"origin": [-27.1, 15.9, -4], "size": [5, 6, 4], "uv": [144, 78]}
{"origin": [-40.1, 44.4, 2.25], "size": [2, 2, 2], "inflate": 0.001, "uv": [60, 4]}
]
},
{
"name": "finger_ring_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, 2],
"name": "finger_right8",
"parent": "finger_right7",
"pivot": [-40.1, 46.4, 3.25],
"cubes": [
{"origin": [-27.6, 21.9, 0], "size": [6, 6, 4], "inflate": 0.015, "uv": [152, 122]}
{"origin": [-42.1, 44.4, 2.25], "size": [2, 2, 2], "uv": [60, 0]}
]
},
{
"name": "finger_ring_right2",
"parent": "finger_ring_right",
"pivot": [-27.1, 21.9, 2],
"name": "finger_right9",
"parent": "hand_right2",
"pivot": [-35.6, 43.15, -3.5],
"cubes": [
{"origin": [-27.1, 15.9, 0], "size": [5, 6, 4], "uv": [144, 68]}
{"origin": [-38.1, 41.65, -5.5], "size": [3, 3, 3], "inflate": 0.001, "uv": [80, 32]}
]
},
{
"name": "finger_pinky_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, 6],
"name": "finger_right10",
"parent": "finger_right9",
"pivot": [-38.1, 43.15, -5.5],
"cubes": [
{"origin": [-27.6, 21.9, 4], "size": [6, 6, 4], "inflate": 0.01, "uv": [206, 96]}
{"origin": [-41.1, 41.65, -5.5], "size": [3, 3, 3], "uv": [54, 49]}
]
},
{
"name": "finger_pinky_right2",
"parent": "finger_pinky_right",
"pivot": [-27.1, 21.9, 6],
"cubes": [
{"origin": [-27.1, 16.9, 4], "size": [5, 5, 4], "uv": [84, 79]}
]
},
{
"name": "hand_fire_right",
"parent": "forearm_right",
"pivot": [-24, 40.25, 0]
},
{
"name": "seat_1_locator",
"parent": "torso",
"pivot": [0, 77, 9],
"locators": {
"seat_1": [0, 77, 10]
}
},
{
"name": "seat_2_locator",
"parent": "torso",
"pivot": [-14, 77, 13],
"locators": {
"seat_2": [-14, 77, 13]
}
},
{
"name": "seat_3_locator",
"parent": "torso",
"pivot": [14, 77, 13],
"locators": {
"seat_3": [14, 77, 13]
}
},
{
"name": "legs",
"parent": "shoulder_pivot",
"pivot": [0, 37, 0]
},
{
"name": "leg_left",
"parent": "legs",
"pivot": [9.5, 37, 0],
"parent": "body",
"pivot": [5, 20, 0],
"cubes": [
{"origin": [4, 27, -5.5], "size": [11, 11, 11], "uv": [188, 28]}
]
},
{
"name": "knee_left",
"parent": "leg_left",
"pivot": [9.5, 28, -0.25],
"cubes": [
{"origin": [6, 23, -3.5], "size": [7, 7, 7], "uv": [172, 50]}
{"origin": [2.5, 14, -2.5], "size": [5, 7, 5], "uv": [24, 105]}
]
},
{
"name": "leg_left2",
"parent": "knee_left",
"pivot": [9.5, 22.5, 0],
"parent": "leg_left",
"pivot": [5, 15, -0.25],
"cubes": [
{"origin": [3, 16, -6.5], "size": [13, 8, 13], "uv": [201, 56]},
{"origin": [0, 8, -8.5], "size": [19, 8, 17], "uv": [72, 166]}
{"origin": [3.5, 10, -2.25], "size": [3, 5, 4], "uv": [74, 92]}
]
},
{
"name": "foot_left",
"parent": "leg_left2",
"pivot": [9.5, 8, -2],
"pivot": [5, 9.5, 0],
"cubes": [
{"origin": [1, 0, -9.5], "size": [17, 8, 18], "uv": [154, 96]},
{"origin": [1, 0, -14.5], "size": [17, 6, 5], "uv": [208, 85]}
{"origin": [1.5, 7, -3.5], "size": [7, 4, 7], "uv": [98, 0]},
{"origin": [0.5, 2, -4.5], "size": [9, 5, 9], "uv": [86, 70]},
{"origin": [1, 0, -7.5], "size": [8, 2, 11], "uv": [80, 19]}
]
},
{
"name": "leg_right",
"parent": "legs",
"pivot": [-9.5, 37, 0],
"parent": "body",
"pivot": [-5, 20, 0],
"cubes": [
{"origin": [-15, 27, -5.5], "size": [11, 11, 11], "uv": [144, 28]}
]
},
{
"name": "knee_right",
"parent": "leg_right",
"pivot": [-9.5, 28, -0.25],
"cubes": [
{"origin": [-13, 23, -3.5], "size": [7, 7, 7], "uv": [144, 50]}
{"origin": [-7.5, 14, -2.5], "size": [5, 7, 5], "uv": [0, 0]}
]
},
{
"name": "leg_right2",
"parent": "knee_right",
"pivot": [-9.5, 22.5, 0],
"parent": "leg_right",
"pivot": [-5, 15, -0.25],
"cubes": [
{"origin": [-16, 16, -6.5], "size": [13, 8, 13], "uv": [162, 64]},
{"origin": [-19, 8, -8.5], "size": [19, 8, 17], "uv": [0, 166]}
{"origin": [-6.5, 10, -2.25], "size": [3, 5, 4], "uv": [54, 40]}
]
},
{
"name": "foot_right",
"parent": "leg_right2",
"pivot": [-9.5, 8, -2],
"pivot": [-5, 10.5, 0],
"cubes": [
{"origin": [-18, 0, -9.5], "size": [17, 8, 18], "uv": [84, 96]},
{"origin": [-18, 0, -14.5], "size": [17, 6, 5], "uv": [164, 85]}
{"origin": [-8.5, 7, -3.5], "size": [7, 4, 7], "uv": [82, 57]},
{"origin": [-9.5, 2, -4.5], "size": [9, 5, 9], "uv": [0, 82]},
{"origin": [-9, 0, -7.5], "size": [8, 2, 11], "uv": [59, 79]}
]
}
]
@@ -18,32 +18,19 @@
{
"name": "body",
"parent": "cyclizar",
"pivot": [0, 11, 3.5]
"pivot": [0, 0, 0]
},
{
"name": "hips",
"parent": "body",
"pivot": [0, 13.4, 5.75]
},
{
"name": "torso",
"parent": "hips",
"pivot": [0, 13.4, 5.75],
"cubes": [
{"origin": [-3, 10.75, -4], "size": [6, 7, 11], "inflate": 0.01, "uv": [40, 0]}
]
},
{
"name": "seat_1_locator",
"parent": "torso",
"pivot": [0, 17.75, 2],
"locators": {
"seat_1": [0, 17.75, 2]
}
},
{
"name": "seat",
"parent": "torso",
"parent": "hips",
"pivot": [0, 17.75, 4.5],
"rotation": [22.5, 0, 0],
"cubes": [
@@ -53,8 +40,8 @@
]
},
{
"name": "torso2",
"parent": "torso",
"name": "torso",
"parent": "hips",
"pivot": [0, 14, -3.05],
"cubes": [
{"origin": [-3.5, 10.75, -10.5], "size": [7, 7, 8], "uv": [40, 54]}
@@ -62,7 +49,7 @@
},
{
"name": "neck",
"parent": "torso2",
"parent": "torso",
"pivot": [0, 14, -9.35],
"rotation": [82.5, 0, 0],
"cubes": [
@@ -247,8 +234,184 @@
]
},
{
"name": "tail",
"name": "arm_left",
"parent": "torso",
"pivot": [3.25, 13.05, -4.75],
"cubes": [
{"origin": [1.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [13, 116]}
]
},
{
"name": "elbow_left",
"parent": "arm_left",
"pivot": [3.25, 7.05, -4.75],
"cubes": [
{"origin": [1.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [44, 81]}
]
},
{
"name": "hand_left",
"parent": "elbow_left",
"pivot": [3.25, 0.55, -5],
"cubes": [
{"origin": [1.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [74, 98]}
]
},
{
"name": "fingers_left",
"parent": "hand_left",
"pivot": [3.25, 0.05, -7.5]
},
{
"name": "finger_left_left2",
"parent": "fingers_left",
"pivot": [2, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [1, -0.95, -9.25], "size": [1, 2, 3], "uv": [54, 107]}
]
},
{
"name": "finger_left_left3",
"parent": "finger_left_left2",
"pivot": [1.5, 1.05, -9.25],
"cubes": [
{"origin": [1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [36, 112]}
]
},
{
"name": "middle_fingers_left",
"parent": "fingers_left",
"pivot": [3.25, 0.3, -7],
"cubes": [
{"origin": [2.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 33]}
]
},
{
"name": "middle_fingers_left2",
"parent": "middle_fingers_left",
"pivot": [3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [2.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [18, 111]}
]
},
{
"name": "middle_fingers_left3",
"parent": "middle_fingers_left2",
"pivot": [3.25, 1.05, -10.5],
"cubes": [
{"origin": [3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [40, 112]},
{"origin": [2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [112, 41]}
]
},
{
"name": "finger_left_left",
"parent": "fingers_left",
"pivot": [4.5, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [4.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [62, 107]}
]
},
{
"name": "finger_right_left4",
"parent": "finger_left_left",
"pivot": [5, 1.05, -9.25],
"cubes": [
{"origin": [5, -0.95, -11.25], "size": [0, 2, 2], "uv": [44, 112]}
]
},
{
"name": "arm_right",
"parent": "torso",
"pivot": [-3.25, 13.05, -4.75],
"cubes": [
{"origin": [-4.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [29, 116]}
]
},
{
"name": "elbow_right",
"parent": "arm_right",
"pivot": [-3.25, 7.05, -4.75],
"cubes": [
{"origin": [-4.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [0, 101]}
]
},
{
"name": "hand_right",
"parent": "elbow_right",
"pivot": [-3.25, 0.55, -5],
"cubes": [
{"origin": [-5.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [100, 22]}
]
},
{
"name": "fingers_right",
"parent": "hand_right",
"pivot": [-3.25, 0.05, -7.5]
},
{
"name": "finger_left_right2",
"parent": "fingers_right",
"pivot": [-2, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [-2, -0.95, -9.25], "size": [1, 2, 3], "uv": [106, 106]}
]
},
{
"name": "finger_left_right3",
"parent": "finger_left_right2",
"pivot": [-1.5, 1.05, -9.25],
"cubes": [
{"origin": [-1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [119, 93]}
]
},
{
"name": "middle_fingers_right",
"parent": "fingers_right",
"pivot": [-3.25, 0.3, -7],
"cubes": [
{"origin": [-4.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 29]}
]
},
{
"name": "middle_fingers_right2",
"parent": "middle_fingers_right",
"pivot": [-3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [-4.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [106, 66]}
]
},
{
"name": "middle_fingers_right3",
"parent": "middle_fingers_right2",
"pivot": [-3.25, 1.05, -10.5],
"cubes": [
{"origin": [-3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [28, 112]},
{"origin": [-2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [123, 93]}
]
},
{
"name": "finger_right_left",
"parent": "fingers_right",
"pivot": [-4.5, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [-5.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [28, 107]}
]
},
{
"name": "finger_right_left2",
"parent": "finger_right_left",
"pivot": [-5, 1.05, -9.25],
"cubes": [
{"origin": [-5, -0.95, -11.25], "size": [0, 2, 2], "uv": [32, 112]}
]
},
{
"name": "tail",
"parent": "hips",
"pivot": [0, 14.5, 6.75],
"rotation": [17.5, 0, 0],
"cubes": [
@@ -297,194 +460,13 @@
},
{
"name": "tail_wheel",
"parent": "torso",
"parent": "hips",
"pivot": [0, 14.5, 5.75],
"rotation": [7.5, 0, 0],
"cubes": [
{"origin": [-2, 1.5, 1.75], "size": [4, 16, 16], "inflate": 0.02, "uv": [0, 0]}
]
},
{
"name": "ftorso2",
"parent": "hips",
"pivot": [0, 14, -3.05]
},
{
"name": "arm_left",
"parent": "ftorso2",
"pivot": [3.25, 13.05, -4.75],
"cubes": [
{"origin": [1.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [13, 116]}
]
},
{
"name": "elbow_left",
"parent": "arm_left",
"pivot": [3.25, 7.05, -4.75],
"cubes": [
{"origin": [1.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [44, 81]}
]
},
{
"name": "hand_left",
"parent": "elbow_left",
"pivot": [3.25, 0.55, -5],
"cubes": [
{"origin": [1.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [74, 98]}
]
},
{
"name": "fingers_left",
"parent": "hand_left",
"pivot": [3.25, 0.05, -7.5]
},
{
"name": "finger_left_right",
"parent": "fingers_left",
"pivot": [2, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [1, -0.95, -9.25], "size": [1, 2, 3], "uv": [54, 107]}
]
},
{
"name": "finger_left_right2",
"parent": "finger_left_right",
"pivot": [1.5, 1.05, -9.25],
"cubes": [
{"origin": [1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [36, 112]}
]
},
{
"name": "middle_fingers_left",
"parent": "fingers_left",
"pivot": [3.25, 0.3, -7],
"cubes": [
{"origin": [2.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 33]}
]
},
{
"name": "middle_fingers_left2",
"parent": "middle_fingers_left",
"pivot": [3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [2.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [18, 111]}
]
},
{
"name": "middle_fingers_left3",
"parent": "middle_fingers_left2",
"pivot": [3.25, 1.05, -10.5],
"cubes": [
{"origin": [3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [40, 112]},
{"origin": [2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [112, 41]}
]
},
{
"name": "finger_left_left",
"parent": "fingers_left",
"pivot": [4.5, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [4.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [62, 107]}
]
},
{
"name": "finger_left_left2",
"parent": "finger_left_left",
"pivot": [5, 1.05, -9.25],
"cubes": [
{"origin": [5, -0.95, -11.25], "size": [0, 2, 2], "uv": [44, 112]}
]
},
{
"name": "arm_right",
"parent": "ftorso2",
"pivot": [-3.25, 13.05, -4.75],
"cubes": [
{"origin": [-4.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [29, 116]}
]
},
{
"name": "elbow_right",
"parent": "arm_right",
"pivot": [-3.25, 7.05, -4.75],
"cubes": [
{"origin": [-4.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [0, 101]}
]
},
{
"name": "hand_right",
"parent": "elbow_right",
"pivot": [-3.25, 0.55, -5],
"cubes": [
{"origin": [-5.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [100, 22]}
]
},
{
"name": "fingers_right",
"parent": "hand_right",
"pivot": [-3.25, 0.05, -7.5]
},
{
"name": "finger_right_left",
"parent": "fingers_right",
"pivot": [-2, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [-2, -0.95, -9.25], "size": [1, 2, 3], "uv": [106, 106]}
]
},
{
"name": "finger_right_left2",
"parent": "finger_right_left",
"pivot": [-1.5, 1.05, -9.25],
"cubes": [
{"origin": [-1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [119, 93]}
]
},
{
"name": "middle_fingers_right",
"parent": "fingers_right",
"pivot": [-3.25, 0.3, -7],
"cubes": [
{"origin": [-4.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 29]}
]
},
{
"name": "middle_fingers_right2",
"parent": "middle_fingers_right",
"pivot": [-3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [-4.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [106, 66]}
]
},
{
"name": "middle_fingers_right3",
"parent": "middle_fingers_right2",
"pivot": [-3.25, 1.05, -10.5],
"cubes": [
{"origin": [-3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [28, 112]},
{"origin": [-2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [123, 93]}
]
},
{
"name": "finger_right_right",
"parent": "fingers_right",
"pivot": [-4.5, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [-5.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [28, 107]}
]
},
{
"name": "finger_right_right2",
"parent": "finger_right_right",
"pivot": [-5, 1.05, -9.25],
"cubes": [
{"origin": [-5, -0.95, -11.25], "size": [0, 2, 2], "uv": [32, 112]}
]
},
{
"name": "leg_left_rotate",
"parent": "body",
@@ -34,7 +34,7 @@
"namedAnimations": {
"faint": "q.bedrock_primary('magikarp', 'water_faint', q.curve('one'))"
},
"animations": ["q.pitch_tilt('body', 0.22)", "q.look('head')", "q.bedrock('magikarp', 'water_idle')"],
"animations": ["q.look('head')", "q.bedrock('magikarp', 'water_idle')"],
"quirks": ["q.bedrock_quirk('magikarp', 'blink')"]
},
"swim": {
@@ -42,7 +42,7 @@
"namedAnimations": {
"faint": "q.bedrock_primary('magikarp', 'water_faint', q.curve('one'))"
},
"animations": ["q.pitch_tilt('body', 0.22)", "q.look('head')", "q.bedrock('magikarp', 'water_swim')"],
"animations": ["q.look('head')", "q.bedrock('magikarp', 'water_swim')"],
"quirks": ["q.bedrock_quirk('magikarp', 'blink')"]
},
"sleep": {
@@ -1,93 +0,0 @@
{
"portraitScale": 1.66,
"portraitTranslation": [-0.14, -0.74, 0],
"profileScale": 1,
"profileTranslation": [0.0, 0.24, 0.0],
"rootBone": "golurk",
"firstPersonCameraOffset": {
"seat_1": [0, 0.25, 0]
},
"thirdPersonCameraOffset": {
"seat_1": [0, 5, 8],
"seat_1_reverse": [0, 3, 8]
},
"animations": {
"faint": "q.bedrock_primary('golurk', 'faint', q.curve('one'))",
"cry": "q.bedrock_stateful('golurk', 'cry')",
"recoil": "q.bedrock_stateful('golurk', 'recoil')"
},
"poses": {
"battle-standing": {
"poseTypes": ["STAND"],
"isBattle": true,
"animations": ["q.look('head')", "q.bedrock('golurk', 'battle_idle')"]
},
"standing": {
"poseTypes": ["STAND", "NONE", "PORTRAIT", "PROFILE"],
"isBattle": false,
"animations": ["q.look('head')", "q.bedrock('golurk', 'ground_idle')"]
},
"walk": {
"poseName": "walk",
"poseTypes": ["WALK"],
"isBattle": false,
"condition": "!q.in_air && !q.is_sprinting",
"animations": [
"q.look('head')",
"q.bedrock('golurk', 'ground_walk')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_ground_run')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"run": {
"poseName": "run",
"poseTypes": ["WALK"],
"isBattle": false,
"condition": "!q.in_air && q.is_sprinting",
"animations": [
"q.look('head')",
"q.bedrock('golurk', 'ground_run')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_ground_run')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"hover": {
"poseTypes": ["HOVER"],
"isBattle": false,
"animations": [
"q.look('head')", "q.bedrock('golurk', 'air_idle')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_air_idle')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"fly": {
"poseTypes": ["FLY"],
"isBattle": false,
"animations": [
"q.look('head')", "q.bedrock('golurk', 'air_fly')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_air_fly')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"sleep": {
"poseTypes": ["SLEEP"],
"animations": ["q.bedrock('golurk', 'sleep')"]
}
}
}
@@ -1,106 +1,41 @@
{
"portraitScale": 1.5,
"portraitTranslation": [0, 0.5, 0],
"profileScale": 0.5,
"profileTranslation": [0, 0.25, -5],
"rootBone": "cyclizar",
"firstPersonCameraOffset": {
"seat_1": [0, 0.25, 0]
},
"thirdPersonCameraOffset": {
"seat_1": [0, 2.5, 5],
"seat_1_reverse": [0, 2.5, 5]
},
"portraitScale": 1.78,
"portraitTranslation": [-0.95,0.48,0],
"profileScale": 0.76,
"profileTranslation": [0,0.54,0],
"animations": {
"cry": "q.bedrock_stateful('cyclizar', 'cry')"
},
"poses": {
"portrait": {
"poseTypes": ["PORTRAIT", "PROFILE"],
"isBattle": false,
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
},
"ride_jump": {
"poseTypes": ["WALK","STAND", "FLOAT", "NONE"],
"isBattle": false,
"condition": "q.in_air && q.is_ridden",
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ride_jump')"],
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"standing": {
"poseTypes": ["STAND", "NONE"],
"isBattle": false,
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_idle')"],
"condition": "!q.is_ridden",
"quirks": [
"q.bedrock_quirk('cyclizar', 'blink')"
]
},
"walking": {
"poseTypes": ["WALK", "SWIM"],
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_walk')"],
"condition": "!q.is_ridden",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
},
"mount_idle": {
"poseTypes": ["STAND"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_drift')"
],
"condition": "q.is_ridden && !q.in_air",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
},
"mount_walk": {
"poseTypes": ["WALK"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_ground_run')",
"q.bedrock('cyclizar', 'ground_run')"
],
"condition": "q.is_ridden && !q.is_sprinting && !q.in_air && !q.is_drifting && !q.is_powered_drifting",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"mount_drift": {
"poseTypes": ["WALK"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_ground_run')",
"q.bedrock('cyclizar', 'ride_drift')"
],
"condition": "q.is_ridden && !q.is_sprinting && !q.in_air && q.is_drifting && !q.is_powered_drifting",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"mount_powered_drift": {
"poseTypes": ["WALK"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_ground_run')",
"q.bedrock('cyclizar', 'ride_drift')",
"q.bedrock('cyclizar', 'ride_drift_legs')",
"q.bedrock('cyclizar', 'ride_drift_override')"
],
"condition": "q.is_ridden && !q.is_sprinting && !q.in_air && !q.is_drifting && q.is_powered_drifting",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"mount_run": {
"poseTypes": ["WALK"],
"isBattle": false,
"condition": "!q.in_air && q.is_sprinting",
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_run')"],
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
"poses": {
"standing": {
"poseTypes": [
"STAND",
"NONE",
"PORTRAIT",
"PROFILE"
],
"isBattle": false,
"animations": [
"q.look('head', 1, 1, 25, -45, 45, -45)",
"q.bedrock('cyclizar', 'grounded')"
],
"quirks": [
"q.bedrock_quirk('cyclizar', 'blink')"
]
},
"walking": {
"poseTypes": [
"WALK",
"SWIM"
],
"animations": [
"q.look('head', 1, 1, 25, -45, 45, -45)",
"q.bedrock('cyclizar', 'grounded')",
"q.quadruped_walk(0.66, 0.6, 'arm_left', 'arm_right', 'leg_left_rotate', 'leg_right_rotate')"
],
"quirks": [
"q.bedrock_quirk('cyclizar', 'blink')"
]
}
}
}
}
}
@@ -6,54 +6,22 @@
"aspects": [],
"poser": "cobblemon:golurk",
"model": "cobblemon:golurk.geo",
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk.png",
"layers": [
{
"name": "color",
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_emissive_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_emissive_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_emissive_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk_emissive.png",
"emissive": true
}
]
},
{
"aspects": ["shiny"],
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk_shiny.png",
"layers": [
{
"name": "color",
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive.png",
"emissive": true
}
]
@@ -2340,7 +2340,6 @@
"cobblemon.command.takemark": "%1$s was taken from %2$s's %3$s.",
"cobblemon.command.takemark.does_not_have": "%1$s's %2$s does not have a %3$s!",
"cobblemon.command.mark.invalid": "Invalid mark name.",
"cobblemon.command.molang.out": "MoLang output: %1$s",
"cobblemon.command.pokeheal.in_battle": "You cannot heal your party while in a battle!",
"cobblemon.command.spawnpokemonfrompool": "A %1$s has spawned in the area.",
"cobblemon.command.spawnpokemonfrompool.unable_to_spawn": "Unable to spawn a Pokémon.",
@@ -2383,6 +2382,7 @@
"cobblemon.config.ui.category.interface": "Interface",
"cobblemon.config.ui.category.riding": "Riding",
"cobblemon.config.ui.category.debug": "Debug",
"cobblemon.config.ui.category.system": "System",
"cobblemon.config.ui.max_pokemon_level": "Max Pokémon Level",
"cobblemon.config.ui.max_pokemon_level.tooltip": "The maximum level Pokémon can get to.",
@@ -2565,6 +2565,8 @@
"cobblemon.config.ui.despawner_max_age_ticks.tooltip": "The age (in game ticks) a Pokémon must reach before it can be despawned at the near distance boundary.",
"cobblemon.config.ui.enable_debug_keys": "Enable Debug Keys",
"cobblemon.config.ui.enable_debug_keys.tooltip": "Enables additional commands and chat logging to assist with implementing Pokémon (see the wiki for a list of commands).",
"cobblemon.config.ui.time_zone_id": "Time Zone Id",
"cobblemon.config.ui.time_zone_id.tooltip": "Time Zone to use for some MoLangs and IRL time zone dates. Server Side only.",
"cobblemon.config.ui.allow_starter_on_join": "Allow Starter on Join",
"cobblemon.config.ui.prompt_starter_once_only": "Prompt Starter Once Only",
@@ -1175,14 +1175,6 @@
"sounds": [
{
"name": "cobblemon:ride/loop/saucer_flight_loop",
"attenuation_distance": 64.0
}
]
},
"ride.loop.rocket": {
"sounds": [
{
"name": "cobblemon:ride/loop/rocket_flight_loop",
"attenuation_distance": 48.0
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

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