Compare commits

...
Author SHA1 Message Date
JacksonB 679920b786 Rolling is now displayed on remote clients while riding. 2025-03-31 23:15:44 -05:00
JacksonB c8ec4e6f1f commit to not lose anything because paranoid the computer explodes 2025-03-31 00:20:21 -05:00
Landon Wills cbcc6abe40 Fix issue with poses not changing properly in multiplayer riding 2025-03-30 17:04:36 -04:00
Landon Wills 64c9d47b0f Fix multiplayer issue where pokemon other player's were riding would not rotate 2025-03-29 21:10:42 -04:00
Landon Wills c0d2803f61 Fix multiplayer issues with clients not recognizing ridden pokemon on login 2025-03-29 15:55:34 -04:00
Landon Wills e249d5238f Fix issue with orientation not getting reset on ride dismount 2025-03-29 02:34:00 -04:00
Landon Wills e32e505e95 Fix issue with sprint not getting recognized on RunUpToJetFlightCompositeController 2025-03-29 02:14:40 -04:00
Landon 7642a84108 Merge branch 'ride-controller-server-crash-fix' into 'multiplayer-riding'
Ride controller server crash fix

See merge request cable-mc/cobblemon!1575
2025-03-29 04:58:25 +00:00
JacksonB b6e20d69cc fix to crouch but sprint during riding is still broken 2025-03-28 15:18:10 -05:00
JacksonB 9d04e20a30 Remove client only code causing server crash and replace with proper code. 2025-03-28 14:51:33 -05:00
Landon Wills d2624813f7 Merge branch 'refs/heads/bug-fixes' into multiplayer-riding 2025-03-28 13:36:22 -04:00
Landon Wills f6f607904e Merge branch 'refs/heads/bug-fixes' into multiplayer-riding 2025-03-28 01:53:19 -04:00
Landon Wills 9284497cec Refactor Rollable interface, fix issue with mixins crossing sides, causing server to fail to load 2025-03-28 00:21:26 -04:00
Landon Wills 36cb12952a Move more functions out of Rollable to mouse handling 2025-03-27 21:55:29 -04:00
Landon Wills 2700dc7f39 Refactor function to get angular velocity out of Rollable 2025-03-27 21:23:12 -04:00
41 changed files with 799 additions and 615 deletions
@@ -0,0 +1,24 @@
/*
* 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;
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,143 +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;
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 Rollable {
// From Minecraft's Camera
Vector3f FORWARDS = new Vector3f(0.0F, 0.0F, -1.0F);
Vector3f UP = new Vector3f(0.0F, 1.0F, 0.0F);
Vector3f LEFT = new Vector3f(-1.0F, 0.0F, 0.0F);
@Nullable Matrix3f getOrientation();
Rollable updateOrientation(Function<Matrix3f, Matrix3f> update);
/**
* @return The forward vector (0, 0, -1) after the rotation matrix is applied.
*/
default Vector3f getForwardVector() {
if (getOrientation() == null) return new Vector3f(FORWARDS);
return getOrientation().transform(FORWARDS, new Vector3f());
}
/**
* @return The left vector (-1, 0, 0) after the rotation matrix is applied.
*/
default Vector3f getLeftVector() {
if (getOrientation() == null) return new Vector3f(LEFT);
return getOrientation().transform(LEFT, new Vector3f());
}
/**
* @return The up vector (0, 1, 0) after the rotation matrix is applied.
*/
default Vector3f getUpVector() {
if (getOrientation() == null) return new Vector3f(UP);
return getOrientation().transform(UP, new Vector3f());
}
/**
*
* @param yaw change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateYaw(float yaw) {
return updateOrientation(prev -> prev.rotateY((float) Math.toRadians(-yaw)));
}
/**
*
* @param pitch change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotatePitch(float pitch) {
return updateOrientation(prev -> prev.rotateX((float) Math.toRadians(-pitch)));
}
/**
*
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateRoll(float roll) {
return updateOrientation(prev -> prev.rotateZ((float) Math.toRadians(-roll)));
}
/**
* Order matters when rotating an object. If you need a different order, individually call rotateYaw, rotatePitch, and rotateRoll
*
* @param yaw change in degrees
* @param pitch change in degrees
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotate(float yaw, float pitch, float roll) {
return rotateYaw(yaw).rotatePitch(pitch).rotateRoll(roll);
}
/**
* @return The pitch angle in degrees as a float.
* Value is between [-180, 180] with 0 being South, 90 being West, 180/-180 being North, and -90 being East
*/
default float getYaw() {
return (float) Mth.wrapDegrees(-Math.toDegrees(FORWARDS.angleSigned(getForwardVector(), UP)) + 180);
}
/**
* @return The yaw angle in degrees as a float.
* Value is between [-90, 90] with 0 being straight forward, 90 being straight down, and -90 being straight up
*/
default float getPitch() {
return (float) Mth.wrapDegrees(-Math.toDegrees(Math.asin(getForwardVector().y)));
}
/**
* @return The roll angle in degrees as a float.
* Value is between [-180, 180] with 0 being no roll (normal), 90 being 90 degrees CW, 180/-180 being 180 degrees CW/CCW, and -90 being 90 degrees CCW
*/
default float getRoll() {
//Cobblemon.LOGGER.info("{}", (float) Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
return (float) Mth.wrapDegrees(-Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
}
/**
* Get the angular velocity in degrees per second as a Vec3
*/
Vec3 angRollVel( double deltaTime );
public Vec3 rotationOnMouseXY(
double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime
);
boolean shouldRoll();
boolean useAngVelSmoothing();
void clearRotation();
}
@@ -10,9 +10,10 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonItems;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.events.CobblemonEvents;
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.api.storage.party.PartyStore;
@@ -31,14 +32,11 @@ import com.cobblemon.mod.common.util.DataKeys;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@@ -46,18 +44,14 @@ import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -65,11 +59,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements Rollable, ScannableEntity {
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable {
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@@ -87,7 +80,7 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Shadow public abstract void displayClientMessage(Component message, boolean overlay);
@Unique private Matrix3f orientation = null;
@Unique private OrientationController cobblemon$orientationController = new OrientationController(this);
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
super(p_20966_, p_20967_);
@@ -241,7 +234,7 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (shouldRoll()) {
if (cobblemon$orientationController.getActive()) {
this.absMoveTo(x, y, z);
this.setYRot(yaw % 360.0f);
this.setXRot(pitch % 360.0f);
@@ -254,111 +247,6 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
}
}
@Inject(method = "tick", at = @At("HEAD"))
public void tick(CallbackInfo ci) {
if (this.shouldRoll()) {
// Proof of concept for yaw movement
if (this.getMainHandItem().is(Items.STICK)) {
this.rotateYaw(2);
}
else if (this.getMainHandItem().is(Items.BLAZE_ROD)) {
this.rotateYaw(-2);
}
}
}
@Override
public Matrix3f getOrientation() {
return orientation;
}
@Override
public Rollable updateOrientation(Function<Matrix3f, Matrix3f> update) {
//Disabled to allow non mouse inputs to modify roll
if (!shouldRoll()) return this;
if (this.orientation == null) {
this.orientation = new Matrix3f();
this.rotate(getYRot() - 180, getXRot(), 0);
}
this.orientation = update.apply(this.orientation);
setYRot(this.getYaw());
setXRot(this.getPitch());
return this;
}
@Override
public boolean shouldRoll() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return false;
if (player.isHolding(Items.DIAMOND)) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Override
public Vec3 rotationOnMouseXY(
double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime
) {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return Vec3.ZERO;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return Vec3.ZERO;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
return pokemonEntity.getRiding().rotationOnMouseXY(
pokemonEntity,
player,
yMouse,
xMouse,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime
);
}
@Override
public boolean useAngVelSmoothing() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return true;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
return pokemonEntity.getRiding().useAngVelSmoothing(pokemonEntity);
}
@Override
public Vec3 angRollVel( double deltaTime ) {
var ret = new Vec3(0.0, 0.0, 0.0);
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return ret;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return ret;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return ret;
return pokemonEntity.getRiding().angRollVel( pokemonEntity, player, deltaTime );
}
@Override
public void clearRotation() {
this.orientation = null;
}
@WrapOperation(
method = "rideTick()V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;wantsToStopRiding()Z")
@@ -370,4 +258,8 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
return original.call(instance);
}
@Override
public OrientationController getOrientationController() {
return cobblemon$orientationController;
}
}
@@ -0,0 +1,103 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
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.S2CUpdateOrientationPacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.network.protocol.Packet;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Set;
import java.util.function.Consumer;
@Mixin(ServerEntity.class)
public abstract class ServerEntityMixin {
@Shadow @Final private Consumer<Packet<?>> broadcast;
@Shadow @Final private Entity entity;
@Unique
private Matrix3f cobblemon$lastSentOrientation;
private boolean cobblemon$lastSentActive;
@Inject(method = "sendChanges", at = @At("TAIL"))
private void cobblemon$sendOrientationChanges(CallbackInfo ci) {
if (!(this.entity instanceof OrientationControllable controllable)) return;
OrientationController controller = controllable.getOrientationController();
Matrix3f currOrientation = controller.getOrientation();
boolean currActive = controller.getActive();
if (currOrientation == null || currOrientation.equals(cobblemon$lastSentOrientation) && (currActive == cobblemon$lastSentActive)) return;
cobblemon$lastSentOrientation = new Matrix3f(currOrientation);
cobblemon$lastSentActive = currActive;
if (!(entity.level() instanceof ServerLevel level)) return;
ChunkMap chunkMap = level.getChunkSource().chunkMap;
Int2ObjectMap<?> entityMap = ((ChunkMapAccessor) chunkMap).getEntityMap();
Object tracked = entityMap.get(entity.getId());
if (!(tracked instanceof TrackedEntityAccessor tracker)) return;
Set<ServerPlayerConnection> seenBy = tracker.getSeenBy();
for (ServerPlayerConnection conn : seenBy) {
ServerPlayer player = conn.getPlayer();
if (player == entity) continue;
CobblemonNetwork.INSTANCE.sendPacketToPlayer(player, new S2CUpdateOrientationPacket(currOrientation, currActive, entity.getId()));
}
/*
OrientationController controller = controllable.getOrientationController();
Matrix3f currOrientation = controller.getOrientation();
if(currOrientation != cobblemon$lastSentOrientation) {
cobblemon$lastSentOrientation = new Matrix3f(currOrientation);
//this.broadcast.accept(new S2CUpdateOrientationPacket(currOrientation,this.entity.getId()))
if (!(entity.level() instanceof ServerLevel level)) return;
ChunkMap chunkMap = level.getChunkSource().chunkMap;
Long2ObjectMap<ChunkMap.TrackedEntity> entityMap = ((ChunkMapAccessor) chunkMap).getEntityMap();
TrackedEntity trackedEntity = entityMap.get(entity.getId());
if (trackedEntity == null) return;
Set<ServerPlayerConnection> seenBy = ((TrackedEntityAccessor) trackedEntity).getSeenBy();
for (ServerPlayerConnection conn : seenBy) {
ServerPlayer player = conn.getPlayer();
if (player == entity) continue; // optional safety filter
CobblemonNetwork.sendPacketToPlayer(player, new S2CUpdateOrientationPacket(orientation, entity.getId()));
}
*/
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.server.level.ServerPlayer;
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(ServerPlayer.class)
public abstract class ServerPlayerMixin {
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((ServerPlayer)(Object)this);
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(ServerPlayer player) {
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Inject(method = "stopRiding", at = @At("HEAD"))
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.server.level.ChunkMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ChunkMap.class)
public interface ChunkMapAccessor {
@Accessor("entityMap")
Int2ObjectMap<?> getEntityMap();
}
@@ -0,0 +1,21 @@
/*
* 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.accessor;
import net.minecraft.server.network.ServerPlayerConnection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Set;
@Mixin(targets = "net.minecraft.server.level.ChunkMap$TrackedEntity")
public interface TrackedEntityAccessor {
@Accessor("seenBy")
Set<ServerPlayerConnection> getSeenBy();
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
@@ -35,10 +35,6 @@ public abstract class CameraMixin {
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow public abstract float getXRot();
@Shadow public abstract float getYRot();
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@@ -47,51 +43,53 @@ public abstract class CameraMixin {
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void open_camera$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof Rollable rollable) || disableRollableCameraDebug) return;
if (!rollable.shouldRoll() && rollable.getOrientation() != null) {
if (!(this.entity instanceof OrientationControllable controllable) || disableRollableCameraDebug) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() && controller.getOrientation() != null) {
if(this.returnTimer < 1) {
//Rotation is taken from entity since we no longer handle mouse ourselves
//Stops a period of time when you can't input anything.
rollable.getOrientation().set(
controller.getOrientation().set(
new Matrix3f()
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
);
if(rollAngleStart == 0){
this.returnTimer = 1;
rollable.clearRotation();
controller.reset();
return;
}
rollable.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
this.returnTimer = 1;
rollable.clearRotation();
controller.reset();
}
return;
}
if (rollable.getOrientation() == null) return;
if (controller.getOrientation() == null) return;
applyRotation();
this.returnTimer = 0;
this.rollAngleStart = rollable.getRoll();
this.rollAngleStart = controller.getRoll();
ci.cancel();
}
@Unique
private void applyRotation(){
if (!(this.entity instanceof Rollable rollable) || rollable.getOrientation() == null) return;
var newRotation = rollable.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null) return;
var controller = controllable.getOrientationController();
var newRotation = controller.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (this.minecraft.options.getCameraType().isMirrored()) {
newRotation.rotateY((float)Math.toRadians(180));
}
this.rotation.set(newRotation);
this.xRot = rollable.getPitch();
this.yRot = rollable.getYaw();
this.forwards.set(rollable.getForwardVector());
this.up.set(rollable.getUpVector());
this.left.set(rollable.getLeftVector());
this.xRot = controller.getPitch();
this.yRot = controller.getYaw();
this.forwards.set(controller.getForwardVector());
this.up.set(controller.getUpVector());
this.left.set(controller.getLeftVector());
}
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
@@ -31,14 +31,14 @@ public class ItemInHandRendererMixin {
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
ci.cancel();
}
else if (abstractClientPlayer instanceof Rollable rollable && rollable.shouldRoll()) {
else if (abstractClientPlayer instanceof OrientationControllable controllable && controllable.getOrientationController().isActive()) {
}
}
@Redirect(method = "renderHandsWithItems", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V", ordinal = 1))
private void cobblemon$renderHandswithItems(PoseStack instance, Quaternionf quaternion) {
if (!(Minecraft.getInstance().player instanceof Rollable rollable && rollable.shouldRoll())) {
if (!(Minecraft.getInstance().player instanceof OrientationControllable controllable && controllable.getOrientationController().isActive())) {
instance.mulPose(quaternion);
}
}
@@ -9,9 +9,15 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket;
import net.minecraft.client.Minecraft;
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.level.Level;
import org.joml.Matrix3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -20,24 +26,53 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LocalPlayer.class)
public class LocalPlayerMixin {
public abstract class LocalPlayerMixin extends LivingEntity {
@Unique Matrix3f cobblemon$lastOrientation;
protected LocalPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
}
@Inject(method = "sendPosition", at = @At("TAIL"))
private void cobblemon_updateRotationMatrix(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
private void cobblemon$updateRotationMatrix(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(controller.getOrientation()));
}
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
private void cobblemon_updateRotationMatrixPassenger(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
private void cobblemon$updateRotationMatrixPassenger(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(controller.getOrientation()));
}
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this instanceof OrientationControllable controllable)) return;
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((LocalPlayer)(Object)this);
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Override
public void stopRiding() {
super.stopRiding();
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
}
}
@@ -8,13 +8,13 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.Blaze3D;
import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler;
import net.minecraft.client.player.LocalPlayer;
@@ -49,8 +49,6 @@ public class MouseHandlerMixin {
@Shadow private double accumulatedDY;
@Shadow private double accumulatedScrollX;
@Inject(
method = "onScroll",
at = @At(
@@ -121,9 +119,9 @@ public class MouseHandlerMixin {
return false;
}
if (!(player instanceof Rollable rollable)) return true;
if (!(player instanceof OrientationControllable controllable)) return true;
if (!rollable.shouldRoll()) {
if (!controllable.getOrientationController().isActive()) {
xMouseSmoother.reset();
yMouseSmoother.reset();
pitchSmoother.reset();
@@ -132,68 +130,100 @@ public class MouseHandlerMixin {
return true;
}
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
var ridingSensitivity = Math.pow(defaultSensitivity, 3);
//Send mouse input to be interpreted into rotation
//deltas by the ride controller
Vec3 angVecMouse = rollable.rotationOnMouseXY(
cursorDeltaY,
cursorDeltaX ,
yMouseSmoother,
xMouseSmoother,
ridingSensitivity,
movementTime
);
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
//Perform Rotation using mouse influenced rotation deltas.
rollable.rotate(
controllable.getOrientationController().rotate(
(float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z
);
//Gather and apply the current rotation deltas
var angRot = rollable.angRollVel(movementTime);
var angRot = cobblemon$getAngularVelocity(movementTime);
//Apply smoothing if requested by the controller.
//This Might be best if done by the controller itself?
if( rollable.useAngVelSmoothing() )
if(cobblemon$shouldUseAngVelSmoothing())
{
if( angRot != null ) {
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
rollable.rotate((float) yaw, (float) pitch, (float) roll);
}
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
}
//Otherwise simply apply the smoothing
else
{
if( angRot != null ) {
rollable.rotate((float) (
angRot.x * 10 * d),
(float) (angRot.y * 10 * d),
(float) (angRot.z * 10 * d)
);
}
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
}
return false;
}
@Inject(method = "handleAccumulatedMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;isMouseGrabbed()Z", ordinal = 0))
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci, @Local(ordinal = 1) double e) {
if (minecraft.player == null) return;
if (!(minecraft.player instanceof Rollable rollable)) return;
if (!rollable.shouldRoll()) return;
if (!(minecraft.player instanceof OrientationControllable controllable)) return;
if (!controllable.getOrientationController().isActive()) return;
if (minecraft.isPaused()) return;
var pitch = pitchSmoother.getNewDeltaValue(0, e);
var roll = rollSmoother.getNewDeltaValue(0, e);
rollable.rotate(0.0F, (float)pitch, (float)roll);
controllable.getOrientationController().rotate(0.0F, (float)pitch, (float)roll);
}
@Unique
private Vec3 cobblemon$getAngularVelocity(double deltaTime) {
var player = minecraft.player;
if (player == null) return Vec3.ZERO;
if (!(player instanceof OrientationControllable)) return Vec3.ZERO;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return Vec3.ZERO;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
return pokemonEntity.getRiding().angRollVel(pokemonEntity, player, deltaTime);
}
@Unique
private Vec3 cobblemon$getRideMouseRotation(double mouseX, double mouseY, double deltaTime) {
var player = minecraft.player;
if (player == null) return Vec3.ZERO;
var vehicle = player.getVehicle();
if (vehicle == null) return Vec3.ZERO;
if (!(vehicle instanceof PokemonEntity pokemon)) return Vec3.ZERO;
var sensitivity = cobblemon$getRidingSensitivity();
return pokemon.getRiding().rotationOnMouseXY(
pokemon,
player,
mouseY,
mouseX,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime
);
}
@Unique
private double cobblemon$getRidingSensitivity() {
var sensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
return Math.pow(sensitivity, 3);
}
@Unique
private boolean cobblemon$shouldUseAngVelSmoothing() {
var player = minecraft.player;
if (player == null) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return true;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
return pokemonEntity.getRiding().useAngVelSmoothing(pokemonEntity);
}
}
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
import com.cobblemon.mod.common.client.net.orientation.S2CUpdateOrientationHandler
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
@@ -73,6 +74,7 @@ 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.S2CUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
@@ -387,6 +389,9 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PokemonPasturedPacket.ID, PokemonPasturedPacket::decode, PokemonPasturedHandler))
list.add(PacketRegisterInfo(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler))
// Orientation
list.add(PacketRegisterInfo(S2CUpdateOrientationPacket.ID, S2CUpdateOrientationPacket::decode, S2CUpdateOrientationHandler))
// Behaviours
list.add(PacketRegisterInfo(PlayPosableAnimationPacket.ID, PlayPosableAnimationPacket::decode, PlayPosableAnimationHandler))
@@ -0,0 +1,71 @@
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.util.Mth
import net.minecraft.world.entity.LivingEntity
import org.joml.Matrix3f
import org.joml.Vector3f
import kotlin.math.asin
open class OrientationController(val entity: LivingEntity) {
companion object {
val FORWARDS: Vector3f = Vector3f(0.0f, 0.0f, -1.0f)
val UP: Vector3f = Vector3f(0.0f, 1.0f, 0.0f)
val LEFT: Vector3f = Vector3f(-1.0f, 0.0f, 0.0f)
}
open var active: Boolean = false
var orientation: Matrix3f? = null
private set
/** Adding this simply because it irritates me seeing `getActive` when most of this is Java. */
fun isActive() = active
fun updateOrientation(fn: (Matrix3f) -> Matrix3f?) {
if (!active) return
if (orientation == null) {
orientation = Matrix3f()
rotate(entity.yRot - 180, entity.xRot, 0f)
}
orientation = fn(orientation!!)
entity.yRot = yaw
entity.xRot = pitch
}
fun reset() {
orientation = null
active = false
}
fun rotate(yaw: Float, pitch: Float, roll: Float) {
rotateYaw(yaw)
rotatePitch(pitch)
rotateRoll(roll)
}
val forwardVector: Vector3f
get() = orientation?.transform(FORWARDS, Vector3f()) ?: Vector3f(FORWARDS)
val leftVector: Vector3f
get() = orientation?.transform(LEFT, Vector3f()) ?: Vector3f(LEFT)
val upVector : Vector3f
get() = orientation?.transform(UP, Vector3f()) ?: Vector3f(UP)
fun rotateYaw(yaw: Float) = updateOrientation { it.rotateY(-yaw.toRadians()) }
fun rotatePitch(pitch: Float) = updateOrientation { it.rotateX(-pitch.toRadians()) }
fun rotateRoll(roll: Float) = updateOrientation { it.rotateZ(-roll.toRadians()) }
val yaw: Float
get() = Mth.wrapDegrees(-FORWARDS.angleSigned(forwardVector, UP).toDegrees() + 180)
val pitch: Float
get() = Mth.wrapDegrees(-asin(forwardVector.y).toDegrees())
val roll: Float
get() = Mth.wrapDegrees(-upVector.angleSigned(UP, forwardVector).toDegrees())
}
@@ -10,23 +10,17 @@ package com.cobblemon.mod.common.api.riding
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.toProperties
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
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 kotlin.math.sin
import kotlin.math.sin
data class RidingManager(val entity: PokemonEntity) {
var lastSpeed = 0F
@@ -74,15 +68,11 @@ data class RidingManager(val entity: PokemonEntity) {
*/
fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val controller = getController(entity) ?: return
val pose = controller.pose(entity)
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
//val speedPlayer = driver.deltaMovement.horizontalDistance()
//driver.displayClientMessage(Component.literal("Speed player: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$speedPlayer b/t")), true)
//val speedEntity = entity.deltaMovement.length() //* 20 * 60 * 60) / ( 1000 )
//println(speedEntity)
//driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("${String.format("%.2f", speedEntity)} km/h")), true)
controller.tick(entity, driver, input)
if (!entity.level().isClientSide) {
val pose = controller.pose(entity)
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
}
}
fun speed(entity: PokemonEntity, driver: Player): Float {
@@ -217,4 +207,4 @@ data class RidingManager(val entity: PokemonEntity) {
val controller = getController(entity) ?: return false
return controller.dismountOnShift(entity)
}
}
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.api.riding.controller
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingState
@@ -54,6 +54,8 @@ interface RideController : Encodable, Decodable {
*/
val condition: (PokemonEntity) -> Boolean
fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {}
/**
* Specifies the pose the ridden mount should be positioned in. This queries the pose provider using
* the entity as a means of determining the conditions the entity is currently under. This allows for
@@ -84,9 +86,7 @@ interface RideController : Encodable, Decodable {
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
driver.yRot = entity.yRot
}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
/**
* Manipulates the movement input as necessary by the controller. This is primarily used to apply limits
@@ -113,7 +113,7 @@ interface RideController : Encodable, Decodable {
fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean = false
//If function is not overwritten by controllers then just perform the defualt
//If function is not overwritten by controllers then just perform the defauit
//rolling function which is roll on mousex and pitch on mousey
fun rotationOnMouseXY(
entity: PokemonEntity,
@@ -125,10 +125,9 @@ interface RideController : Encodable, Decodable {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is Rollable) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
//Might need to add the smoothing here for default.
val rollable = driver as Rollable
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
return Vec3(0.0, yMouse * invertPitch, xMouse * invertRoll)
@@ -150,4 +149,4 @@ interface RideController : Encodable, Decodable {
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeIdentifier(this.key)
}
}
}
@@ -13,7 +13,6 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.pokeball.PokeBalls
@@ -42,11 +41,8 @@ import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Vector3f
class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
companion object {
@@ -440,4 +436,4 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
}
cryAnimation = animation
}
}
}
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.orientation
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.client.orientation.S2CUpdateOrientationPacket
import net.minecraft.client.Minecraft
object S2CUpdateOrientationHandler : ClientNetworkPacketHandler<S2CUpdateOrientationPacket> {
override fun handle(packet: S2CUpdateOrientationPacket, client: Minecraft) {
val level = client.level ?: return
val entity = level.getEntity(packet.entityId)
if (entity is OrientationControllable) {
entity.orientationController.updateOrientation { _ -> packet.orientation }
packet.active?.let {
entity.orientationController.active = it
}
}
}
}
@@ -11,13 +11,12 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.client.ClientMoLangFunctions.animationFunctions
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
import com.cobblemon.mod.common.client.render.ModelLayer
@@ -717,12 +716,13 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
var scale = 1F
// We could improve this to be generalized for other entities. First we'd have to figure out wtf is going on, though.
if (entity is PokemonEntity) {
if(entity.passengers.isNotEmpty() && entity.controllingPassenger is Rollable && (entity.controllingPassenger as Rollable).orientation != null){
if(entity.passengers.isNotEmpty() && entity.controllingPassenger is OrientationControllable
&& (entity.controllingPassenger as OrientationControllable).orientationController.orientation != null){
val controllingPassenger = entity.controllingPassenger
val transformationMatrix = Matrix4f()
val center = Vector3f(0f, entity.bbHeight/2, 0f)
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f((controllingPassenger as Rollable).orientation!!))
transformationMatrix.mul(Matrix4f((controllingPassenger as OrientationControllable).orientationController.orientation!!))
transformationMatrix.translate(center.negate())
matrixStack.mulPose(transformationMatrix)
} else {
@@ -819,4 +819,4 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
condition = condition,
animations = { animations(it) }
)
}
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.client.render.player
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.Rideable
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
@@ -20,6 +20,7 @@ import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.player.AbstractClientPlayer
import net.minecraft.util.Mth
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Matrix4f
import org.joml.Vector3f
@@ -48,10 +49,6 @@ object MountedPlayerRenderer {
val locatorOffsetToCenter = locatorOffset.sub(center, Vector3f())
val transformationMatrix = Matrix4f()
if (player is Rollable && player.shouldRoll()){
//transformationMatrix.rotate(Axis.YP.rotationDegrees(-yBodyRot))
//transformationMatrix.mulLocal(player.orientation)
}
val rotatedOffset = transformationMatrix.transformPosition(locatorOffsetToCenter, Vector3f()).add(center).sub(Vector3f(0f, player.bbHeight/2, 0f))
matrix.translate(rotatedOffset)
@@ -73,12 +70,14 @@ object MountedPlayerRenderer {
}
//Rotates player
if (player is Rollable && player.shouldRoll() && !disableRollableRenderDebug) {
val controller = (player as? OrientationControllable)?.orientationController ?: return
if (controller.active && !disableRollableRenderDebug) {
val center = Vector3f(0f, player.bbHeight / 2, 0f)
val transformationMatrix = Matrix4f()
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f(player.orientation))
val orientation = controller.orientation ?: Matrix3f()
transformationMatrix.mul(Matrix4f(orientation))
transformationMatrix.translate(center.negate(Vector3f()))
//Pre-Undo Yaw
@@ -131,4 +130,4 @@ object MountedPlayerRenderer {
)
}
}
}
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.client.render.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.ClientBallDisplay
@@ -178,18 +178,20 @@ class PokemonRenderer(
packedLight: Int
) {
val driver = entity.firstPassenger ?: return
val rollable = driver as? Rollable ?: return
val rollable = driver as? OrientationControllable ?: return
val controller = rollable.orientationController
poseMatrix.pushPose()
if(!DISABLE_ROLLING_DEBUG && rollable.shouldRoll()){
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)
val transformationMatrix = Matrix4f()
//Move origin to center of Pokemon
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f(rollable.orientation))
val orientation = controller.orientation ?: Matrix3f()
transformationMatrix.mul(Matrix4f(orientation))
//Move origin to base of the entity
transformationMatrix.translate(center.negate(Vector3f()))
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.entity.pokemon
import com.cobblemon.mod.common.*
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.entity.PokemonSender
@@ -305,6 +304,11 @@ open class PokemonEntity(
.addPokemonEntityFunctions(this)
}
var flyDistO = 0F
var isPokemonWalking = false
var isPokemonFlying = false
init {
delegate.initialize(this)
delegate.changePokemon(pokemon)
@@ -394,6 +398,8 @@ open class PokemonEntity(
// return super.canWalkOnFluid(state)
}
override fun canSprint() = true
override fun handleEntityEvent(status: Byte) {
delegate.handleStatus(status)
super.handleEntityEvent(status)
@@ -418,7 +424,13 @@ open class PokemonEntity(
yHeadRotO = Mth.wrapDegrees(yHeadRotO)
/* I'm sure it's not even us but something altering the logic of the loops in LivingEntity */
isPokemonFlying = flyDist - flyDistO > 0.005F
isPokemonWalking = walkDist - walkDistO > 0.005F
val isMoving = entityData.get(MOVING)
super.tick()
flyDistO = flyDist
if (isBattling) {
// Deploy a platform if a non-wild Pokemon is touching water but not underwater.
@@ -1771,7 +1783,7 @@ open class PokemonEntity(
val center = Vector3f(0f, this.bbHeight/2, 0f)
val seatToCenter = center.sub(seatOffset, Vector3f())
val matrix = (this.passengers.first() as Rollable).orientation ?: Matrix3f().rotate((180f-passenger.yRot).toRadians(), Vector3f(0f, 1f, 0f))
val matrix = (this.passengers.first() as? OrientationControllable)?.orientationController?.orientation ?: Matrix3f().rotate((180f-passenger.yRot).toRadians(), Vector3f(0f, 1f, 0f))
val rotatedOffset = matrix.transform(seatToCenter, Vector3f()).add(center).sub(Vector3f(0f, passenger.bbHeight/2, 0f))
positionUpdater.accept(passenger, this.x + rotatedOffset.x, this.y + rotatedOffset.y, this.z + rotatedOffset.z)
@@ -1932,4 +1944,4 @@ open class PokemonEntity(
override fun resolveEntityScan(): LivingEntity {
return this
}
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.entity.pokemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.entity.PokemonSender
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
@@ -23,23 +22,19 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
import com.cobblemon.mod.common.util.getIsSubmerged
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.update
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.util.Optional
import net.minecraft.world.entity.Entity
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.level.ServerLevel
import net.minecraft.network.chat.Component
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.pathfinder.PathType
import org.joml.Matrix3f
import org.joml.Vector3f
import java.util.*
/** Handles purely server logic for a Pokémon */
class PokemonServerDelegate : PokemonSideDelegate {
@@ -119,7 +114,14 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
entity.entityData.set(PokemonEntity.ASPECTS, trackedAspects)
entity.entityData.set(PokemonEntity.LABEL_LEVEL, entity.pokemon.level)
entity.entityData.set(PokemonEntity.MOVING, entity.platform == PlatformType.NONE && entity.deltaMovement.multiply(1.0, if (entity.onGround()) 0.0 else 1.0, 1.0).length() > 0.005F)
if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.entityData.set(PokemonEntity.MOVING, entity.isPokemonFlying)
}
else {
entity.entityData.set(PokemonEntity.MOVING, entity.isPokemonWalking)
}
entity.entityData.set(PokemonEntity.FRIENDSHIP, entity.pokemon.friendship)
entity.entityData.set(PokemonEntity.CAUGHT_BALL, trackedBall)
@@ -288,4 +290,4 @@ class PokemonServerDelegate : PokemonSideDelegate {
entity.remove(Entity.RemovalReason.KILLED)
}
}
}
}
@@ -0,0 +1,47 @@
/*
* 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.util.cobblemonResource
import com.cobblemon.mod.common.util.readMatrix3f
import com.cobblemon.mod.common.util.writeMatrix3f
import net.minecraft.network.RegistryFriendlyByteBuf
import org.joml.Matrix3f
/**
* Packet sent from the server to update the current orientation of the
* given entity for the remote entity receiving this packet.
*
* @author Jackowes
* @since March 30th, 2025
*/
class S2CUpdateOrientationPacket internal constructor(val orientation: Matrix3f?, val active: Boolean?, val entityId: Int) : NetworkPacket<S2CUpdateOrientationPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVarInt(entityId)
buffer.writeBoolean(orientation != null)
if (orientation != null && active != null) {
buffer.writeMatrix3f(orientation)
buffer.writeBoolean(active)
}
}
companion object {
val ID = cobblemonResource("s2c_update_orientation")
fun decode(buffer: RegistryFriendlyByteBuf): S2CUpdateOrientationPacket {
val entityId = buffer.readVarInt()
val valid = buffer.readBoolean()
val orientation = if (valid) buffer.readMatrix3f() else null
val active = if (valid) buffer.readBoolean() else null
return S2CUpdateOrientationPacket(orientation, active, entityId)
}
}
}
@@ -26,7 +26,7 @@ abstract class SpawnExtraDataEntityPacket<T: NetworkPacket<T>, E : Entity>(var v
abstract fun encodeEntityData(buffer: RegistryFriendlyByteBuf)
abstract fun applyData(entity: E)
abstract fun applyData(entity: E, level: ClientLevel)
abstract fun checkType(entity: Entity): Boolean
@@ -48,7 +48,7 @@ abstract class SpawnExtraDataEntityPacket<T: NetworkPacket<T>, E : Entity>(var v
)
// Cobblemon start
if (this.checkType(entity)) {
this.applyData(entity as E)
this.applyData(entity as E, world)
}
// Cobblemon end
world.addEntity(entity)
@@ -59,4 +59,4 @@ abstract class SpawnExtraDataEntityPacket<T: NetworkPacket<T>, E : Entity>(var v
companion object {
fun decodeVanillaPacket(buffer: RegistryFriendlyByteBuf) = ClientboundAddEntityPacket(buffer)
}
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.*
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
import net.minecraft.resources.ResourceLocation
@@ -36,7 +37,7 @@ class SpawnGenericBedrockPacket(
) : SpawnExtraDataEntityPacket<SpawnGenericBedrockPacket, GenericBedrockEntity>(vanillaSpawnPacket) {
override val id: ResourceLocation = ID
override fun applyData(entity: GenericBedrockEntity) {
override fun applyData(entity: GenericBedrockEntity, level: ClientLevel) {
entity.category = this.category
entity.aspects = this.aspects
entity.entityData.set(GenericBedrockEntity.POSE_TYPE, this.poseType)
@@ -74,4 +75,4 @@ class SpawnGenericBedrockPacket(
return SpawnGenericBedrockPacket(category, aspects, poseType, scale, width, height, startAge, vanillaPacket)
}
}
}
}
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.util.writeEnumConstant
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeString
import com.cobblemon.mod.common.util.writeText
import net.minecraft.client.multiplayer.ClientLevel
import java.util.UUID
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
@@ -69,7 +70,7 @@ class SpawnNPCPacket(
buffer.writeBoolean(this.hideNameTag)
}
override fun applyData(entity: NPCEntity) {
override fun applyData(entity: NPCEntity, level: ClientLevel) {
entity.npc = NPCClasses.getByIdentifier(this.npcClass) ?: error("received unknown NPCClass: $npcClass")
entity.customName = name
entity.entityData.set(NPCEntity.LEVEL, this.level)
@@ -104,4 +105,4 @@ class SpawnNPCPacket(
}
}
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.*
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
import net.minecraft.resources.ResourceLocation
@@ -30,7 +31,7 @@ class SpawnPokeballPacket(
buffer.writeCollection(aspects) { _, aspect -> buffer.writeString(aspect)}
}
override fun applyData(entity: EmptyPokeBallEntity) {
override fun applyData(entity: EmptyPokeBallEntity, level: ClientLevel) {
entity.pokeBall = this.pokeBall
entity.aspects = this.aspects
}
@@ -47,4 +48,4 @@ class SpawnPokeballPacket(
return SpawnPokeballPacket(pokeBall, aspects, vanillaPacket)
}
}
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.*
import net.minecraft.client.multiplayer.ClientLevel
import java.util.UUID
import net.minecraft.world.entity.Entity
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -44,7 +45,8 @@ class SpawnPokemonPacket(
var spawnYaw: Float,
var friendship: Int,
var freezeFrame: Float,
vanillaSpawnPacket: ClientboundAddEntityPacket
var passengers: IntArray,
vanillaSpawnPacket: ClientboundAddEntityPacket,
) : SpawnExtraDataEntityPacket<SpawnPokemonPacket, PokemonEntity>(vanillaSpawnPacket) {
override val id: ResourceLocation = ID
@@ -70,6 +72,7 @@ class SpawnPokemonPacket(
entity.entityData.get(PokemonEntity.SPAWN_DIRECTION),
entity.entityData.get(PokemonEntity.FRIENDSHIP),
entity.entityData.get(PokemonEntity.FREEZE_FRAME),
entity.passengers.map { it.id }.toIntArray(),
vanillaSpawnPacket
)
@@ -94,9 +97,10 @@ class SpawnPokemonPacket(
buffer.writeFloat(this.spawnYaw)
buffer.writeInt(this.friendship)
buffer.writeFloat(this.freezeFrame)
buffer.writeVarIntArray(this.passengers)
}
override fun applyData(entity: PokemonEntity) {
override fun applyData(entity: PokemonEntity, level: ClientLevel) {
entity.ownerUUID = ownerId
entity.pokemon.apply {
scaleModifier = this@SpawnPokemonPacket.scaleModifier
@@ -121,6 +125,12 @@ class SpawnPokemonPacket(
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, spawnYaw)
entity.entityData.set(PokemonEntity.FRIENDSHIP, friendship)
entity.entityData.set(PokemonEntity.FREEZE_FRAME, freezeFrame)
entity.ejectPassengers()
passengers.forEach {
val passenger = level.getEntity(it) ?: return@forEach
passenger.startRiding(entity)
}
}
override fun checkType(entity: Entity): Boolean = entity is PokemonEntity
@@ -148,10 +158,11 @@ class SpawnPokemonPacket(
val spawnAngle = buffer.readFloat()
val friendship = buffer.readInt()
val freezeFrame = buffer.readFloat()
val passengers = buffer.readVarIntArray()
val vanillaPacket = decodeVanillaPacket(buffer)
return SpawnPokemonPacket(ownerId, scaleModifier, speciesId, gender, shiny, formName, aspects, battleId, phasingTargetId, beamModeEmitter, platform, nickname, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, friendship, freezeFrame, vanillaPacket)
return SpawnPokemonPacket(ownerId, scaleModifier, speciesId, gender, shiny, formName, aspects, battleId, phasingTargetId, beamModeEmitter, platform, nickname, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, friendship, freezeFrame, passengers, vanillaPacket)
}
}
}
}
@@ -8,16 +8,25 @@
package com.cobblemon.mod.common.net.serverhandling.orientation
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import kotlin.math.atan2
object OrientationPacketHandler : ServerNetworkPacketHandler<C2SUpdateOrientationPacket> {
override fun handle(packet: C2SUpdateOrientationPacket, server: MinecraftServer, player: ServerPlayer) {
if (player is Rollable) {
player.updateOrientation { _ -> packet.orientation }
if (player is OrientationControllable) {
player.orientationController.updateOrientation { _ -> packet.orientation }
println("Packet Received! Roll ${"%.2f".format(player.orientationController.roll)} degrees")
// val orientation = packet.orientation
// val roll = orientation?.let { atan2(it.m10, it.m00) } ?: 0f
// val rollDegrees = Math.toDegrees(roll.toDouble())
// println("Packet Received! Roll: ${"%.2f".format(rollDegrees)} degrees")
}
}
}
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
@@ -19,9 +19,7 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.states.BirdAirState
import com.cobblemon.mod.common.util.*
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
@@ -32,9 +30,7 @@ import kotlin.math.*
class BirdAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) {
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
it.deltaMovement.length() > 0.5 })
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
@@ -82,15 +78,15 @@ class BirdAirController : RideController {
calculateRideSpaceVel(entity, driver, state)
//Translate ride space velocity to world space velocity.
val rollable = driver as? Rollable
if (rollable != null) {
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
//Need to deadzone this when straight up or down
upForce += -1.0 * sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
forwardForce += cos(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
upForce += -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
forwardForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
upForce += cos(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.y
forwardForce += sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.y
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.y
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.y
}
@@ -113,7 +109,7 @@ class BirdAirController : RideController {
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
val state = getState(entity, ::BirdAirState)
//TODO: Tie in handling
@@ -121,14 +117,14 @@ class BirdAirController : RideController {
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val rotationChangeRate = 10.0
if (rollable != null) {
var yawForce = rotationChangeRate * sin(Math.toRadians(rollable.roll.toDouble()))
if (controller != null) {
var yawForce = rotationChangeRate * sin(Math.toRadians(controller.roll.toDouble()))
//for a bit of correction on the rolls influence pitch as well
var pitchForce = -0.35 * rotationChangeRate * abs(sin(Math.toRadians(rollable.roll.toDouble())))
var pitchForce = -0.35 * rotationChangeRate * abs(sin(Math.toRadians(controller.roll.toDouble())))
//limit rotation modulation when pitched up heavily or pitched down heavily
yawForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble())))
pitchForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble()))) * 1.5
yawForce *= abs(cos(Math.toRadians(controller.pitch.toDouble())))
pitchForce *= abs(cos(Math.toRadians(controller.pitch.toDouble()))) * 1.5
return Vec3(yawForce, pitchForce, 0.0)
}
@@ -146,8 +142,8 @@ class BirdAirController : RideController {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is Rollable) return Vec3.ZERO
val rollable = driver as Rollable
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val state = getState(entity, ::BirdAirState)
val handling = getRuntime(entity).resolveDouble(handlingExpr)
@@ -169,12 +165,12 @@ class BirdAirController : RideController {
//Limit roll by non linearly decreasing inputs towards
// a rotation limit based on the current distance from
// that rotation limit
if (abs(rollable.roll + rollForce) < rotLimit) {
if (sign(rollForce) == sign(rollable.roll).toDouble()) {
val d = abs(abs(rollable.roll) - rotLimit)
if (abs(controller.roll + rollForce) < rotLimit) {
if (sign(rollForce) == sign(controller.roll).toDouble()) {
val d = abs(abs(controller.roll) - rotLimit)
rollForce *= (d.pow(2)) / (rotLimit.pow(2))
}
} else if (sign(rollForce) == sign(rollable.roll).toDouble()) {
} else if (sign(rollForce) == sign(controller.roll).toDouble()) {
rollForce = 0.0
}
@@ -273,8 +269,6 @@ class BirdAirController : RideController {
* Calculates the change in the ride space vector due to player input and ride state
*/
fun calculateRideSpaceVel(entity: PokemonEntity, driver: Player, state: BirdAirState) {
//retrieve stats
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val glideTopSpeed = getRuntime(entity).resolveDouble(glideTopSpeedExpr)
@@ -305,10 +299,10 @@ class BirdAirController : RideController {
activeInput = true
}
val rollable = driver as? Rollable
if (rollable != null) {
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
//Base glide speed change on current pitch of the ride.
glideSpeedChange = sin(Math.toRadians(rollable.pitch.toDouble()))
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
glideSpeedChange = glideSpeedChange.pow(3) * 0.5
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
@@ -353,8 +347,8 @@ class BirdAirController : RideController {
//Vertical movement based on driver input.
val vertTopSpeed = topSpeed / 2.0
val vertInput = when {
Minecraft.getInstance().options.keyJump.isDown() -> 1.0
Minecraft.getInstance().options.keyShift.isDown() -> -1.0
driver.jumping -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
}
@@ -399,4 +393,4 @@ class BirdAirController : RideController {
state.rideVel.y,
lerp( state.rideVel.z,0.0, topSpeed / ( 20.0 * 30.0)))
}
}
}
@@ -9,13 +9,12 @@
package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
@@ -28,7 +27,6 @@ import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
class DolphinController : RideController {
companion object {
@@ -54,7 +52,8 @@ class DolphinController : RideController {
override val key: ResourceLocation = KEY
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { it.deltaMovement.length() >= 0.05 })
.with(PoseOption(PoseType.SWIM) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { entity ->
//If there are only fluid blocks or air block below the ride
@@ -110,12 +109,12 @@ class DolphinController : RideController {
//Not sure if this is desired? Will need to mess around with this
//a bit more
var zComp = 0.0
val rollable = driver as? Rollable
if (rollable != null)
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null)
{
//Can be used again if I better figure out how to deadzone it near the tops?
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(rollable.roll.toDouble()))
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(rollable.pitch.toDouble()))
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.roll.toDouble()))
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.pitch.toDouble()))
}
val currVelocity = Vec3(0.0, yComp , g.toDouble())
@@ -130,17 +129,17 @@ class DolphinController : RideController {
return Vec3(0.0, 0.0, 0.0)
}
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//this should be changed to be speed maybe?
val movingForce = driver.zza
if (rollable != null) {
var yawAngVel = 3 * sin(Math.toRadians(rollable.roll.toDouble())).toFloat()
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(rollable.roll.toDouble())).toFloat())
if (controller != null) {
var yawAngVel = 3 * sin(Math.toRadians(controller.roll.toDouble())).toFloat()
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(controller.roll.toDouble())).toFloat())
//limit rotation modulation when pitched up heavily or pitched down heavily
yawAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
pitchAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
yawAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
pitchAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
//if you are not pressing forward then don't turn
//yawAngVel *= movingForce
//Ignore x,y,z its angular velocity:
@@ -199,4 +198,4 @@ class DolphinController : RideController {
val z = buffer.readDouble()
this.lastVelocity = Vec3(x, y, z)
}
}
}
@@ -45,6 +45,18 @@ class FallToFlightCompositeController : RideController {
var flightController: RideController = GliderAirController()
private set
override fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val state = getState(entity, ::CompositeState)
val shouldBeFlying = checkShouldBeFlying(entity, state.activeController == flightController)
if (state.activeController == flightController && !shouldBeFlying) { // && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
} else if (state.activeController == landController && shouldBeFlying) {
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
}
}
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
@@ -77,20 +89,7 @@ class FallToFlightCompositeController : RideController {
}
}
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::CompositeState)
val shouldBeFlying = checkShouldBeFlying(entity, state.activeController == flightController)
if (state.activeController == flightController && !shouldBeFlying) { // && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
} else if (state.activeController == landController && shouldBeFlying) {
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).speed(entity, driver)
}
@@ -149,4 +148,4 @@ class FallToFlightCompositeController : RideController {
companion object {
val KEY = cobblemonResource("composite/fall_to_flight")
}
}
}
@@ -10,16 +10,13 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.states.GenericLandState
import com.cobblemon.mod.common.pokemon.riding.states.JetAirState
import com.cobblemon.mod.common.util.*
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -28,7 +25,9 @@ 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.*
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
class GenericLandController : RideController {
companion object {
@@ -66,8 +65,11 @@ class GenericLandController : RideController {
@Transient
override val key: ResourceLocation = KEY
@Transient
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.deltaMovement.horizontalDistance() > 0.1 })
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
@Transient
override val condition: (PokemonEntity) -> Boolean = { entity ->
// Are there any blocks under the mon that aren't air or fluid
@@ -115,7 +117,7 @@ class GenericLandController : RideController {
calculateRideSpaceVel(entity, driver, state)
//Jump the thang!
if (Minecraft.getInstance().options.keyJump.isDown() && entity.onGround()) {
if (driver.jumping && entity.onGround()) {
state.rideVel = Vec3(state.rideVel.x, 2.0, state.rideVel.z)
}
@@ -245,4 +247,4 @@ class GenericLandController : RideController {
}
}
}
}
@@ -51,8 +51,10 @@ class GenericLiquidController : RideController {
private set
override val key: ResourceLocation = KEY
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { it.isSwimming && it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { entity ->
//This could be kinda weird... what if the top of the mon is in a fluid but the bottom isnt?
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
@@ -120,4 +122,4 @@ class GenericLiquidController : RideController {
this.reverseDriveFactor = buffer.readString().asExpression()
this.strafeFactor = buffer.readString().asExpression()
}
}
}
@@ -27,7 +27,7 @@ import net.minecraft.world.phys.Vec3
class GliderAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.horizontalDistance() > 0.1 })
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
var glideSpeed: Expression = "0.1".asExpression()
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
@@ -38,7 +38,7 @@ import kotlin.math.sin
class HelicopterAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.horizontalDistance() > 0.1 })
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
//If there are only fluid blocks or air block below the ride
//then activate the controller. If it is in water the ride will
@@ -87,12 +87,12 @@ class HelicopterAirController : RideController {
var xVel = 0.0
var zVel = 0.0
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//Horizontal velocity is based on pitch and roll
if (rollable != null) {
xVel = -1.0 * sin(Math.toRadians(rollable.roll.toDouble()))
zVel = sin(Math.toRadians(rollable.pitch.toDouble()))
if (controller != null) {
xVel = -1.0 * sin(Math.toRadians(controller.roll.toDouble()))
zVel = sin(Math.toRadians(controller.pitch.toDouble()))
}
return Vec3(xVel, yVel, zVel)
@@ -101,7 +101,7 @@ class HelicopterAirController : RideController {
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//In degrees per second? It was supposed to be I think but
//I have messed something up
@@ -113,7 +113,7 @@ class HelicopterAirController : RideController {
//TODO: Fix accumulated movement causing pitch and roll out of set bounds
if (rollable != null) {
if (controller != null) {
//If the roll or pitch have exceeded the limit then do not modulate that
//rotation in that direction further
@@ -124,20 +124,20 @@ class HelicopterAirController : RideController {
//The three denotes that the force will be 1/3 what it would have been at
//the rotation limit
rollForce *= 2.0.pow(-1.0 * (abs(rollable.roll.toDouble()) / ROTATION_LIMIT))
rollForce *= 2.0.pow(-1.0 * (abs(controller.roll.toDouble()) / ROTATION_LIMIT))
pitchForce *= 2.0.pow(-1.0 * (abs(rollable.pitch.toDouble()) / ROTATION_LIMIT))
pitchForce *= 2.0.pow(-1.0 * (abs(controller.pitch.toDouble()) / ROTATION_LIMIT))
if (rollable.roll >= ROTATION_LIMIT && rollForce > 0.0) {
if (controller.roll >= ROTATION_LIMIT && rollForce > 0.0) {
rollForce = 0.0;
}
if (rollable.roll <= -ROTATION_LIMIT && rollForce < 0.0) {
if (controller.roll <= -ROTATION_LIMIT && rollForce < 0.0) {
rollForce = 0.0;
}
if (rollable.pitch >= ROTATION_LIMIT && pitchForce > 0.0) {
if (controller.pitch >= ROTATION_LIMIT && pitchForce > 0.0) {
pitchForce = 0.0;
}
if (rollable.pitch <= -ROTATION_LIMIT && pitchForce < 0.0) {
if (controller.pitch <= -ROTATION_LIMIT && pitchForce < 0.0) {
pitchForce = 0.0;
}
@@ -181,9 +181,9 @@ class HelicopterAirController : RideController {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is Rollable) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
val rollable = driver as Rollable
val controller = (driver as OrientationControllable).orientationController
val invertYaw = if (Cobblemon.config.invertYaw) -1 else 1
//yaw, pitch, roll
return Vec3(xMouse * invertYaw, 0.0, 0.0)
@@ -216,4 +216,4 @@ class HelicopterAirController : RideController {
val KEY = cobblemonResource("air/helicopter")
val ROTATION_LIMIT = 30.0f
}
}
}
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
@@ -30,7 +30,7 @@ import kotlin.math.*
class JetAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.length() > 0 })
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
@@ -105,17 +105,17 @@ class JetAirController : RideController {
//Gather state information
val state = getState(entity, ::JetAirState)
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//Calculate ride space velocity
calculateRideSpaceVel(entity, driver, state)
//Translate ride space velocity to world space velocity.
if( rollable != null ) {
if(controller != null) {
upForce = -1.0 * sin(Math.toRadians( rollable.pitch.toDouble() )) * state.rideVel.z
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
forwardForce = cos(Math.toRadians( rollable.pitch.toDouble() )) * state.rideVel.z
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
}
@@ -169,7 +169,7 @@ class JetAirController : RideController {
sensitivity: Double,
deltaTime: Double ): Vec3
{
if(driver !is Rollable) return Vec3.ZERO
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)
@@ -23,7 +23,6 @@ import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.writeString
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
@@ -47,64 +46,67 @@ class RunUpToFlightCompositeController : RideController {
var flightController: RideController = BirdAirController()
private set
private fun shouldTransitionToGround(state: RunUpToFlightCompositeState, entity: PokemonEntity): Boolean {
return state.activeController == flightController
&& entity.onGround()
&& state.timeTransitioned + 20 < entity.level().gameTime
}
private fun shouldTransitionToAir(state: RunUpToFlightCompositeState, entity: PokemonEntity, driver: Player): Boolean {
return state.activeController == landController
&& driver.jumping
&& state.timeTransitioned + 20 < entity.level().gameTime
}
override fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val state = getState(entity, ::RunUpToFlightCompositeState)
val flightState = flightController.getState(entity, ::BirdAirState)
val groundState = landController.getState(entity, ::GenericLandState)
if (shouldTransitionToGround(state, entity)) {
//Pass the speed to the next state
groundState.currSpeed = flightState.currSpeed
groundState.rideVel = flightState.rideVel
groundState.stamina = flightState.stamina
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
state.timeTransitioned = entity.level().gameTime
}
else if (shouldTransitionToAir(state, entity, driver)) {
//Pass the speed to the next state
flightState.currSpeed = groundState.currSpeed
flightState.rideVel = groundState.rideVel
flightState.stamina = groundState.stamina
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
state.timeTransitioned = entity.level().gameTime
}
}
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
fun getActiveController(entity: PokemonEntity): RideController {
val state = getState(entity, ::RunUpToFlightCompositeState)
return state.activeController ?: let {
val controller = if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
flightController
} else {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
landController
}
state.activeController = controller
if (state.activeController != null) return state.activeController!!
if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
state.activeController = flightController
state.timeTransitioned = entity.level().gameTime
controller
return flightController
}
else {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
state.activeController = landController
state.timeTransitioned = entity.level().gameTime
return landController
}
}
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::RunUpToFlightCompositeState)
if (state.activeController == flightController && entity.onGround() &&
state.timeTransitioned + 20 < entity.level().gameTime) {
//Pass the speed to the next state
val flightState = flightController.getState(entity, ::BirdAirState)
val groundState = landController.getState(entity, ::GenericLandState)
groundState.currSpeed = flightState.currSpeed
groundState.rideVel = flightState.rideVel
groundState.stamina = flightState.stamina
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
it.timeTransitioned = entity.level().gameTime
}
}
else if (state.activeController == landController && Minecraft.getInstance().options.keyJump.isDown &&
state.timeTransitioned + 20 < entity.level().gameTime) {
//Pass the speed to the next state
val flightState = flightController.getState( entity, ::BirdAirState )
val groundState = landController.getState( entity, ::GenericLandState )
flightState.currSpeed = groundState.currSpeed
flightState.rideVel = groundState.rideVel
flightState.stamina = groundState.stamina
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).speed(entity, driver)
}
@@ -142,9 +144,6 @@ class RunUpToFlightCompositeController : RideController {
jumpStrength: Int
): Vec3 {
val controller = getActiveController(entity)
val state = getState(entity, ::CompositeState)
val isJumping = Minecraft.getInstance().options.keyJump.isDown
return controller.jumpForce(entity, driver, jumpStrength)
}
@@ -212,4 +211,4 @@ class RunUpToFlightCompositeController : RideController {
companion object {
val KEY = cobblemonResource("composite/run_up_to_flight")
}
}
}
@@ -21,9 +21,7 @@ import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.writeString
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
@@ -34,7 +32,9 @@ import net.minecraft.world.phys.Vec3
class RunUpToJetFlightCompositeController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
.with(PoseOption(PoseType.WALK) {
it.entityData.get(PokemonEntity.MOVING)
})
override val condition: (PokemonEntity) -> Boolean = { true }
var minimumSpeed: Expression = "0.5".asExpression()
@@ -47,6 +47,44 @@ class RunUpToJetFlightCompositeController : RideController {
var flightController: RideController = JetAirController()
private set
fun shouldTransitionToGround(state: RunUpToFlightCompositeState, entity: PokemonEntity, driver: Player): Boolean {
if (state.activeController != flightController) return false
if (state.timeTransitioned + 20 >= entity.level().gameTime) return false
return driver.isSprinting || entity.onGround()
}
fun shouldTransitionToAir(state: RunUpToFlightCompositeState, entity: PokemonEntity, driver: Player, input: Vec3): Boolean {
if (state.activeController != landController) return false
if (state.timeTransitioned + 20 >= entity.level().gameTime) return false
return driver.isSprinting && input.z > 0.5
}
//TODO: This composite controller needs to have the jet lift off at a certain speed.
//maybe just pressing shift or space? Unsure, will need to test to see what works best.
override fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val state = getState(entity, ::RunUpToFlightCompositeState)
val flightState = flightController.getState(entity, ::JetAirState)
val groundState = landController.getState(entity, ::GenericLandState)
if (shouldTransitionToGround(state, entity, driver)) {
groundState.currSpeed = flightState.currSpeed
groundState.stamina = flightState.stamina
groundState.rideVel = flightState.rideVel
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
state.timeTransitioned = entity.level().gameTime
}
else if (shouldTransitionToAir(state, entity, driver, input)) {
flightState.currSpeed = groundState.currSpeed
flightState.stamina = groundState.stamina
flightState.rideVel = groundState.rideVel
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
state.timeTransitioned = entity.level().gameTime
}
}
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
@@ -67,57 +105,7 @@ class RunUpToJetFlightCompositeController : RideController {
}
}
//TODO: This composite controller needs to have the jet lift off at a certain speed.
//maybe just pressing shift or space? Unsure, will need to test to see what works best.
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::RunUpToFlightCompositeState)
if ((state.activeController == flightController && entity.onGround() &&
state.timeTransitioned + 20 < entity.level().gameTime) ||
(Minecraft.getInstance().options.keySprint.isDown && state.activeController == flightController &&
state.timeTransitioned + 20 < entity.level().gameTime)) {
//Pass the data to the next state
val flightState = flightController.getState(entity, ::JetAirState)
val groundState = landController.getState(entity, ::GenericLandState)
groundState.currSpeed = flightState.currSpeed
groundState.stamina = flightState.stamina
groundState.rideVel = flightState.rideVel
//Clear accumulated rotation input when transferring to ground input
flightState.currMouseXForce = 0.0
flightState.currMouseYForce = 0.0
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
//These conditions should also be more graceful. As it stands though it feels better than jump and then
//having to hit the ground
else if(
state.activeController == landController && entity.speed > 0.5 &&
Minecraft.getInstance().options.keySprint.isDown() &&
state.timeTransitioned + 20 < entity.level().gameTime
)
{
//Pass the data to the next state
val flightState = flightController.getState(entity, ::JetAirState)
val groundState = landController.getState(entity, ::GenericLandState)
flightState.currSpeed = groundState.currSpeed
flightState.stamina = groundState.stamina
flightState.rideVel = groundState.rideVel
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).speed(entity, driver)
}
@@ -82,7 +82,7 @@ class VehicleLandController : RideController {
@Transient
override val key: ResourceLocation = KEY
@Transient
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.deltaMovement.horizontalDistance() > 0.1 })
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
@Transient
override val condition: (PokemonEntity) -> Boolean = { entity ->
// Are there any blocks under the mon that aren't air or fluid
@@ -191,4 +191,4 @@ class VehicleLandController : RideController {
this.lookYawLimit = buffer.readString().asExpression()
}
}
}
@@ -48,6 +48,8 @@
"PlayerAdvancementsMixin",
"PlayerMixin",
"PointedDripstoneBlockMixin",
"ServerPlayerMixin",
"ServerEntityMixin",
"ShearsDispenserBehaviorMixin",
"StructurePoolAccessor",
"StructurePoolGeneratorMixin",
@@ -56,8 +58,10 @@
"accessor.AccessorEntity",
"accessor.BiomeAccessor",
"accessor.BoatEntityAccessor",
"accessor.ChunkMapAccessor",
"accessor.EntryListWidgetAccessor",
"accessor.StatusEffectInstanceAccessor",
"accessor.TrackedEntityAccessor",
"entity.EntityMixin",
"entity.TameableEntityMixin",
"invoker.AdornRegisterInvoker",