Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8e55214a1 | ||
|
|
b1d81d89b5 | ||
|
|
714030458b | ||
|
|
411a728282 | ||
|
|
dddb44a9fd | ||
|
|
ede8bdf0e3 | ||
|
|
ef79cf79e3 | ||
|
|
2b0128aed0 |
No files matched your search
@@ -8,16 +8,8 @@
|
||||
### Pokémon Added
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Froakie
|
||||
- Frogadier
|
||||
- Greninja
|
||||
(They have eyelids now.)
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Froakie
|
||||
- Frogadier
|
||||
- Greninja
|
||||
(They can blink now.)
|
||||
|
||||
### Changes
|
||||
- Reworked spawn presets and added structure tags for spawns
|
||||
@@ -25,8 +17,6 @@
|
||||
### Fixes
|
||||
- Fixed blank Cooking Pot recipe book entries and missing tabs on Fabric.
|
||||
- Fixed Minior's "Shields Down" ability not working as intended
|
||||
- Fixed Dusclops using Squirtle's cry
|
||||
- Fixed size error with ceiling attached Orb hitbox
|
||||
|
||||
### Developer
|
||||
|
||||
@@ -34,7 +24,6 @@
|
||||
- Added `q.target_fainted` function to move action effects that checks if any opponents fainted from the move.
|
||||
|
||||
### Particles
|
||||
- Updated Water shuriken to account for new `q.target_fainted` function
|
||||
|
||||
#### New Move Animations
|
||||
|
||||
|
||||
@@ -75,9 +75,10 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
public @NotNull HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle instanceof PokemonEntity pokemonEntity && pokemonEntity.getDelegate() instanceof PokemonClientDelegate delegate) {
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
// Passenger order isn't seat order, so look the seat up directly
|
||||
Seat seat = pokemonEntity.getSeatForPassenger(this);
|
||||
MatrixWrapper locator = seat != null ? delegate.getLocatorStates().get(seat.getLocator()) : null;
|
||||
if (locator == null) return super.pick(hitDistance, partialTicks, hitFluids);
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
|
||||
@@ -99,7 +100,8 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
|
||||
@Unique
|
||||
private Vec3 cobblemon$getEyePosition(float partialTicks, PokemonEntity pokemonEntity, Vec3 locatorOffset) {
|
||||
OrientationController controller = this.getOrientationController();
|
||||
// The ride owns the orientation, the passenger's own controller is never active
|
||||
OrientationController controller = ((OrientationControllable) pokemonEntity).getOrientationController();
|
||||
|
||||
float currEyeHeight = this.getEyeHeight();
|
||||
Vec3 offset = locatorOffset.add(pokemonEntity.position());
|
||||
@@ -107,7 +109,7 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
Quaternionf orientation = controller.getRenderOrientation(partialTicks);
|
||||
Vec3 rotatedEyeHeight = new Vec3(orientation.transform(new Vector3f(0f, currEyeHeight - (this.getBbHeight() / 2), 0f)));
|
||||
|
||||
offset.add(rotatedEyeHeight);
|
||||
offset = offset.add(rotatedEyeHeight);
|
||||
}
|
||||
|
||||
return offset;
|
||||
|
||||
@@ -83,7 +83,7 @@ public abstract class CameraMixin implements CameraDuck {
|
||||
}
|
||||
|
||||
// reset the rotation values if it is now not being ridden.
|
||||
MountedCameraRenderer.INSTANCE.setSmoothRotation(null);
|
||||
MountedCameraRenderer.INSTANCE.reset();
|
||||
original.call(instance, x, y, z);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.phys.Vec2;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -71,17 +72,18 @@ public class HumanoidModelMixin {
|
||||
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
|
||||
);
|
||||
|
||||
// If the player is on a rollable ride then use the ride local rotations to rotate the head
|
||||
// This is to allow the player head to face the correct direction when "freelooking"
|
||||
// TODO: find a way for this to work on built jars without borking head rotations.
|
||||
// if (entity instanceof RidePassenger playerRotater &&
|
||||
// vehicle instanceof OrientationControllable controller &&
|
||||
// controller.getOrientationController().isActive()) {
|
||||
// this.head.xRot = playerRotater.cobblemon$getRideXRot();
|
||||
// headPitch = playerRotater.cobblemon$getRideXRot();
|
||||
// this.head.yRot = playerRotater.cobblemon$getRideYRot();
|
||||
// netHeadYaw = playerRotater.cobblemon$getRideYRot();
|
||||
// }
|
||||
// Vanilla's head angles are world yaw but the body sits on a seat that can be on its side, so measure the real look against the seat
|
||||
if (shouldRotatePlayerHead) {
|
||||
Vec2 riderHead = MountedPlayerRenderer.riderHeadAngles(pokemonEntity, entity, Mth.clamp(ageInTicks - entity.tickCount, 0f, 1f));
|
||||
if (riderHead != null) {
|
||||
netHeadYaw = riderHead.x;
|
||||
headPitch = riderHead.y;
|
||||
if (this.head != null) {
|
||||
this.head.yRot = netHeadYaw * Mth.DEG_TO_RAD;
|
||||
this.head.xRot = headPitch * Mth.DEG_TO_RAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldRotatePlayerHead) {
|
||||
netHeadYaw = 0f;
|
||||
|
||||
@@ -9,11 +9,14 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
|
||||
import com.cobblemon.mod.common.client.render.player.FirstPersonRecallBallRenderer;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.client.renderer.ItemInHandRenderer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
@@ -40,6 +43,14 @@ public class ItemInHandRendererMixin {
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderHandsWithItems", at = @At("HEAD"))
|
||||
private void cobblemon$rollHandsWithRide(float partialTicks, PoseStack poseStack, MultiBufferSource.BufferSource buffer, LocalPlayer player, int light, CallbackInfo ci) {
|
||||
float roll = MountedCameraRenderer.INSTANCE.getHandRoll();
|
||||
if (roll != 0f) {
|
||||
poseStack.mulPose(Axis.ZN.rotationDegrees(roll));
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "renderHandsWithItems", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V"))
|
||||
private void cobblemon$renderHandswithItems(PoseStack instance, Quaternionf quaternion) {
|
||||
if (!(Minecraft.getInstance().player != null && Minecraft.getInstance().player.getVehicle() instanceof OrientationControllable controllable && controllable.getOrientationController().isActive())) {
|
||||
|
||||
@@ -32,7 +32,6 @@ import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.Input;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
@@ -50,11 +49,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@Mixin(LocalPlayer.class)
|
||||
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable, DoubleJump {
|
||||
|
||||
@Unique Matrix3f cobblemon$lastOrientation;
|
||||
@Unique int cobblemon$lastOrientationVehicleId = -1;
|
||||
|
||||
@Unique float cobblemon$lastRideXRot;
|
||||
@Unique float cobblemon$lastRideYRot;
|
||||
@@ -81,30 +82,28 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
private void cobblemon$updateRotationMatrixPassenger(CallbackInfo ci) {
|
||||
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
if (!vehicleController.isActive() || vehicleController.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = vehicleController.getOrientation() != null ? new Matrix3f(vehicleController.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(this.getVehicle().getId(), vehicleController.getOrientation()));
|
||||
if (!vehicleController.isActive()) {
|
||||
// Forget the last sent value so the next ride always gets its first orientation sent
|
||||
cobblemon$lastOrientation = null;
|
||||
return;
|
||||
}
|
||||
var orientation = vehicleController.getOrientation();
|
||||
var vehicleId = this.getVehicle().getId();
|
||||
// Compare by value, the stored one is a copy so a reference check never matches
|
||||
if (vehicleId == cobblemon$lastOrientationVehicleId && Objects.equals(orientation, cobblemon$lastOrientation)) return;
|
||||
cobblemon$lastOrientation = orientation != null ? new Matrix3f(orientation) : null;
|
||||
cobblemon$lastOrientationVehicleId = vehicleId;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(vehicleId, orientation));
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
var driver = (LocalPlayer)(Object)this;
|
||||
var vehicle = driver.getVehicle();
|
||||
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((LocalPlayer)(Object)this);
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
|
||||
// If the player has just switched to riding a custom orientation ride then set their
|
||||
// x and y rots local to the vehicle rots(if the controller wasn't active and will now be active)
|
||||
// This ensures that on transition your camera stays in the same spot
|
||||
if (!vehicleController.isActive() && shouldUseCustomOrientation) {
|
||||
// Set local to the vehicle x and yrot so
|
||||
var playerRotater = (RidePassenger)driver;
|
||||
playerRotater.cobblemon$setRideXRot(Mth.wrapDegrees(driver.getXRot() - vehicle.getXRot()));
|
||||
playerRotater.cobblemon$setRideYRot(Mth.wrapDegrees(driver.getYRot() - vehicle.getYRot()));
|
||||
}
|
||||
|
||||
// Look angles get seeded by the camera on its first rolling frame
|
||||
vehicleController.setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RideSteerInput;
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookInput;
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookSettings;
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookTake;
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.RidingFreelookBinding;
|
||||
@@ -20,13 +24,12 @@ 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 kotlin.Pair;
|
||||
import kotlin.Unit;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.MouseHandler;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.SmoothDouble;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -42,6 +45,11 @@ import static net.minecraft.util.Mth.lerp;
|
||||
|
||||
@Mixin(MouseHandler.class)
|
||||
public abstract class MouseHandlerMixin {
|
||||
// Entity.turn scales mouse movement to degrees by this
|
||||
@Unique private static final float COBBLEMON$VANILLA_DEGREES_PER_MOUSE_UNIT = 0.15F;
|
||||
// Share of the look offset closed per second as it drifts back to centre. Raise to recentre faster
|
||||
@Unique private static final float COBBLEMON$LOOK_RECENTER_RATE = 5.0F;
|
||||
|
||||
@Shadow private double accumulatedScrollY;
|
||||
@Shadow @Final private Minecraft minecraft;
|
||||
|
||||
@@ -160,74 +168,48 @@ public abstract class MouseHandlerMixin {
|
||||
}
|
||||
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
var freeLooking = RidingFreelookBinding.INSTANCE.isDown();
|
||||
Pair<Boolean, Boolean> mouseShouldModifyDriver = ((PokemonEntity) vehicle).ifRidingAvailableSupply(new Pair(false, false), (behaviour, settings, state) -> {
|
||||
return behaviour.mouseModifiesDriverRotation(
|
||||
settings,
|
||||
state,
|
||||
(PokemonEntity) vehicle
|
||||
);
|
||||
var pokemonVehicle = (PokemonEntity) vehicle;
|
||||
var playerRotater = (RidePassenger)player;
|
||||
float yawDelta = (float)cursorDeltaX * COBBLEMON$VANILLA_DEGREES_PER_MOUSE_UNIT;
|
||||
float pitchDelta = (float)cursorDeltaY * COBBLEMON$VANILLA_DEGREES_PER_MOUSE_UNIT;
|
||||
|
||||
// Passengers only ever look around
|
||||
if (player.getControlledVehicle() == null) {
|
||||
cobblemon$addLook(player, yawDelta, pitchDelta);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The ride takes what it wants of the mouse to steer with and the rest moves the look. Freelook keeps it all for the look
|
||||
var lookInput = new RideLookInput(yawDelta, pitchDelta, playerRotater.cobblemon$getRideYRot(), playerRotater.cobblemon$getRideXRot());
|
||||
RideLookTake take = RidingFreelookBinding.INSTANCE.isDown() ? RideLookTake.NONE : pokemonVehicle.ifRidingAvailableSupply(RideLookTake.all(lookInput), (behaviour, settings, state) -> {
|
||||
return behaviour.consumeLook(settings, state, pokemonVehicle, lookInput);
|
||||
});
|
||||
float lookYaw = yawDelta - take.getYaw();
|
||||
float lookPitch = pitchDelta - take.getPitch();
|
||||
if (lookYaw != 0.0f || lookPitch != 0.0f) {
|
||||
cobblemon$addLook(player, lookYaw, lookPitch);
|
||||
}
|
||||
|
||||
// Drift the look back to centre on the axes the ride steers with
|
||||
float recenter = min(COBBLEMON$LOOK_RECENTER_RATE * (float)movementTime, 1.0f);
|
||||
if (take.getRecenterYaw()) {
|
||||
playerRotater.cobblemon$setRideYRot(lerp(recenter, Mth.wrapDegrees(playerRotater.cobblemon$getRideYRot()), 0.0f));
|
||||
}
|
||||
if (take.getRecenterPitch()) {
|
||||
playerRotater.cobblemon$setRideXRot(lerp(recenter, Mth.wrapDegrees(playerRotater.cobblemon$getRideXRot()), 0.0f));
|
||||
}
|
||||
|
||||
// Hand the ride its share back in mouse units since that's what every ride's steering is tuned for
|
||||
double rideDeltaX = yawDelta == 0.0f ? 0.0 : cursorDeltaX * (take.getYaw() / yawDelta);
|
||||
double rideDeltaY = pitchDelta == 0.0f ? 0.0 : cursorDeltaY * (take.getPitch() / pitchDelta);
|
||||
var steerInput = cobblemon$getSteerInput(vehicleController, rideDeltaX, rideDeltaY, movementTime);
|
||||
pokemonVehicle.ifRidingAvailable((behaviour, settings, state) -> {
|
||||
behaviour.steer(settings, state, pokemonVehicle, player, steerInput);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// If looking around then modify the players orientation instead and zero the mouse deltas so the mouse doesn't
|
||||
// affect the vehicle orientation when looking around.
|
||||
// If the client is a passenger it will modify these rotations and return
|
||||
//-------------------------------------------------------------------------
|
||||
if (freeLooking || player.getControlledVehicle() == null) {
|
||||
// Modify driver rotations
|
||||
cobblemon$setDriverRotations(player, cursorDeltaX, cursorDeltaY);
|
||||
|
||||
// If the player/passenger is not the driver then return and ignore all vehicle rotation logic
|
||||
if (player.getControlledVehicle() == null) return false;
|
||||
|
||||
// Set to zero so that vehicle rotations don't also get affected by movement.
|
||||
cursorDeltaX = 0.0;
|
||||
cursorDeltaY = 0.0;
|
||||
} else if (mouseShouldModifyDriver.getFirst()) { // If not freelooking then check modification per mouse input dimension
|
||||
cobblemon$setDriverRotations(player, cursorDeltaX, 0.0);
|
||||
cursorDeltaX = 0.0;
|
||||
} else if (mouseShouldModifyDriver.getSecond()) {
|
||||
cobblemon$setDriverRotations(player, 0.0, cursorDeltaY);
|
||||
cursorDeltaY = 0.0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Smoothly return driver rotations to zero when needed
|
||||
//-------------------------------------------------------------------------
|
||||
var playerRotater = (RidePassenger)player;
|
||||
if( !freeLooking && !mouseShouldModifyDriver.getFirst()){
|
||||
playerRotater.cobblemon$setRideYRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideYRot()), 0.0f));
|
||||
}
|
||||
if( !freeLooking && !mouseShouldModifyDriver.getSecond()) {
|
||||
playerRotater.cobblemon$setRideXRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideXRot()), 0.0f));
|
||||
}
|
||||
|
||||
// Send mouse input to be interpreted into rotation deltas by the ride controller
|
||||
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
|
||||
|
||||
// Perform Rotation using mouse influenced rotation deltas.
|
||||
vehicleController.rotate(
|
||||
(float) angVecMouse.x,
|
||||
(float) angVecMouse.y,
|
||||
(float) angVecMouse.z
|
||||
);
|
||||
|
||||
// Gather and apply the current rotation deltas
|
||||
var angRot = cobblemon$getAngularVelocity(movementTime);
|
||||
|
||||
// Apply smoothing if requested by the controller.
|
||||
if(cobblemon$shouldUseAngVelSmoothing()) {
|
||||
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
|
||||
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
|
||||
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
|
||||
vehicleController.rotate((float) yaw, (float) pitch, (float) roll);
|
||||
}
|
||||
else {
|
||||
vehicleController.rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
// The camera sets the player's rotation from the look every frame so vanilla turn would only get overwritten
|
||||
return false;
|
||||
}
|
||||
|
||||
@Inject(method = "handleAccumulatedMovement", at = @At("HEAD"))
|
||||
@@ -248,28 +230,9 @@ public abstract class MouseHandlerMixin {
|
||||
this.turnPlayer(cobblemon$timeDelta);
|
||||
}
|
||||
|
||||
/** The ride's share of the mouse put through the riding sensitivity, invert and swap options. */
|
||||
@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.ifRidingAvailableSupply(Vec3.ZERO, (behaviour, settings, state) -> {
|
||||
return behaviour.angRollVel(settings, state, 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 pokemonEntity)) return Vec3.ZERO;
|
||||
|
||||
private RideSteerInput cobblemon$getSteerInput(OrientationController controller, double mouseX, double mouseY, double deltaTime) {
|
||||
var invertYaw = Cobblemon.config.getInvertYaw();
|
||||
var invertPitch = Cobblemon.config.getInvertPitch();
|
||||
|
||||
@@ -282,21 +245,18 @@ public abstract class MouseHandlerMixin {
|
||||
|
||||
var pitch = (swapXAndY ? xValue : yValue) * (invertPitch? -1.0f : 1.0f);
|
||||
|
||||
var sensitivity = cobblemon$getRidingSensitivity();
|
||||
return pokemonEntity.ifRidingAvailableSupply(Vec3.ZERO, (behaviour, settings, state) -> {
|
||||
return behaviour.rotationOnMouseXY(
|
||||
settings,
|
||||
state,
|
||||
pokemonEntity,
|
||||
player,
|
||||
pitch,
|
||||
yaw,
|
||||
yMouseSmoother,
|
||||
xMouseSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
);
|
||||
});
|
||||
return new RideSteerInput(
|
||||
controller,
|
||||
yaw,
|
||||
pitch,
|
||||
xMouseSmoother,
|
||||
yMouseSmoother,
|
||||
cobblemon$getRidingSensitivity(),
|
||||
deltaTime,
|
||||
yawSmoother,
|
||||
pitchSmoother,
|
||||
rollSmoother
|
||||
);
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -305,31 +265,33 @@ public abstract class MouseHandlerMixin {
|
||||
return Math.pow(sensitivity, 3);
|
||||
}
|
||||
|
||||
/** Moves the ride look by degrees within the ride's look limits. Same job as vanilla turn but on the look instead of the player. */
|
||||
@Unique
|
||||
private void cobblemon$setDriverRotations(LocalPlayer player, double cursorDeltaX, double cursorDeltaY) {
|
||||
// the vanilla turn() method redone so that it uses rideRotations.
|
||||
float f = (float)cursorDeltaY * 0.15F; // Note: turn() uses xRot for pitch (up/down)
|
||||
float g = (float)cursorDeltaX * 0.15F; // Note: turn() uses yRot for yaw (left/right)
|
||||
private void cobblemon$addLook(LocalPlayer player, float yawDelta, float pitchDelta) {
|
||||
var playerRotater = (RidePassenger)player;
|
||||
playerRotater.cobblemon$setRideXRot(playerRotater.cobblemon$getRideXRot() + f);
|
||||
playerRotater.cobblemon$setRideYRot(playerRotater.cobblemon$getRideYRot() + g);
|
||||
playerRotater.cobblemon$setRideXRot(Mth.clamp(playerRotater.cobblemon$getRideXRot(), -89.0F, 89.0F));
|
||||
playerRotater.cobblemon$setRideYRot(Mth.clamp(playerRotater.cobblemon$getRideYRot(), -105.0F, 105.0F));
|
||||
var lookSettings = cobblemon$getLookSettings(player);
|
||||
playerRotater.cobblemon$setRideXRot(cobblemon$softClamp(playerRotater.cobblemon$getRideXRot(), pitchDelta, lookSettings.getMaxPitch()));
|
||||
playerRotater.cobblemon$setRideYRot(cobblemon$softClamp(playerRotater.cobblemon$getRideYRot(), yawDelta, lookSettings.getMaxYaw()));
|
||||
if (player.getVehicle() != null) {
|
||||
player.getVehicle().onPassengerTurned(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves an angle by delta while keeping it inside the limit. An angle already past the limit
|
||||
* (the base turning under a low carry look can put it there) isn't snapped back, it just can't go further out.
|
||||
*/
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseAngVelSmoothing() {
|
||||
var player = minecraft.player;
|
||||
if (player == null) return true;
|
||||
private static float cobblemon$softClamp(float angle, float delta, float limit) {
|
||||
float bound = Math.max(limit, Math.abs(angle));
|
||||
return Mth.clamp(angle + delta, -bound, bound);
|
||||
}
|
||||
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return true;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
|
||||
return pokemonEntity.ifRidingAvailableSupply(true, (behaviour, settings, state) -> {
|
||||
return behaviour.useAngVelSmoothing(settings, state, pokemonEntity);
|
||||
@Unique
|
||||
private RideLookSettings cobblemon$getLookSettings(LocalPlayer player) {
|
||||
if (!(player.getVehicle() instanceof PokemonEntity pokemonEntity)) return RideLookSettings.DEFAULT;
|
||||
return pokemonEntity.ifRidingAvailableSupply(RideLookSettings.DEFAULT, (behaviour, settings, state) -> {
|
||||
return behaviour.lookSettings(settings, state, pokemonEntity);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
@Mixin(Entity.class)
|
||||
public abstract class EntityMixin {
|
||||
|
||||
// How far the clientsent ride eye can sit from the players
|
||||
@Unique private static final double COBBLEMON$MAX_RIDE_EYE_OFFSET = 3.0;
|
||||
|
||||
@Shadow @Nullable public abstract Entity getVehicle();
|
||||
|
||||
@Inject(method = "updateInWaterStateAndDoWaterCurrentPushing", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/Entity;wasTouchingWater:Z", ordinal = 1), cancellable = true)
|
||||
@@ -91,6 +94,9 @@ public abstract class EntityMixin {
|
||||
if (vehicleController == null) return;
|
||||
|
||||
Vec3 customEyePos = ridePassenger.cobblemon$getRideEyePos();
|
||||
Vec3 vanillaEyePos = new Vec3(player.getX(), player.getEyeY(), player.getZ());
|
||||
double maxSqr = COBBLEMON$MAX_RIDE_EYE_OFFSET * COBBLEMON$MAX_RIDE_EYE_OFFSET;
|
||||
if (customEyePos == null || !(customEyePos.distanceToSqr(vanillaEyePos) <= maxSqr)) return;
|
||||
cir.setReturnValue(customEyePos);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.HelicopterSetting
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BoardSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
|
||||
@@ -56,6 +57,7 @@ object CobblemonRideSettings : DataRegistry {
|
||||
var hover = HoverSettings()
|
||||
var jet = JetSettings()
|
||||
var rocket = RocketSettings()
|
||||
var board = BoardSettings()
|
||||
|
||||
var horse = HorseSettings()
|
||||
var minekart = MinekartSettings()
|
||||
@@ -75,6 +77,7 @@ object CobblemonRideSettings : DataRegistry {
|
||||
hover = hover,
|
||||
jet = jet,
|
||||
rocket = rocket,
|
||||
board = board,
|
||||
horse = horse,
|
||||
minekart = minekart,
|
||||
vehicle = vehicle,
|
||||
@@ -93,6 +96,7 @@ object CobblemonRideSettings : DataRegistry {
|
||||
hover = loadStyle(manager, "hover", HoverSettings::class.java)
|
||||
jet = loadStyle(manager, "jet", JetSettings::class.java)
|
||||
rocket = loadStyle(manager, "rocket", RocketSettings::class.java)
|
||||
board = loadStyle(manager, "board", BoardSettings::class.java)
|
||||
horse = loadStyle(manager, "horse", HorseSettings::class.java)
|
||||
minekart = loadStyle(manager, "minekart", MinekartSettings::class.java)
|
||||
vehicle = loadStyle(manager, "vehicle", VehicleSettings::class.java)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.orientation
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.math.QuatSpring
|
||||
import com.cobblemon.mod.common.util.math.geometry.toDegrees
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
@@ -29,7 +30,8 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
val UP: Vector3f = Vector3f(0.0f, 1.0f, 0.0f)
|
||||
val LEFT: Vector3f = Vector3f(-1.0f, 0.0f, 0.0f)
|
||||
private const val TICK_DT = 1f / 20f // Minecraft server tick rate
|
||||
private const val RENDER_SMOOTH_HALFLIFE = 0.1f // seconds to close ~half the gap
|
||||
// Seconds for the render orientation to close half the gap to the real one. Raise for smoother but laggier visuals
|
||||
private const val RENDER_SMOOTH_HALFLIFE = 0.15f
|
||||
}
|
||||
|
||||
open var active: Boolean = false
|
||||
@@ -101,7 +103,11 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
// Use a "quat spring" to smoothly move the render orientation towards the goal orientation.
|
||||
// It takes into account the orientations angular velocities and should be much more resilient
|
||||
// to sharp changes in goal orientation or late/missed packets.
|
||||
simpleSpringDamperQuat(renderSpring, targetQuat, 0.15f, TICK_DT)
|
||||
val pokemon = entity as? PokemonEntity
|
||||
val halflife = pokemon?.ifRidingAvailableSupply(fallback = null as Float?) { behaviour, settings, state ->
|
||||
behaviour.renderHalflife(settings, state, pokemon)
|
||||
} ?: RENDER_SMOOTH_HALFLIFE
|
||||
simpleSpringDamperQuat(renderSpring, targetQuat, halflife, TICK_DT)
|
||||
|
||||
val newRenderOrientation = Matrix3f()
|
||||
renderSpring.quat.get(newRenderOrientation)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* One frame of steering for a rolling ride on the client that's driving it.
|
||||
* The mouse values are the share the ride took in consumeLook, already through the riding
|
||||
* sensitivity, invert and swap options and still in mouse units.
|
||||
*/
|
||||
class RideSteerInput(
|
||||
/** The ride's own orientation controller */
|
||||
val controller: OrientationController,
|
||||
val mouseYaw: Double,
|
||||
val mousePitch: Double,
|
||||
val mouseXSmoother: SmoothDouble,
|
||||
val mouseYSmoother: SmoothDouble,
|
||||
/** Vanilla's mouse sensitivity curve, cubed */
|
||||
val sensitivity: Double,
|
||||
/** Seconds since the last frame */
|
||||
val deltaTime: Double,
|
||||
private val yawSmoother: SmoothDouble,
|
||||
private val pitchSmoother: SmoothDouble,
|
||||
private val rollSmoother: SmoothDouble
|
||||
) {
|
||||
companion object {
|
||||
// Share of each push the smoothers get. Lower makes smoothed turning softer
|
||||
const val SMOOTHED_SHARE = 0.5
|
||||
}
|
||||
|
||||
/** Turns the ride by local yaw, pitch and roll degrees right away. */
|
||||
fun rotate(rotation: Vec3) {
|
||||
controller.rotate(rotation.x.toFloat(), rotation.y.toFloat(), rotation.z.toFloat())
|
||||
}
|
||||
|
||||
/** Turns the ride by local yaw, pitch and roll degrees eased through vanilla's mouse smoother so it spreads over a few frames. */
|
||||
fun rotateSmoothed(rotation: Vec3) {
|
||||
controller.rotate(
|
||||
yawSmoother.getNewDeltaValue(rotation.x * SMOOTHED_SHARE, deltaTime).toFloat(),
|
||||
pitchSmoother.getNewDeltaValue(rotation.y * SMOOTHED_SHARE, deltaTime).toFloat(),
|
||||
rollSmoother.getNewDeltaValue(rotation.z * SMOOTHED_SHARE, deltaTime).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,21 @@ import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideCameraContext
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookInput
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookTake
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookSettings
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
/**
|
||||
* Represents the behaviour of a Pokemon when being ridden.
|
||||
@@ -41,30 +47,42 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
|
||||
fun pose(settings: Settings, state: State, vehicle: PokemonEntity): PoseType
|
||||
|
||||
/** Ride speed for vanilla's setSpeed. Only reads state, anything that changes state goes in [driverTick]. */
|
||||
fun speed(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float
|
||||
|
||||
/**
|
||||
* Runs once a tick only on the client that's steering, after [tick] and the ride's rotation.
|
||||
* Whatever it works out reaches the server and other players through the riding state sync.
|
||||
*/
|
||||
fun driverTick(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player) {}
|
||||
|
||||
fun clampPassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {}
|
||||
|
||||
fun updatePassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {}
|
||||
|
||||
fun rotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity): Vec2
|
||||
|
||||
/**
|
||||
* Ride velocity for this tick in world space blocks per tick.
|
||||
* [RideFrame.yawToWorld] turns a velocity worked out along the ride's yaw into world space.
|
||||
*/
|
||||
fun velocity(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: Vec3): Vec3
|
||||
|
||||
fun angRollVel(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, deltaTime: Double): Vec3
|
||||
/**
|
||||
* True to have the driver's client call [velocity] a second time each tick inside travel.
|
||||
* Vanilla already asks for it once in getRiddenInput and travel always asked again, so every ride's
|
||||
* numbers are tuned around running twice a tick. Return false once a ride is retuned for a single run.
|
||||
*/
|
||||
fun velocityRunsTwicePerTick(settings: Settings, state: State, vehicle: PokemonEntity): Boolean = true
|
||||
|
||||
fun rotationOnMouseXY(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3
|
||||
/** Moves the ride itself this tick instead of the shared velocity and inertia. True if it did */
|
||||
fun travel(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean = false
|
||||
|
||||
/**
|
||||
* Turns the ride for one frame while it rolls. Runs every frame on the client that's driving with the mouse
|
||||
* share the ride took in [consumeLook]. Rotate through [RideSteerInput.controller] or its helpers.
|
||||
*/
|
||||
fun steer(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: RideSteerInput) {}
|
||||
|
||||
fun canJump(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean
|
||||
|
||||
@@ -76,8 +94,6 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
|
||||
fun rideFovMultiplier(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float
|
||||
|
||||
fun useAngVelSmoothing(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
|
||||
fun inertia(settings: Settings, state: State, vehicle: PokemonEntity): Double
|
||||
|
||||
fun shouldRoll(settings: Settings, state: State, vehicle: PokemonEntity): Boolean
|
||||
@@ -111,10 +127,28 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
fun applyRenderRotation(settings: Settings, state: State, vehicle: PokemonEntity, partialTicks: Float) {}
|
||||
|
||||
/**
|
||||
* Determines if the pair of mouse inputs Pair(mouseX, mouseY) get used to modify the driverRotations or the vehicle rotations themselves.
|
||||
* Driver rotations are the rotations used by passengers or a driver that is freelooking. This does not override the freelook button.
|
||||
* How much of this frame's mouse look the ride takes to steer with. What it takes goes through
|
||||
* [steer] and the rest moves the rider's look. Only asked for the driver and never while freelooking.
|
||||
*/
|
||||
fun mouseModifiesDriverRotation(settings: Settings, state: State, vehicle: PokemonEntity): Pair<Boolean, Boolean> = Pair(false, false)
|
||||
fun consumeLook(settings: Settings, state: State, vehicle: PokemonEntity, input: RideLookInput): RideLookTake = RideLookTake.all(input)
|
||||
|
||||
/**
|
||||
* Rotation the rider's camera hangs off while the ride rolls. Called every frame on the client
|
||||
* and the rider's look sits on top of it. The player's disableRoll setting still applies after this.
|
||||
*/
|
||||
fun cameraBase(settings: Settings, state: State, vehicle: PokemonEntity, context: RideCameraContext): Quaternionf = context.defaultBase()
|
||||
|
||||
/** Degrees to roll the first person hands and held items */
|
||||
fun firstPersonHandRoll(settings: Settings, state: State, vehicle: PokemonEntity, context: RideCameraContext): Float = 0f
|
||||
|
||||
/** Blocks to shift the model and its locators by once [orientation] has turned it, so a ride can keep its model on what it's standing on */
|
||||
fun renderOffset(settings: Settings, state: State, vehicle: PokemonEntity, orientation: Quaternionf): Vector3f = Vector3f()
|
||||
|
||||
/** Seconds the drawn orientation takes to close half the gap to the real one. Null keeps the controller's default */
|
||||
fun renderHalflife(settings: Settings, state: State, vehicle: PokemonEntity): Float? = null
|
||||
|
||||
/** Look limits and carry for riders while the ride rolls. */
|
||||
fun lookSettings(settings: Settings, state: State, vehicle: PokemonEntity): RideLookSettings = RideLookSettings.DEFAULT
|
||||
|
||||
/**
|
||||
* Used in MoLang functions for querying states for animations or other magical MoLang effects.
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.ClimbBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
|
||||
@@ -35,7 +36,9 @@ object RidingBehaviours {
|
||||
register(MinekartBehaviour.KEY, MinekartBehaviour())
|
||||
register(HoverBehaviour.KEY, HoverBehaviour())
|
||||
register(RocketBehaviour.KEY, RocketBehaviour())
|
||||
register(BoardBehaviour.KEY, BoardBehaviour())
|
||||
register(SubmarineBehaviour.KEY, SubmarineBehaviour())
|
||||
register(ClimbBehaviour.KEY, ClimbBehaviour())
|
||||
register(CompositeBehaviour.KEY, CompositeBehaviour())
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,13 @@ import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController.Companion.FORWARDS
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController.Companion.UP
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideCameraContext
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookInput
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookTake
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
@@ -24,7 +29,9 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.FloatSpring
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.math.simpleSpringDamper
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -34,6 +41,8 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
|
||||
@@ -42,6 +51,28 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val KEY = cobblemonResource("air/bird")
|
||||
|
||||
private const val FLY_POSE_SPEED = 0.1
|
||||
// At or below this the bird hovers instead of flying
|
||||
private const val HOVER_SPEED = 0.1
|
||||
// Within this many degrees of straight up or down wings level means nothing so the camera holds its roll
|
||||
private const val VERTICAL_DEADZONE_DEGREES = 15f
|
||||
private val MIN_LEVEL_UP_LENGTH_SQ = sin(VERTICAL_DEADZONE_DEGREES.toRadians()).pow(2)
|
||||
// Below this speed the bird is too slow to bank so its wings always level out
|
||||
private const val NO_BANK_SPEED = 0.2
|
||||
// Degrees before the max roll where rolling further starts easing off. Raise for a softer stop
|
||||
private const val ROLL_CAP_BAND_DEGREES = 20f
|
||||
// Degrees past the turn roll limit over which mouse pitch hands over from world up and down to the bird's own pitch
|
||||
private const val PITCH_BLEND_BAND_DEGREES = 15f
|
||||
// Pitch where mouse pitch starts handing back to the bird's own pitch. By straight up or down it's all the bird's
|
||||
// own so a loop carries on over the top instead of world up pulling it back
|
||||
private const val WORLD_PITCH_FADE_START = 60f
|
||||
// Fastest the wings level out by themselves in degrees per second
|
||||
private const val MAX_ROLL_SETTLE_RATE = 45f
|
||||
// Within this many degrees of level the settle rate eases off so it lands on level instead of overshooting
|
||||
private const val ROLL_SETTLE_ARRIVAL_DEGREES = 35f
|
||||
// How fast the settle rate can change in degrees per second squared. Same as the old per frame cap at 60 fps
|
||||
private const val ROLL_SETTLE_ACCEL = 67.5f
|
||||
// Keeps the turn roll limit from dividing by zero
|
||||
private const val MIN_TURN_ROLL_LIMIT = 1f
|
||||
private const val GLIDE_ENTRY_SPEED = 0.19
|
||||
private const val GLIDE_EXIT_SPEED = 0.13
|
||||
}
|
||||
@@ -84,25 +115,17 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
checkUpsideDown(state, vehicle, driver)
|
||||
checkStaminaDive(state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUpsideDown(
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
) {
|
||||
// Also checks stamina
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
|
||||
} else if (state.stamina.get() == 0.0f) { // checks stamina depletion and forces dive if depleted
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 1 seconds to max out
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate))
|
||||
} else { // Its right side up or has stamina so reset it
|
||||
state.currUpsideDownForce.set(0.0)
|
||||
// Out of stamina the bird can't hold itself up so it builds up a dive
|
||||
private fun checkStaminaDive(state: BirdState) {
|
||||
if (state.stamina.get() == 0.0f) {
|
||||
val diveRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out
|
||||
state.staminaDiveForce.set(min(1.0, state.staminaDiveForce.get() + diveRate))
|
||||
} else {
|
||||
state.staminaDiveForce.set(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +184,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
//Perform ride velocity update
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
// The downward force used to encourage players to stop flying upside down.
|
||||
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
|
||||
// Pulls the bird down once it's out of stamina
|
||||
val extraDownwardForce = state.staminaDiveForce.get().pow(2) * -0.3 // 6 blocks a second downward
|
||||
|
||||
// Convert the local velocity vector into a world vector
|
||||
val localVelVec = Vector3f(
|
||||
@@ -170,8 +193,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
(state.rideVelocity.get().y).toFloat(),
|
||||
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed
|
||||
)
|
||||
// Unrotate preemptively as this vector gets rotatee later down the line in MC logic.
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians())
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d()
|
||||
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
|
||||
|
||||
return worldVelVec
|
||||
@@ -282,19 +304,23 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
state.rideVelocity.set(newVelocity)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
private fun rotateOverTime(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
) {
|
||||
if (vehicle !is OrientationControllable) return
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
|
||||
|
||||
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble()))
|
||||
// Turn rate tops out at the turn roll limit. Banking further is for show and doesn't turn any harder
|
||||
val turnRollLimit = frameTuning(settings, state, vehicle).turnRollLimit.coerceIn(MIN_TURN_ROLL_LIMIT, 90f)
|
||||
val bankFromLevel = if (abs(controller.roll) > 90f) 180f - abs(controller.roll) else abs(controller.roll)
|
||||
val turnShare = sin(min(bankFromLevel, turnRollLimit).toRadians()) / sin(turnRollLimit.toRadians()) * controller.roll.sign
|
||||
val yawDeltaDeg = deltaTime * handling * turnShare
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))).coerceIn(-1.0, 1.0)
|
||||
|
||||
// Dampen yaw when upside down
|
||||
@@ -309,8 +335,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
controller.applyGlobalPitch(pCorr)
|
||||
controller.rotateRoll(rCorr)
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
@@ -320,45 +344,28 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
controller: OrientationController,
|
||||
deltaTime: Double
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!)
|
||||
// If the ride is moving then have the correction rate be an equal proportion of:
|
||||
// - how rolled are you
|
||||
// - how long has it been since last mouse input (higher is stronger correction)
|
||||
// Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
|
||||
val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
|
||||
val maxRollCorrectionRate =
|
||||
if (state.rideVelocity.get().length() < 0.2) 45.0f
|
||||
else 45.0f * cos(controller.pitch.toRadians()).pow(2) * (
|
||||
howRolledAmI*1.0f +
|
||||
(state.noInputTimeRoll.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
|
||||
)
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0f
|
||||
val rollArrivalDeg = 35.0f
|
||||
val desiredRoll = 0.0f
|
||||
|
||||
// Calculate the signed error (how far and in what direction)
|
||||
val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
|
||||
// Use the absolute error to calculate the magnitude of the influence
|
||||
val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
|
||||
val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
// Wings only level out by themselves when the rider has let go and they're nearly level already,
|
||||
// or when the bird's too slow to bank at all. A bank the rider holds stays where they put it
|
||||
val tuning = frameTuning(settings, state, vehicle)
|
||||
val idle = state.noInputTimeRoll.get() >= tuning.rollSettleDelay
|
||||
val settling = state.rideVelocity.get().length() < NO_BANK_SPEED || (idle && abs(controller.roll) < tuning.rollSettleAngle)
|
||||
val rollError = Mth.wrapDegrees(0.0f.angleDifference(controller.roll))
|
||||
val desiredRollRate = if (settling) {
|
||||
MAX_ROLL_SETTLE_RATE * (min(ROLL_SETTLE_ARRIVAL_DEGREES, abs(rollError)) / ROLL_SETTLE_ARRIVAL_DEGREES) * -rollError.sign
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
val maxRateChange = ROLL_SETTLE_ACCEL * deltaTime
|
||||
val currentRollRate = state.currRollCorrectionForce.get()
|
||||
state.currRollCorrectionForce.set(currentRollRate + (desiredRollRate - currentRollRate).coerceIn(-maxRateChange, maxRateChange))
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 15.0f * state.currUpsideDownForce.get().toFloat() else 30.0f
|
||||
val maxPitchCorrectionRate = if (state.staminaDiveForce.get() != 0.0) 15.0f * state.staminaDiveForce.get().toFloat() else 30.0f
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0f
|
||||
val pitchArrivalDeg = 35.0f
|
||||
val desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0f else 0.0f
|
||||
val desiredPitch = if (state.staminaDiveForce.get() != 0.0) 90.0f else 0.0f
|
||||
|
||||
if((state.rideVelocity.get().length() < 0.1) || state.currUpsideDownForce.get() != 0.0) {
|
||||
if((state.rideVelocity.get().length() < 0.1) || state.staminaDiveForce.get() != 0.0) {
|
||||
val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
|
||||
@@ -381,7 +388,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
override fun steer(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: RideSteerInput
|
||||
) {
|
||||
input.rotate(mouseRotation(settings, state, vehicle, driver, input.mousePitch, input.mouseYaw, input.mouseYSmoother, input.mouseXSmoother, input.sensitivity, input.deltaTime))
|
||||
rotateOverTime(settings, state, vehicle, driver, input.deltaTime)
|
||||
}
|
||||
|
||||
private fun mouseRotation(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -394,15 +412,10 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
// Begin the roll correction time counter if not enough mouse input
|
||||
// is detected
|
||||
if (mouseX < 1 && abs(mouseY) < 1) {
|
||||
// Time since the rider last moved the mouse, wings only settle once it passes the settle delay
|
||||
if (abs(mouseX) < 1 && abs(mouseY) < 1) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime)
|
||||
} else if (cos(controller.roll.toRadians()) < 0.1) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime * 0.5)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
state.noInputTimeRoll.set(0.0)
|
||||
}
|
||||
|
||||
@@ -443,46 +456,42 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
|
||||
// Applied to mouseX to prevent turning upside down when trying to roll hard.
|
||||
val rollDegreeDebuff = if(mouseX.sign.toFloat() == controller.roll.sign) 1 - 0.9f*sqrt(abs(sin(controller.roll.toRadians())))*sqrt(abs(cos(controller.pitch))) else 1.0f
|
||||
val tuning = frameTuning(settings, state, vehicle)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 2.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult * rollDegreeDebuff
|
||||
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
|
||||
// of being upside down
|
||||
val rolledOverPitchCorrection = -8*sqrt(abs(min(cos(controller.roll.toRadians()),0.0f))) * (cos(controller.pitch.toRadians())).pow(2)
|
||||
val mouseYc = ((mouseY).coerceIn(-45.0, 45.0) * mouseInputMult) + rolledOverPitchCorrection
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed)
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed)
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
|
||||
val hoverSpeed = 0.1
|
||||
val hoverSpeed = HOVER_SPEED
|
||||
val pitchDamping = abs(cos(controller.pitch.toRadians())).pow(2) // Helper value used when wanting to discard the influence of another value at steep pitches
|
||||
|
||||
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
|
||||
val globalYaw = xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
controller.applyGlobalYaw(globalYaw.toFloat())
|
||||
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
// - Do not pitch at all when at or below hover speed
|
||||
// - pitch faster at higher speeds
|
||||
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalBird.horizontalPitchExpr!!)
|
||||
var localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
|
||||
localPitch *= if (currSpeed > hoverSpeed || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
|
||||
// No pitching at hover speed and faster pitching the faster the bird goes
|
||||
val pitchInput = yInput * if (currSpeed > hoverSpeed || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
|
||||
else 0.0
|
||||
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val pitchInfluencedRollCorrection = 0.0
|
||||
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2) //Coerce into how much roll you want to let in in the deadzone
|
||||
// Inside a normal bank mouse pitch is world up and down so pulling up climbs instead of tightening the turn.
|
||||
// Past the turn roll limit, or pointing near straight up or down, it hands over to the bird's own pitch
|
||||
val pastTurnRoll = smoothstep01((abs(controller.roll) - tuning.turnRollLimit) / PITCH_BLEND_BAND_DEGREES)
|
||||
val nearVertical = smoothstep01((abs(controller.pitch) - WORLD_PITCH_FADE_START) / (90f - WORLD_PITCH_FADE_START))
|
||||
val localShare = 1f - (1f - pastTurnRoll) * (1f - nearVertical)
|
||||
controller.applyGlobalPitch((pitchInput * (1f - localShare)).toFloat())
|
||||
|
||||
// Pulling with the bird's own pitch while rolled turns hard so it's held back by handling there
|
||||
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalBird.horizontalPitchExpr!!)
|
||||
val localPitch = pitchInput * localShare * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
|
||||
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower
|
||||
val isInputtingTowardsRoll = if ( xInput.sign == controller.roll.sign.toDouble()) 1.0 else 0.0
|
||||
val inputInRolledDir = if (isInputtingTowardsRoll == 1.0) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
|
||||
val rollDampen = (1 - howRolledAmI.coerceIn(0.0f, 0.7f) * pitchDamping * inputInRolledDir)
|
||||
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
|
||||
else 0.0
|
||||
val rollInput = xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0))
|
||||
val rollForce = if (currHorzSpeed > hoverSpeed) capRoll(rollInput, controller.roll, tuning.maxRoll) else 0.0
|
||||
|
||||
// When already rolled significantly part of the roll force should turn to local yaw force
|
||||
val localYawForce = rollForce * abs(sin(controller.roll.toRadians())).pow(2) * pitchDamping * isInputtingTowardsRoll
|
||||
@@ -546,14 +555,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
@@ -607,6 +608,139 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
|
||||
override fun createDefaultState(settings: BirdSettings) = BirdState()
|
||||
|
||||
/**
|
||||
* Keeps the camera level until the bird rolls past the engage angle, then eases it into the bird's roll.
|
||||
* It levels out again once the bird comes back under the release angle. Yaw and pitch always follow the bird.
|
||||
*/
|
||||
override fun cameraBase(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
context: RideCameraContext
|
||||
): Quaternionf {
|
||||
val ride = context.defaultBase()
|
||||
val tuning = frameTuning(settings, state, vehicle)
|
||||
val roll = context.data as? BirdCameraRoll ?: BirdCameraRoll().also { context.data = it }
|
||||
|
||||
val forward = FORWARDS.rotate(ride, Vector3f())
|
||||
val up = UP.rotate(ride, Vector3f())
|
||||
|
||||
// Up with wings level. Pointing near straight up or down it flips sides so it's only used away from vertical
|
||||
val levelUp = Vector3f(0f, 1f, 0f).sub(Vector3f(forward).mul(forward.y))
|
||||
val hasLevel = levelUp.lengthSquared() > MIN_LEVEL_UP_LENGTH_SQ
|
||||
if (hasLevel) {
|
||||
levelUp.normalize()
|
||||
val rideRoll = signedAngle(levelUp, up, forward)
|
||||
if (roll.engaged && abs(rideRoll) < tuning.rollRelease) {
|
||||
roll.engaged = false
|
||||
} else if (!roll.engaged && abs(rideRoll) > tuning.rollEngage) {
|
||||
roll.engaged = true
|
||||
}
|
||||
}
|
||||
|
||||
// Camera up lives in world space so the level up flipping over the top of a loop can't drag the camera round
|
||||
val cameraUp = roll.cameraUp?.let { flatten(it, forward) } ?: Vector3f(up)
|
||||
val target = when {
|
||||
roll.engaged -> up
|
||||
hasLevel -> levelUp
|
||||
else -> Vector3f(cameraUp)
|
||||
}
|
||||
|
||||
if (roll.cameraUp == null) {
|
||||
cameraUp.set(target)
|
||||
} else if (context.frameSeconds > 0f) {
|
||||
// Spring the leftover angle so it builds up speed and settles instead of lunging at the start
|
||||
roll.spring.value = signedAngle(target, cameraUp, forward)
|
||||
simpleSpringDamper(roll.spring, 0f, tuning.rollHalflife, context.frameSeconds)
|
||||
cameraUp.set(target).rotateAxis(roll.spring.value * Mth.DEG_TO_RAD, forward.x, forward.y, forward.z)
|
||||
}
|
||||
roll.cameraUp = cameraUp
|
||||
|
||||
return levelFrame(forward, cameraUp)
|
||||
}
|
||||
|
||||
override fun firstPersonHandRoll(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
context: RideCameraContext
|
||||
): Float {
|
||||
return context.rideRollAroundView() * frameTuning(settings, state, vehicle).handRollShare
|
||||
}
|
||||
|
||||
// Hovering the rider can look up and down freely and around up to the hover limit. Only pushing past it turns the bird
|
||||
override fun consumeLook(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
input: RideLookInput
|
||||
): RideLookTake {
|
||||
// Only forward speed counts so rising or sinking on the spot is still hovering
|
||||
if (state.rideVelocity.get().horizontalDistance() > HOVER_SPEED) return RideLookTake.all(input)
|
||||
val limit = frameTuning(settings, state, vehicle).hoverLookYawLimit
|
||||
// Never snap a look that's already past the limit, it just can't be pushed further out
|
||||
val bound = max(limit, abs(input.yaw))
|
||||
val target = input.yaw + input.yawDelta
|
||||
val pushed = target - target.coerceIn(-bound, bound)
|
||||
return RideLookTake(pushed, 0f, recenterYaw = false, recenterPitch = false)
|
||||
}
|
||||
|
||||
/** Knobs the bird reads every frame, resolved at most once a tick so MoLang doesn't run per frame. */
|
||||
private fun frameTuning(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): BirdTuning {
|
||||
val cached = state.frameTuning.get()
|
||||
if (cached != null && cached.tick == vehicle.tickCount) return cached
|
||||
val runtime = vehicle.runtime
|
||||
return BirdTuning(
|
||||
tick = vehicle.tickCount,
|
||||
rollEngage = runtime.resolveFloat(settings.cameraRollEngage ?: globalBird.cameraRollEngage!!),
|
||||
rollRelease = runtime.resolveFloat(settings.cameraRollRelease ?: globalBird.cameraRollRelease!!),
|
||||
rollHalflife = runtime.resolveFloat(settings.cameraRollHalflife ?: globalBird.cameraRollHalflife!!),
|
||||
hoverLookYawLimit = runtime.resolveFloat(settings.hoverLookYawLimit ?: globalBird.hoverLookYawLimit!!),
|
||||
turnRollLimit = runtime.resolveFloat(settings.turnRollLimit ?: globalBird.turnRollLimit!!),
|
||||
maxRoll = runtime.resolveFloat(settings.maxRoll ?: globalBird.maxRoll!!),
|
||||
rollSettleAngle = runtime.resolveFloat(settings.rollSettleAngle ?: globalBird.rollSettleAngle!!),
|
||||
rollSettleDelay = runtime.resolveFloat(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!),
|
||||
handRollShare = runtime.resolveFloat(settings.handRollShare ?: globalBird.handRollShare!!)
|
||||
).also { state.frameTuning.set(it) }
|
||||
}
|
||||
|
||||
/** Smoothstep of [t] clamped to 0 to 1. */
|
||||
private fun smoothstep01(t: Float): Float {
|
||||
val x = t.coerceIn(0f, 1f)
|
||||
return x * x * (3f - 2f * x)
|
||||
}
|
||||
|
||||
/**
|
||||
* Eases roll that would take the bird further from level to a stop at [maxRoll] degrees.
|
||||
* Rolling back toward level is never held back.
|
||||
*/
|
||||
private fun capRoll(rollDelta: Double, roll: Float, maxRoll: Float): Double {
|
||||
if (rollDelta.sign != roll.sign.toDouble()) return rollDelta
|
||||
val room = (maxRoll - abs(roll)).coerceAtLeast(0f)
|
||||
val ease = (room / ROLL_CAP_BAND_DEGREES).coerceIn(0f, 1f)
|
||||
val eased = rollDelta * ease * ease * (3f - 2f * ease)
|
||||
// Never step past the cap in one frame
|
||||
return eased.coerceIn(-room.toDouble(), room.toDouble())
|
||||
}
|
||||
|
||||
/** Signed degrees from [from] to [to] turning around [axis]. Both have to be perpendicular to it. */
|
||||
private fun signedAngle(from: Vector3f, to: Vector3f, axis: Vector3f): Float {
|
||||
return atan2(Vector3f(from).cross(to).dot(axis), from.dot(to)) * Mth.RAD_TO_DEG
|
||||
}
|
||||
|
||||
/** [vector] with the part along [axis] removed, or null if nothing is left. */
|
||||
private fun flatten(vector: Vector3f, axis: Vector3f): Vector3f? {
|
||||
val flat = Vector3f(vector).sub(Vector3f(axis).mul(vector.dot(axis)))
|
||||
return if (flat.lengthSquared() > 0f) flat.normalize() else null
|
||||
}
|
||||
|
||||
/** Rotation whose forward is [forward] and up is [up]. They have to be unit length and perpendicular. */
|
||||
private fun levelFrame(forward: Vector3f, up: Vector3f): Quaternionf {
|
||||
val right = Vector3f(forward).cross(up)
|
||||
val back = Vector3f(forward).negate()
|
||||
return Quaternionf().setFromNormalized(Matrix3f(right, up, back))
|
||||
}
|
||||
|
||||
override fun asMoLangValue(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
@@ -659,6 +793,39 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
// This is coerced between 1 and 0 or things would get weird.
|
||||
var stamDrainPerRateDive: Expression? = null
|
||||
private set
|
||||
// Degrees of roll before the camera starts rolling with the bird
|
||||
var cameraRollEngage: Expression? = null
|
||||
private set
|
||||
|
||||
// Degrees the bird has to roll back under before the camera levels out. Keep it below engage so it doesn't flicker
|
||||
var cameraRollRelease: Expression? = null
|
||||
private set
|
||||
|
||||
// Seconds the camera roll spring takes to settle. Raise for a lazier camera
|
||||
var cameraRollHalflife: Expression? = null
|
||||
private set
|
||||
|
||||
// Degrees either side the rider can look while hovering before pushing further turns the bird
|
||||
var hoverLookYawLimit: Expression? = null
|
||||
private set
|
||||
|
||||
// Roll in degrees where turning tops out and mouse pitch stops being world up and down
|
||||
var turnRollLimit: Expression? = null
|
||||
private set
|
||||
|
||||
// Furthest the bird can roll from level in degrees. Keep cameraRollEngage just above it so the camera
|
||||
// stays level through normal flying and only rolls in loops and flips. 180 lets it roll all the way over
|
||||
var maxRoll: Expression? = null
|
||||
private set
|
||||
|
||||
// Within this many degrees of level the wings settle back to level once the rider lets go of the mouse
|
||||
var rollSettleAngle: Expression? = null
|
||||
private set
|
||||
|
||||
// Share of the bird's roll the first person hands lean with, 0 to 1
|
||||
var handRollShare: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -677,6 +844,14 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
buffer.writeNullableExpression(stamDrainPercRateGlide)
|
||||
buffer.writeNullableExpression(stamDrainPerRateDive)
|
||||
buffer.writeNullableExpression(timeToRollCorrect)
|
||||
buffer.writeNullableExpression(cameraRollEngage)
|
||||
buffer.writeNullableExpression(cameraRollRelease)
|
||||
buffer.writeNullableExpression(cameraRollHalflife)
|
||||
buffer.writeNullableExpression(hoverLookYawLimit)
|
||||
buffer.writeNullableExpression(turnRollLimit)
|
||||
buffer.writeNullableExpression(maxRoll)
|
||||
buffer.writeNullableExpression(rollSettleAngle)
|
||||
buffer.writeNullableExpression(handRollShare)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -695,6 +870,14 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
stamDrainPercRateGlide = buffer.readNullableExpression()
|
||||
stamDrainPerRateDive = buffer.readNullableExpression()
|
||||
timeToRollCorrect = buffer.readNullableExpression()
|
||||
cameraRollEngage = buffer.readNullableExpression()
|
||||
cameraRollRelease = buffer.readNullableExpression()
|
||||
cameraRollHalflife = buffer.readNullableExpression()
|
||||
hoverLookYawLimit = buffer.readNullableExpression()
|
||||
turnRollLimit = buffer.readNullableExpression()
|
||||
maxRoll = buffer.readNullableExpression()
|
||||
rollSettleAngle = buffer.readNullableExpression()
|
||||
handRollShare = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,7 +888,9 @@ class BirdState : RidingBehaviourState() {
|
||||
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var noInputTimeRoll = ridingState(0.0, Side.CLIENT)
|
||||
var noInputTimePitch = ridingState(0.0, Side.CLIENT)
|
||||
var currUpsideDownForce = ridingState(0.0, Side.CLIENT)
|
||||
var staminaDiveForce = ridingState(0.0, Side.CLIENT)
|
||||
// Client only cache of the knobs read every frame, never synced
|
||||
var frameTuning = ridingState<BirdTuning?>(null, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
@@ -725,6 +910,7 @@ class BirdState : RidingBehaviourState() {
|
||||
currPitchCorrectionForce.set(0.0, forced = true)
|
||||
noInputTimeRoll.set(0.0, forced = true)
|
||||
noInputTimePitch.set(0.0, forced = true)
|
||||
frameTuning.set(null, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -761,3 +947,26 @@ fun Float.angleDifference(other: Float): Float {
|
||||
while (diff > 180F) diff -= 360F
|
||||
return diff
|
||||
}
|
||||
|
||||
/** Bird knobs resolved for one tick. */
|
||||
data class BirdTuning(
|
||||
val tick: Int,
|
||||
val rollEngage: Float,
|
||||
val rollRelease: Float,
|
||||
val rollHalflife: Float,
|
||||
val hoverLookYawLimit: Float,
|
||||
val turnRollLimit: Float,
|
||||
val maxRoll: Float,
|
||||
val rollSettleAngle: Float,
|
||||
val rollSettleDelay: Float,
|
||||
val handRollShare: Float
|
||||
)
|
||||
|
||||
/** Bird's camera roll smoothing, kept in the camera context between frames. */
|
||||
class BirdCameraRoll {
|
||||
var engaged = false
|
||||
// World space up the camera had last frame, null until the first frame
|
||||
var cameraUp: Vector3f? = null
|
||||
// Degrees left between the camera and where it's heading, plus how fast that's closing
|
||||
val spring = FloatSpring()
|
||||
}
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
@@ -19,11 +18,11 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
@@ -67,7 +66,18 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
return Vec2(driver.xRot, driver.yRot)
|
||||
}
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -80,34 +90,6 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
return Vec3(xVector, yVector, zVector)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: GliderSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
return Vec3(0.0, mouseY, mouseX)
|
||||
}
|
||||
|
||||
override fun canJump(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
return false
|
||||
}
|
||||
@@ -149,10 +131,6 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RideSteerInput
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
@@ -20,6 +21,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -91,12 +93,23 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
|
||||
@@ -120,7 +133,7 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
return Vec3(xVel, yVel, zVel)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
private fun rotationOverTime(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -175,7 +188,18 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
override fun steer(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: RideSteerInput
|
||||
) {
|
||||
input.rotate(mouseRotation(settings, state, vehicle, driver, input.mousePitch, input.mouseYaw, input.mouseYSmoother, input.mouseXSmoother, input.sensitivity, input.deltaTime))
|
||||
input.rotateSmoothed(rotationOverTime(settings, state, vehicle, driver, input.deltaTime))
|
||||
}
|
||||
|
||||
private fun mouseRotation(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -241,10 +265,6 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun inertia(settings: HelicopterSettings, state: RidingBehaviourState, vehicle: PokemonEntity): Double {
|
||||
return 0.1
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -26,7 +27,6 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
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
|
||||
import net.minecraft.world.level.ClipContext
|
||||
@@ -190,12 +190,23 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
return rotAmount
|
||||
}
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val retVel = calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
state.rideVelocity.set(retVel)
|
||||
@@ -283,31 +294,6 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
@@ -355,14 +341,6 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
return if (state.isBoosting.get()) 1.1f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RideSteerInput
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
@@ -28,7 +29,6 @@ import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
@@ -176,7 +176,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
(state.rideVelocity.get().y).toFloat(),
|
||||
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed? orr.. right handed? idk, flip it though
|
||||
)
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians()) // Unrotate preemptively as this vector gets rotate later down the line in MC logic.
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d()
|
||||
worldVelVec = Vec3(
|
||||
worldVelVec.x,
|
||||
worldVelVec.y * (1 - (state.noStamTickCnt.get() / penaltyDeccel).coerceIn(0.0f, 1.0f)), // Dampen the upwards movement depending on stamina depletion time
|
||||
@@ -266,7 +266,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
private fun rotationOverTime(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -292,7 +292,18 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return Vec3(yawForce, 0.0, 0.0)
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
override fun steer(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: RideSteerInput
|
||||
) {
|
||||
input.rotate(mouseRotation(settings, state, vehicle, driver, input.mousePitch, input.mouseYaw, input.mouseYSmoother, input.mouseXSmoother, input.sensitivity, input.deltaTime))
|
||||
input.rotateSmoothed(rotationOverTime(settings, state, vehicle, driver, input.deltaTime))
|
||||
}
|
||||
|
||||
private fun mouseRotation(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -390,10 +401,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun inertia(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
@@ -27,7 +28,6 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
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
|
||||
import net.minecraft.world.phys.Vec2
|
||||
@@ -75,15 +75,19 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
// Use this as a "tick" function and check to see if the driver is "boosting"
|
||||
if (vehicle.level().isClientSide) {
|
||||
handleBoosting(settings, state, vehicle, driver)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun driverTick(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
handleBoosting(settings, state, vehicle, driver)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
@@ -208,12 +212,23 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
}
|
||||
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val retVel = calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
state.rideVelocity.set(retVel)
|
||||
@@ -318,37 +333,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, yInput, xInput)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
@@ -403,14 +387,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
|
||||
@@ -11,6 +11,10 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.composite
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.CompositeRidingStrategies
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideCameraContext
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookInput
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookTake
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookSettings
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
@@ -22,11 +26,12 @@ import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
companion object {
|
||||
@@ -170,16 +175,6 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.useAngVelSmoothing(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
@@ -236,46 +231,6 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.rotationOnMouseXY(
|
||||
behaviourSettings,
|
||||
behaviourState,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.angRollVel(behaviourSettings, behaviourState, vehicle, driver, deltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
@@ -288,6 +243,39 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun steer(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: RideSteerInput
|
||||
) {
|
||||
chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.steer(behaviourSettings, behaviourState, vehicle, driver, input)
|
||||
}
|
||||
}
|
||||
|
||||
override fun travel(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.travel(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun velocityRunsTwicePerTick(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.velocityRunsTwicePerTick(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
@@ -310,6 +298,17 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun driverTick(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.driverTick(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
@@ -361,6 +360,70 @@ class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
behaviour.damageOnCollision(behaviourSettings, behaviourState, vehicle, impactVec)
|
||||
}
|
||||
}
|
||||
|
||||
override fun cameraBase(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
context: RideCameraContext
|
||||
): Quaternionf {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.cameraBase(behaviourSettings, behaviourState, vehicle, context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstPersonHandRoll(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
context: RideCameraContext
|
||||
): Float {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.firstPersonHandRoll(behaviourSettings, behaviourState, vehicle, context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderOffset(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
orientation: Quaternionf
|
||||
): Vector3f {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.renderOffset(behaviourSettings, behaviourState, vehicle, orientation)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderHalflife(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Float? {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.renderHalflife(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun lookSettings(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): RideLookSettings {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.lookSettings(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun consumeLook(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
input: RideLookInput
|
||||
): RideLookTake {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.consumeLook(behaviourSettings, behaviourState, vehicle, input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class CompositeSettings : RidingBehaviourSettings {
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviours
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@@ -63,7 +64,8 @@ object RunStrategy : CompositeRidingStrategy<CompositeSettings> {
|
||||
if (state.activeBehaviour.get() == alternativeSettings.key) return false
|
||||
if (state.lastTransition.get() + 20 >= entity.level().gameTime) return false
|
||||
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
|
||||
return driver.isSprinting && input.z > 0.5 && alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, entity)
|
||||
// Ride velocity is world space so check how much of it is forward
|
||||
return driver.isSprinting && RideFrame.worldToYaw(input, entity.yRot).z > 0.5 && alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, entity)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -44,7 +45,6 @@ import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
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.ai.attributes.Attributes
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@@ -267,12 +267,23 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
}
|
||||
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
return state.rideVelocity.get()
|
||||
@@ -398,37 +409,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, yInput, xInput)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
@@ -475,14 +455,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
return if (state.sprinting.get()) 1.15f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: HorseSettings,
|
||||
state: HorseState,
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
@@ -25,6 +24,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -45,7 +45,6 @@ import kotlin.math.sign
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
@@ -98,7 +97,15 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
override fun driverTick(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit ?: globalMinekart.boostLimit!!)
|
||||
//TODO: should this be by how much turn has accumulated or simply time turning... or both?
|
||||
val boostPerSecond = 20.0f
|
||||
@@ -127,7 +134,6 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
}
|
||||
|
||||
inAirCheck(state, vehicle)
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
@@ -197,12 +203,23 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
|
||||
}
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
return state.rideVelocity.get()
|
||||
@@ -308,34 +325,6 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
return Vec3(0.0, mouseY, mouseX)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
@@ -387,14 +376,6 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
return fov
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
@@ -25,6 +24,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -44,7 +44,6 @@ import kotlin.math.sign
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
@@ -98,7 +97,15 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
override fun driverTick(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
if (driver.isShiftKeyDown) {
|
||||
state.drifting.set(true)
|
||||
if(driver.zza != 0.0f) {
|
||||
@@ -112,7 +119,6 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
}
|
||||
|
||||
inAirCheck(state, vehicle)
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
@@ -172,12 +178,23 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
|
||||
}
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
return state.rideVelocity.get()
|
||||
@@ -278,34 +295,6 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
return Vec3(0.0, mouseY, mouseX)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
@@ -352,14 +341,6 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
@@ -23,6 +22,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -40,7 +40,6 @@ import kotlin.math.min
|
||||
import net.minecraft.core.BlockPos
|
||||
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
|
||||
import net.minecraft.world.phys.Vec2
|
||||
@@ -133,12 +132,23 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
return Vec2(vehicle.xRot, vehicle.yRot + state.deltaRotation.get().toFloat())
|
||||
}
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
applyStrafeRotation(vehicle, driver, settings, state)
|
||||
|
||||
@@ -259,34 +269,6 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
vehicle.setYHeadRot(driver.yRot)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
return Vec3(0.0, mouseY, mouseX)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BoatSettings,
|
||||
state: BoatState,
|
||||
@@ -334,10 +316,6 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
return if (state.isVehicleSprinting.get()) sprintFov else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
@@ -22,6 +21,7 @@ import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
@@ -29,7 +29,6 @@ import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
@@ -96,12 +95,23 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val f = driver.xxa * 0.05f
|
||||
var g = driver.zza * 0.6f
|
||||
@@ -112,34 +122,6 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
return Vec3(f.toDouble(), 0.0, g.toDouble())
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
return Vec3(0.0, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BurstSettings,
|
||||
state: BurstState,
|
||||
@@ -186,10 +168,6 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: BurstSettings, state: BurstState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RideSteerInput
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
@@ -54,6 +55,8 @@ import org.joml.Vector3f
|
||||
|
||||
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
companion object {
|
||||
// The correction forces were tuned as degrees per tenth of a second
|
||||
private const val CORRECTION_FORCE_TO_DEGREES_PER_SECOND = 10.0
|
||||
val KEY = cobblemonResource("liquid/dolphin")
|
||||
}
|
||||
|
||||
@@ -201,7 +204,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
state.lastVelocity.set(prevVel)
|
||||
return state.lastVelocity.get().yRot(vehicle.yRot.toRadians())
|
||||
return state.lastVelocity.get()
|
||||
} else {
|
||||
if(state.hasBreached.get()) {
|
||||
state.clearMouseSmoothers.set(true)
|
||||
@@ -238,18 +241,17 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val worldVelVec = localVelVec.mul(orientation).toVec3d()
|
||||
state.lastVelocity.set(worldVelVec) // Set last velocity to be used during breaching
|
||||
|
||||
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
|
||||
return worldVelVec.yRot(vehicle.yRot.toRadians())
|
||||
return worldVelVec
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
private fun rotateOverTime(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
) {
|
||||
if (vehicle !is OrientationControllable) return
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
@@ -267,8 +269,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, driver, deltaTime)
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
|
||||
val correction = deltaTime * CORRECTION_FORCE_TO_DEGREES_PER_SECOND
|
||||
controller.rotate(0f, (state.currPitchCorrectionForce.get() * correction).toFloat(), (state.currRollCorrectionForce.get() * correction).toFloat())
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
@@ -354,7 +356,18 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
override fun steer(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: RideSteerInput
|
||||
) {
|
||||
input.rotate(mouseRotation(settings, state, vehicle, driver, input.mousePitch, input.mouseYaw, input.mouseYSmoother, input.mouseXSmoother, input.sensitivity, input.deltaTime))
|
||||
rotateOverTime(settings, state, vehicle, driver, input.deltaTime)
|
||||
}
|
||||
|
||||
private fun mouseRotation(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -487,10 +500,6 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
return if (state.boosting.get()) 1.2f else 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Double {
|
||||
return if (!vehicle.isInWater && !vehicle.isUnderWater) 1.0 else 0.1
|
||||
}
|
||||
|
||||
@@ -17,14 +17,16 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookInput
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookTake
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
@@ -254,29 +256,17 @@ class SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
|
||||
*********************************************/
|
||||
// If the driver and vehicles eyes are out of the water but the vehicle is still in it then move only horizontally or downwards as you have surfaced
|
||||
if (state.onSurface.get()) {
|
||||
val worldVelVec = Vec3(currVel.x, min(0.0, currVel.y), currVel.z)
|
||||
return worldVelVec
|
||||
return RideFrame.yawToWorld(Vec3(currVel.x, min(0.0, currVel.y), currVel.z), vehicle.yRot)
|
||||
} else {
|
||||
val vertVec = Vec3(0.0, currVel.y, 0.0)
|
||||
val horzVec = Vec3(currVel.x, 0.0, 0.0)
|
||||
val horzVec = RideFrame.yawToWorld(Vec3(currVel.x, 0.0, 0.0), vehicle.yRot)
|
||||
val localZVelVec = Vector3f(0.0f, 0.0f, currVel.z.toFloat() * -1.0f) // Flip the z axis to make this left handed
|
||||
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
|
||||
val worldVelVec = localZVelVec.mul(orientation).toVec3d().yRot(vehicle.yRot.toRadians())
|
||||
val worldVelVec = localZVelVec.mul(orientation).toVec3d()
|
||||
// Add back the horizontal and vertical components.
|
||||
return worldVelVec.add(vertVec).add(horzVec)
|
||||
}
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun applyRenderRotation(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
@@ -323,7 +313,17 @@ class SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
|
||||
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
override fun steer(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: RideSteerInput
|
||||
) {
|
||||
input.rotate(mouseRotation(settings, state, vehicle, driver, input.mousePitch, input.mouseYaw, input.mouseYSmoother, input.mouseXSmoother, input.sensitivity, input.deltaTime))
|
||||
}
|
||||
|
||||
private fun mouseRotation(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -432,10 +432,6 @@ class SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun inertia(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Double {
|
||||
return if (!vehicle.isUnderWater) 1.0 else 0.1
|
||||
}
|
||||
@@ -452,8 +448,10 @@ class SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun mouseModifiesDriverRotation(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Pair<Boolean, Boolean> {
|
||||
return Pair(false, state.onSurface.get())
|
||||
// Surfaced the mouse pitch only moves the look so you can look around without nosing the sub under
|
||||
override fun consumeLook(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity, input: RideLookInput): RideLookTake {
|
||||
if (!state.onSurface.get()) return RideLookTake.all(input)
|
||||
return RideLookTake(input.yawDelta, 0f, recenterYaw = true, recenterPitch = false)
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.camera
|
||||
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController.Companion.FORWARDS
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController.Companion.UP
|
||||
import net.minecraft.util.Mth
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.atan2
|
||||
|
||||
/**
|
||||
* What a ride gets to build its camera base from each frame.
|
||||
* One lives per ride on the client so [data] can keep a ride's own camera state between frames.
|
||||
*/
|
||||
class RideCameraContext {
|
||||
companion object {
|
||||
// Ride up this close to the view direction has no readable roll
|
||||
private const val MIN_FLAT_LENGTH_SQ = 1.0E-6f
|
||||
|
||||
/** Rotation whose forward is [forward] and up is [up]. Both unit length and perpendicular */
|
||||
@JvmStatic
|
||||
fun frameOf(forward: Vector3f, up: Vector3f): Quaternionf {
|
||||
val right = Vector3f(forward).cross(up)
|
||||
return Quaternionf().setFromNormalized(Matrix3f(right, up, Vector3f(forward).negate()))
|
||||
}
|
||||
}
|
||||
|
||||
/** Ride orientation exactly as the driver sets it */
|
||||
val rawOrientation = Quaternionf()
|
||||
/** Spring smoothed ride orientation the model and seats are drawn with */
|
||||
val renderOrientation = Quaternionf()
|
||||
/** True when the local player is the one steering */
|
||||
var isDriving = false
|
||||
private set
|
||||
/** Base the camera used last frame, null on the first frame of a ride */
|
||||
var previousBase: Quaternionf? = null
|
||||
private set
|
||||
/** Seconds since the last frame, 0 while paused */
|
||||
var frameSeconds = 0f
|
||||
private set
|
||||
var partialTicks = 0f
|
||||
private set
|
||||
/** Free slot for a ride to keep its own camera state in. Cleared when the ride changes */
|
||||
var data: Any? = null
|
||||
/** Final camera view this frame, base with the look on top */
|
||||
val view = Quaternionf()
|
||||
|
||||
/** Drivers hang straight off the ride. Passengers get the smoothed one since raw packets make it jerky */
|
||||
fun defaultBase(): Quaternionf = Quaternionf(if (isDriving) rawOrientation else renderOrientation)
|
||||
|
||||
internal fun update(raw: Quaternionf, render: Quaternionf, isDriving: Boolean, frameSeconds: Float, partialTicks: Float) {
|
||||
rawOrientation.set(raw)
|
||||
renderOrientation.set(render)
|
||||
this.isDriving = isDriving
|
||||
this.frameSeconds = frameSeconds
|
||||
this.partialTicks = partialTicks
|
||||
}
|
||||
|
||||
/** Degrees the ride's model is rolled from the camera around the view direction */
|
||||
fun rideRollAroundView(): Float {
|
||||
val forward = FORWARDS.rotate(view, Vector3f())
|
||||
val cameraUp = UP.rotate(view, Vector3f())
|
||||
val rideUp = UP.rotate(renderOrientation, Vector3f())
|
||||
rideUp.sub(Vector3f(forward).mul(rideUp.dot(forward)))
|
||||
if (rideUp.lengthSquared() < MIN_FLAT_LENGTH_SQ) return 0f
|
||||
return atan2(Vector3f(cameraUp).cross(rideUp).dot(forward), cameraUp.dot(rideUp)) * Mth.RAD_TO_DEG
|
||||
}
|
||||
|
||||
internal fun updateView(view: Quaternionf) {
|
||||
this.view.set(view)
|
||||
}
|
||||
|
||||
internal fun finishFrame(base: Quaternionf) {
|
||||
previousBase = (previousBase ?: Quaternionf()).set(base)
|
||||
}
|
||||
|
||||
internal fun reset() {
|
||||
view.identity()
|
||||
rawOrientation.identity()
|
||||
renderOrientation.identity()
|
||||
isDriving = false
|
||||
previousBase = null
|
||||
frameSeconds = 0f
|
||||
partialTicks = 0f
|
||||
data = null
|
||||
}
|
||||
}
|
||||
@@ -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.api.riding.camera
|
||||
|
||||
/**
|
||||
* One frame of mouse look in degrees, before the ride takes its share.
|
||||
* Angles are relative to the camera base.
|
||||
*/
|
||||
data class RideLookInput(
|
||||
/** Degrees the mouse moved the look sideways this frame */
|
||||
val yawDelta: Float,
|
||||
/** Degrees the mouse moved the look up or down this frame */
|
||||
val pitchDelta: Float,
|
||||
/** Look yaw before this frame's movement */
|
||||
val yaw: Float,
|
||||
/** Look pitch before this frame's movement */
|
||||
val pitch: Float
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.camera
|
||||
|
||||
/**
|
||||
* How far a rider can look around the camera base and how much of the base's turning the look follows.
|
||||
*/
|
||||
data class RideLookSettings(
|
||||
/** Degrees the look can pitch either side of the camera base */
|
||||
val maxPitch: Float = DEFAULT_MAX_PITCH,
|
||||
/** Degrees the look can yaw either side of the camera base */
|
||||
val maxYaw: Float = DEFAULT_MAX_YAW,
|
||||
/** 0 to 1 share of the base's turning the look follows. 1 turns with the base and 0 holds its direction in the world */
|
||||
val carry: Float = FULL_CARRY
|
||||
) {
|
||||
companion object {
|
||||
// Just short of straight up or down so yaw never goes degenerate
|
||||
const val DEFAULT_MAX_PITCH = 89f
|
||||
// A bit past side on so you can look over your shoulder. Raise to let riders look further back
|
||||
const val DEFAULT_MAX_YAW = 105f
|
||||
const val FULL_CARRY = 1f
|
||||
|
||||
@JvmField
|
||||
val DEFAULT = RideLookSettings()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.camera
|
||||
|
||||
/**
|
||||
* The part of a frame's mouse look a ride takes to steer with, in degrees.
|
||||
* Whatever isn't taken moves the rider's look instead.
|
||||
*/
|
||||
data class RideLookTake(
|
||||
val yaw: Float,
|
||||
val pitch: Float,
|
||||
/** Ease the look back to centre on this axis */
|
||||
val recenterYaw: Boolean,
|
||||
val recenterPitch: Boolean
|
||||
) {
|
||||
companion object {
|
||||
/** Ride takes nothing and the look stays where it's put */
|
||||
@JvmField
|
||||
val NONE = RideLookTake(0f, 0f, recenterYaw = false, recenterPitch = false)
|
||||
|
||||
/** Ride steers with all of it and the look drifts back to centre. How rolling rides have always worked */
|
||||
@JvmStatic
|
||||
fun all(input: RideLookInput) = RideLookTake(input.yawDelta, input.pitchDelta, recenterYaw = true, recenterPitch = true)
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,12 @@
|
||||
package com.cobblemon.mod.common.api.riding.util
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.shapes.BooleanOp
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
@@ -60,3 +64,22 @@ fun collectPrimitivesFromBlocks(
|
||||
|
||||
return primitives
|
||||
}
|
||||
|
||||
/**
|
||||
* Real collision boxes in [searchBox] as [PrimitiveShape.BOX] primitives, so slabs, stairs and fences
|
||||
* keep their actual shape. Merged first so a flat wall comes out as one box with no seams to dip the field
|
||||
*/
|
||||
fun collectCollisionPrimitives(entity: Entity, searchBox: AABB): List<FieldPrimitive> {
|
||||
var merged: VoxelShape = Shapes.empty()
|
||||
for (shape in entity.level().getBlockCollisions(entity, searchBox)) {
|
||||
merged = Shapes.joinUnoptimized(merged, shape, BooleanOp.OR)
|
||||
}
|
||||
return merged.optimize().toAabbs().map {
|
||||
val center = it.center
|
||||
FieldPrimitive(
|
||||
shape = PrimitiveShape.BOX,
|
||||
centerX = center.x.toFloat(), centerY = center.y.toFloat(), centerZ = center.z.toFloat(),
|
||||
halfWidth = (it.xsize * 0.5).toFloat(), halfHeight = (it.ysize * 0.5).toFloat(), halfDepth = (it.zsize * 0.5).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.util
|
||||
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Turns ride velocities between world space and a ride's yaw local space,
|
||||
* where x is strafe, y is up and z is forward along the ride's yaw.
|
||||
*/
|
||||
object RideFrame {
|
||||
/** Yaw local to world. Same maths PokemonEntity.travel always used so rides come out identical. */
|
||||
@JvmStatic
|
||||
fun yawToWorld(local: Vec3, yawDegrees: Float): Vec3 {
|
||||
val sin = Mth.sin(yawDegrees.toRadians()).toDouble()
|
||||
val cos = Mth.cos(yawDegrees.toRadians()).toDouble()
|
||||
return Vec3(local.x * cos - local.z * sin, local.y, local.z * cos + local.x * sin)
|
||||
}
|
||||
|
||||
/** World to yaw local. Exact inverse of [yawToWorld]. */
|
||||
@JvmStatic
|
||||
fun worldToYaw(world: Vec3, yawDegrees: Float): Vec3 {
|
||||
val sin = Mth.sin(yawDegrees.toRadians()).toDouble()
|
||||
val cos = Mth.cos(yawDegrees.toRadians()).toDouble()
|
||||
return Vec3(world.x * cos + world.z * sin, world.y, world.z * cos - world.x * sin)
|
||||
}
|
||||
}
|
||||
@@ -198,17 +198,7 @@ interface Spawner {
|
||||
val mid = chunk.getBlockState(startPos)
|
||||
val above = chunk.getBlockState(abovePos)
|
||||
|
||||
// Above must be non-solid
|
||||
if (!above.isPathfindable(PathComputationType.AIR)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Position must be non-air
|
||||
if (mid.isAir) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return above.isPathfindable(PathComputationType.AIR) && mid.isAir
|
||||
}
|
||||
|
||||
fun constrainArea(area: SpawningZoneInput): SpawningZoneInput? {
|
||||
|
||||
@@ -30,7 +30,7 @@ class OrbBlock(properties: Properties) : SixFaceRotationalBlock(properties) {
|
||||
|
||||
val FLOOR_NORTH = Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.375, 0.6875)
|
||||
val WALL_NORTH = Shapes.box(0.3125, 0.3125, 0.625, 0.6875, 0.6875, 1.0)
|
||||
val CEILING_NORTH = Shapes.box(0.3125, 0.625, 0.3125, 0.6875, 1.0, 0.6875)
|
||||
val CEILING_NORTH = Shapes.box(0.3125, 0.625, 0.375, 0.6875, 1.0, 0.6875)
|
||||
}
|
||||
|
||||
init {
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
import org.joml.Quaternionf
|
||||
import org.spongepowered.asm.mixin.Unique
|
||||
|
||||
interface RidingCameraInterface {
|
||||
// Used to help in camera rotation rendering when transitioning
|
||||
// from rolled to unrolled.
|
||||
@Unique
|
||||
fun `getCobblemon$returnTimer`(): Float
|
||||
|
||||
@Unique
|
||||
fun `setCobblemon$returnTimer`(time: Float)
|
||||
|
||||
@Unique
|
||||
fun `getCobblemon$lastHandledRotationTime`(): Double
|
||||
|
||||
@Unique
|
||||
fun `setCobblemon$lastHandledRotationTime`(time: Double)
|
||||
|
||||
@Unique
|
||||
fun `getCobblemon$frameTime`(): Double
|
||||
|
||||
@Unique
|
||||
fun `setCobblemon$frameTime`(time: Double)
|
||||
|
||||
@Unique
|
||||
fun `getCobblemon$partialTickTime`(): Float
|
||||
|
||||
@Unique
|
||||
fun `setCobblemon$partialTickTime`(time: Float)
|
||||
|
||||
@Unique
|
||||
fun `getCobblemon$rollAngleStart`(): Float
|
||||
|
||||
@Unique
|
||||
fun `setCobblemon$rollAngleStart`(angle: Float)
|
||||
|
||||
fun `getCobblemon$smoothRotation`(): Quaternionf?
|
||||
|
||||
fun `setCobblemon$smoothRotation`(smoothRotation: Quaternionf)
|
||||
}
|
||||
@@ -182,6 +182,7 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
|
||||
showHorizontalMouse = false
|
||||
}
|
||||
VehicleBehaviour.KEY -> {}
|
||||
BoardBehaviour.KEY -> {}
|
||||
}
|
||||
|
||||
val centerX = minecraft.window.guiScaledWidth / 2
|
||||
|
||||
@@ -10,10 +10,8 @@ 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.duck.RidePassenger
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.Mth
|
||||
|
||||
object ClientboundUpdateOrientationHandler : ClientNetworkPacketHandler<ClientboundUpdateOrientationPacket> {
|
||||
override fun handle(packet: ClientboundUpdateOrientationPacket, client: Minecraft) {
|
||||
@@ -26,21 +24,7 @@ object ClientboundUpdateOrientationHandler : ClientNetworkPacketHandler<Clientbo
|
||||
// of truth on vehicle orientation updates.
|
||||
if (vehicle.controllingPassenger != player) {
|
||||
packet.active?.let { shouldUseCustomOrientation ->
|
||||
|
||||
val playerIsPassenger = vehicle.passengers.contains(player)
|
||||
|
||||
// If the player has just switched to riding a custom orientation ride then set their
|
||||
// ride x and y rots local to the vehicle rots(if the controller wasn't active and will now be active)
|
||||
// This ensures that on transition your camera stays in the same spot
|
||||
if (!vehicleController.isActive() && shouldUseCustomOrientation && playerIsPassenger) {
|
||||
// Set local to the vehicle x and yrot
|
||||
val playerRotater = player as RidePassenger?
|
||||
if (playerRotater != null) {
|
||||
playerRotater.`cobblemon$setRideXRot`(Mth.wrapDegrees(player.getXRot() - vehicle.getXRot()))
|
||||
playerRotater.`cobblemon$setRideYRot`(Mth.wrapDegrees(player.getYRot() - vehicle.getYRot()))
|
||||
}
|
||||
}
|
||||
|
||||
// Passenger look angles get seeded by the camera on its first rolling frame
|
||||
vehicle.orientationController.active = shouldUseCustomOrientation
|
||||
}
|
||||
vehicle.orientationController.updateOrientation { _ -> packet.orientation }
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController.Companion.FORWARDS
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController.Companion.LEFT
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController.Companion.UP
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideLookSettings
|
||||
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
@@ -30,23 +31,45 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3d
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
|
||||
object MountedCameraRenderer {
|
||||
private var returnTimer: Float = 0f
|
||||
private const val MIN_OFFSET_LENGTH = 1.0E-6
|
||||
// Below this the forward vector is pointing almost straight up or down so it has no usable yaw
|
||||
private const val MIN_HORIZONTAL_LENGTH_SQ = 1.0E-6f
|
||||
// Entity.turn clamps pitch to this
|
||||
private const val VANILLA_MAX_PITCH = 90f
|
||||
// Camera.getMaxZoom checks this far either side on each axis, so it reads a wall as hit if it starts closer than this
|
||||
private const val VANILLA_CAMERA_PROBE = 0.1
|
||||
// Extra gap past vanilla's probe the camera keeps from walls. Raise if the view still starts inside blocks
|
||||
private const val CAMERA_WALL_CLEARANCE = 0.02
|
||||
// Longest step of the camera sweep. Lower follows the straight line more closely round corners but costs more
|
||||
private const val CAMERA_SWEEP_STEP = 0.25
|
||||
|
||||
private var lastHandledRotationTime: Double = Double.MIN_VALUE
|
||||
private var frameTime: Double = 0.0
|
||||
private var rollAngleStart: Float = 0f
|
||||
|
||||
var smoothRotation: Quaternionf? = null
|
||||
private val state = RideCameraState()
|
||||
|
||||
/** Drops all per ride camera state. Called whenever the camera isn't on a ride. */
|
||||
fun reset() = state.reset()
|
||||
|
||||
/** Degrees to roll the first person hands this frame */
|
||||
fun getHandRoll(): Float = state.handRoll
|
||||
|
||||
fun getCameraPosition(
|
||||
instance: Camera,
|
||||
@@ -79,7 +102,7 @@ object MountedCameraRenderer {
|
||||
val locator = delegate.locatorStates[locatorName] ?: return null
|
||||
|
||||
// Get additional offset from poser and add to the eyeHeight offset
|
||||
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
|
||||
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeightOld, eyeHeight)
|
||||
var offset = Vec3(0.0, currEyeHeight - (driver.bbHeight / 2), 0.0)
|
||||
var eyeOffset = Vec3(0.0, currEyeHeight - (driver.bbHeight / 2), 0.0)
|
||||
|
||||
@@ -125,24 +148,44 @@ object MountedCameraRenderer {
|
||||
pivot.add(entityPos.toVector3f())
|
||||
} */
|
||||
|
||||
val offsetDistance = offset.length()
|
||||
val offsetDirection = offset.scale(1 / offsetDistance)
|
||||
|
||||
val eyeOffsetDistance = eyeOffset.length()
|
||||
val eyeOffsetDirection = eyeOffset.scale(1 / eyeOffsetDistance)
|
||||
val eyeOffsetDirection = safeDirection(eyeOffset, eyeOffsetDistance)
|
||||
|
||||
// Use getMaxZoom to calculate clipping
|
||||
val maxZoom: Double = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
|
||||
val eyeMaxZoom: Double = getMaxZoom(eyeOffsetDistance, eyeOffsetDirection, eyePos, instance)
|
||||
|
||||
val playerRotater = driver as RidePassenger
|
||||
playerRotater.`cobblemon$setRideEyePos`(eyeOffsetDirection.scale(eyeMaxZoom).add(eyePos))
|
||||
return offsetDirection.scale(maxZoom).add(pos)
|
||||
// The seat can swing into a wall round corners so clipping from it isn't safe. Come out from the hitbox instead
|
||||
return sweepFromBody(vehicle, entityPos, offset.add(pos))
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides a small box from the middle of the ride's hitbox out to [target] and returns where it ends up.
|
||||
* The hitbox never sits in blocks so this never starts inside a wall, and the box keeps vanilla's
|
||||
* zoom clipping from starting inside one either
|
||||
*/
|
||||
private fun sweepFromBody(vehicle: Entity, lerpedPos: Vec3, target: Vec3): Vec3 {
|
||||
val level = vehicle.level()
|
||||
val halfSize = VANILLA_CAMERA_PROBE + CAMERA_WALL_CLEARANCE
|
||||
var start = lerpedPos.add(0.0, vehicle.bbHeight * 0.5, 0.0)
|
||||
// Lerping between ticks can cut across a corner the ride went round, so fall back to where it really is
|
||||
if (level.getBlockCollisions(null, AABB(start, start).inflate(halfSize)).any()) start = vehicle.boundingBox.center
|
||||
var box = AABB(start, start).inflate(halfSize)
|
||||
val path = target.subtract(start)
|
||||
// Small steps so it slides along the straight line instead of going one axis at a time
|
||||
val steps = max(1, ceil(path.length() / CAMERA_SWEEP_STEP).toInt())
|
||||
val step = path.scale(1.0 / steps)
|
||||
repeat(steps) {
|
||||
box = box.move(Entity.collideBoundingBox(null, step, box, level, emptyList()))
|
||||
}
|
||||
return box.center
|
||||
}
|
||||
|
||||
// Return value is false if the vanilla camera rotations should be applied.
|
||||
fun setRotation(instance: Camera): Boolean {
|
||||
val cameraDuck = instance as CameraDuck
|
||||
state.handRoll = 0f
|
||||
|
||||
// Calculate the current frametime
|
||||
val d = Blaze3D.getTime()
|
||||
@@ -152,6 +195,7 @@ object MountedCameraRenderer {
|
||||
// Don't assume the camera to be attached to an entity. Ponder Scenes from create et.al. for example aren't.
|
||||
val vehicle: Entity? = cameraDuck.`cobblemon$getEntity`()?.vehicle ?: return false
|
||||
if (vehicle !is OrientationControllable) return false
|
||||
state.bindTo(vehicle.id)
|
||||
|
||||
// If the current vehicle has no orientation then return and perform
|
||||
// vanilla camera rotations
|
||||
@@ -161,11 +205,12 @@ object MountedCameraRenderer {
|
||||
// If the controller has been deactivated but the orientation isn't null yet
|
||||
// then perform transition from rolled ride/camera to vanilla
|
||||
if (!vehicleController.isActive() && vehicleController.orientation != null) {
|
||||
state.lookSeeded = false
|
||||
return applyTransitionRotation(vehicleController, instance)
|
||||
} else { // Otherwise perform cobblemon specific camera rotation and set transition helper variables
|
||||
applyCameraRotation(instance)
|
||||
returnTimer = 0f
|
||||
rollAngleStart = instance.rotation().getEulerAnglesYXZ(Vector3f()).z()
|
||||
state.returnTimer = 0f
|
||||
state.rollAngleStart = instance.rotation().getEulerAnglesYXZ(Vector3f()).z()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -182,25 +227,31 @@ object MountedCameraRenderer {
|
||||
val vehicleController: OrientationController = controllableVehicle.getOrientationController()
|
||||
|
||||
val isDriving = driver.controlledVehicle != null
|
||||
val ride = vehicle as? PokemonEntity
|
||||
|
||||
|
||||
// Grab the ride orientation
|
||||
// If the player is driving then use the current orientation since the driver sets it.
|
||||
// If not then use the smoothed render orientation for passengers otherwise the vehicle rotation will be jerky
|
||||
var newRotation = if (isDriving) {
|
||||
vehicleController.orientation!!.normal(Matrix3f()).getNormalizedRotation(Quaternionf())
|
||||
} else {
|
||||
vehicleController.getRenderOrientation(instance.partialTickTime)
|
||||
}
|
||||
val context = state.context
|
||||
context.update(
|
||||
vehicleController.orientation!!.normal(Matrix3f()).getNormalizedRotation(Quaternionf()),
|
||||
vehicleController.getRenderOrientation(instance.partialTickTime),
|
||||
isDriving,
|
||||
if (Minecraft.getInstance().isPaused) 0f else frameTime.toFloat(),
|
||||
instance.partialTickTime
|
||||
)
|
||||
// The ride picks what the camera hangs off. disableRoll still gets the last word below since it's the player's setting
|
||||
var newRotation = ride?.let { pokemon ->
|
||||
pokemon.ifRidingAvailableSupply<Quaternionf?>(null) { behaviour, settings, rideState ->
|
||||
behaviour.cameraBase(settings, rideState, pokemon, context)
|
||||
}
|
||||
} ?: context.defaultBase()
|
||||
|
||||
// Unroll the current rotation if no roll is requested and not currently freelooking
|
||||
if (Cobblemon.config.disableRoll) {
|
||||
// Init the smooth rotation if it has not been set yet.
|
||||
if (smoothRotation == null) {
|
||||
smoothRotation = Quaternionf().set(instance.rotation())
|
||||
if (state.smoothRotation == null) {
|
||||
state.smoothRotation = Quaternionf().set(instance.rotation())
|
||||
}
|
||||
val rideAngs = newRotation.getEulerAnglesYXZ(Vector3f())
|
||||
val cameraAngs = smoothRotation!!.getEulerAnglesYXZ(Vector3f())
|
||||
val cameraAngs = state.smoothRotation!!.getEulerAnglesYXZ(Vector3f())
|
||||
|
||||
val degDiff = Mth.degreesDifference(
|
||||
Math.toDegrees(cameraAngs.y().toDouble()).toFloat(),
|
||||
@@ -237,37 +288,114 @@ object MountedCameraRenderer {
|
||||
|
||||
// Set rotations
|
||||
val smoothedRot = Quaternionf().rotateYXZ(newYaw.toFloat(), newPitch.toFloat(), newRoll)
|
||||
smoothRotation?.set(smoothedRot)
|
||||
state.smoothRotation?.set(smoothedRot)
|
||||
newRotation.set(smoothedRot)
|
||||
|
||||
}
|
||||
|
||||
var rotationOffset = 0
|
||||
if (Minecraft.getInstance().options.cameraType.isMirrored) {
|
||||
newRotation.rotateY(Math.toRadians(180.0).toFloat())
|
||||
rotationOffset = 180
|
||||
// newRotation is the camera base from here on
|
||||
val playerRotater = driver as RidePassenger
|
||||
// First frame on a roll ride so carry over wherever the player was already looking
|
||||
if (!state.lookSeeded) {
|
||||
seedLook(driver, playerRotater, newRotation)
|
||||
state.lookSeeded = true
|
||||
} else {
|
||||
val lookSettings = ride?.let { pokemon ->
|
||||
pokemon.ifRidingAvailableSupply(RideLookSettings.DEFAULT) { behaviour, settings, rideState ->
|
||||
behaviour.lookSettings(settings, rideState, pokemon)
|
||||
}
|
||||
} ?: RideLookSettings.DEFAULT
|
||||
val previousBase = context.previousBase
|
||||
// Full carry is exactly what the look angles already do so skip the work
|
||||
if (lookSettings.carry < RideLookSettings.FULL_CARRY && previousBase != null) {
|
||||
val oldYaw = playerRotater.`cobblemon$getRideYRot`()
|
||||
val oldPitch = playerRotater.`cobblemon$getRideXRot`()
|
||||
carryLook(playerRotater, previousBase, newRotation, lookSettings.carry)
|
||||
// The base turning drags the look along at its limits but never pushes it further out than it was
|
||||
playerRotater.`cobblemon$setRideYRot`(clampOutward(playerRotater.`cobblemon$getRideYRot`(), oldYaw, lookSettings.maxYaw))
|
||||
playerRotater.`cobblemon$setRideXRot`(clampOutward(playerRotater.`cobblemon$getRideXRot`(), oldPitch, lookSettings.maxPitch))
|
||||
}
|
||||
}
|
||||
context.finishFrame(newRotation)
|
||||
|
||||
// Look sits on top of the base. Built straight as a quaternion so nothing round trips through euler angles
|
||||
val view = Quaternionf(newRotation).mul(lookRotation(playerRotater)).normalize()
|
||||
|
||||
// Get the drivers local look angle in relation to the ride and apply it.
|
||||
if (vehicleController.active) {
|
||||
val vehicleMatrix = Matrix3f().set(newRotation)
|
||||
val driverMatrix = Matrix3f() // Base the driver
|
||||
val playerRotater = driver as RidePassenger
|
||||
newRotation = vehicleMatrix.mul(
|
||||
driverMatrix.rotateYXZ(
|
||||
-Mth.DEG_TO_RAD * (playerRotater.`cobblemon$getRideYRot`()),
|
||||
-Mth.DEG_TO_RAD * playerRotater.`cobblemon$getRideXRot`(),
|
||||
0.0f
|
||||
)
|
||||
).normal(Matrix3f()).getNormalizedRotation(Quaternionf())
|
||||
// Mirroring only flips the camera so the player still aims where they look
|
||||
val cameraRotation = if (Minecraft.getInstance().options.cameraType.isMirrored) Quaternionf(view).rotateY(Mth.PI) else view
|
||||
setCameraRotations(cameraRotation, instance, cameraDuck)
|
||||
lookAlong(driver, view)
|
||||
|
||||
// Third person shows the player model which already leans with the ride
|
||||
context.updateView(view)
|
||||
if (Minecraft.getInstance().options.cameraType.isFirstPerson) {
|
||||
state.handRoll = ride?.let { pokemon ->
|
||||
pokemon.ifRidingAvailableSupply(0f) { behaviour, settings, rideState ->
|
||||
behaviour.firstPersonHandRoll(settings, rideState, pokemon, context)
|
||||
}
|
||||
} ?: 0f
|
||||
}
|
||||
val eulerAngs = newRotation.getEulerAnglesYXZ(Vector3f())
|
||||
setCameraRotations(newRotation, instance, cameraDuck)
|
||||
}
|
||||
|
||||
// Set the drivers rotations to match
|
||||
driver.xRot = -eulerAngs.x() * Mth.RAD_TO_DEG + rotationOffset
|
||||
driver.yRot = 180.0f - (eulerAngs.y() * Mth.RAD_TO_DEG)
|
||||
/** Sets ride look angles so the view keeps pointing where [player] already looks once [base] takes over. */
|
||||
private fun seedLook(player: Entity, rotater: RidePassenger, base: Quaternionf) {
|
||||
val local = Quaternionf(base).invert().transform(player.calculateViewVector(player.xRot, player.yRot).toVector3f())
|
||||
setLookFromLocal(rotater, local)
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the look by only [carry] of how far the base turned since last frame.
|
||||
* Whatever the look doesn't follow ends up as a change in the look angles instead.
|
||||
*/
|
||||
private fun carryLook(rotater: RidePassenger, previousBase: Quaternionf, base: Quaternionf, carry: Float) {
|
||||
val worldLook = Quaternionf(previousBase).mul(lookRotation(rotater))
|
||||
val baseDelta = Quaternionf(base).mul(Quaternionf(previousBase).invert())
|
||||
Quaternionf().slerp(baseDelta, carry).mul(worldLook, worldLook)
|
||||
val local = Quaternionf(base).invert().transform(FORWARDS.rotate(worldLook, Vector3f()))
|
||||
setLookFromLocal(rotater, local)
|
||||
}
|
||||
|
||||
/** Keeps [angle] within [limit] either side, or within [previous] if that was already further out */
|
||||
private fun clampOutward(angle: Float, previous: Float, limit: Float): Float {
|
||||
val bound = max(limit, abs(previous))
|
||||
return angle.coerceIn(-bound, bound)
|
||||
}
|
||||
|
||||
/** Look as a rotation relative to the base. */
|
||||
private fun lookRotation(rotater: RidePassenger): Quaternionf = Quaternionf().rotationYXZ(
|
||||
-Mth.DEG_TO_RAD * rotater.`cobblemon$getRideYRot`(),
|
||||
-Mth.DEG_TO_RAD * rotater.`cobblemon$getRideXRot`(),
|
||||
0.0f
|
||||
)
|
||||
|
||||
/** Look angles that point along [local], a direction in the base's frame. Inverse of [lookRotation]. */
|
||||
private fun setLookFromLocal(rotater: RidePassenger, local: Vector3f) {
|
||||
rotater.`cobblemon$setRideXRot`(-asin(local.y.coerceIn(-1f, 1f)) * Mth.RAD_TO_DEG)
|
||||
rotater.`cobblemon$setRideYRot`(atan2(local.x, -local.z) * Mth.RAD_TO_DEG)
|
||||
}
|
||||
|
||||
/**
|
||||
* Points [player] along [view] so picking, bows and other clients follow the camera.
|
||||
* Yaw stays continuous so partial tick lerps never sweep through a 360 wrap.
|
||||
*/
|
||||
private fun lookAlong(player: Entity, view: Quaternionf) {
|
||||
val forward = FORWARDS.rotate(view, Vector3f())
|
||||
val pitch = pitchOf(forward)
|
||||
val yaw = player.yRot + Mth.wrapDegrees(yawOf(forward, player.yRot) - player.yRot)
|
||||
// Shift the old values by the same amount like vanilla turn does so the lerped view vector doesn't trail a tick behind
|
||||
player.xRotO = Mth.clamp(player.xRotO + pitch - player.xRot, -VANILLA_MAX_PITCH, VANILLA_MAX_PITCH)
|
||||
player.yRotO += yaw - player.yRot
|
||||
player.xRot = pitch
|
||||
player.yRot = yaw
|
||||
}
|
||||
|
||||
/** Vanilla style pitch in degrees for a forward vector. */
|
||||
private fun pitchOf(forward: Vector3f): Float = -asin(forward.y.coerceIn(-1f, 1f)) * Mth.RAD_TO_DEG
|
||||
|
||||
/** Vanilla style yaw in degrees for a forward vector, or [fallback] when looking straight up or down. */
|
||||
private fun yawOf(forward: Vector3f, fallback: Float): Float {
|
||||
if (forward.x * forward.x + forward.z * forward.z < MIN_HORIZONTAL_LENGTH_SQ) return fallback
|
||||
return atan2(-forward.x, forward.z) * Mth.RAD_TO_DEG
|
||||
}
|
||||
|
||||
private fun applyTransitionRotation(
|
||||
@@ -277,14 +405,14 @@ object MountedCameraRenderer {
|
||||
val cameraDuck = instance as CameraDuck
|
||||
val entity = cameraDuck.`cobblemon$getEntity`()!!
|
||||
// If the transition has just started then
|
||||
if (returnTimer == 0f) {
|
||||
if (state.returnTimer == 0f) {
|
||||
resetDriverRotations(instance, entity)
|
||||
}
|
||||
|
||||
if (returnTimer < 1f) {
|
||||
if (state.returnTimer < 1f) {
|
||||
// Rotation is taken from entity since we no longer handle mouse ourselves
|
||||
// Stops a period of time when you can't input anything.
|
||||
val interpolatedRoll = Mth.lerp(returnTimer, rollAngleStart, 0.0f)
|
||||
val interpolatedRoll = Mth.lerp(state.returnTimer, state.rollAngleStart, 0.0f)
|
||||
val pitch = Math.toRadians(-entity.xRot.toDouble()).toFloat()
|
||||
val yaw = Math.toRadians((180 - entity.yRot).toDouble()).toFloat()
|
||||
|
||||
@@ -293,16 +421,16 @@ object MountedCameraRenderer {
|
||||
|
||||
setCameraRotations(interRot, instance, cameraDuck)
|
||||
|
||||
if (rollAngleStart == 0f) {
|
||||
rollAngleStart = 1f
|
||||
if (state.rollAngleStart == 0f) {
|
||||
state.rollAngleStart = 1f
|
||||
vehicleController.reset()
|
||||
resetDriverRotations(instance, entity)
|
||||
return false
|
||||
}
|
||||
returnTimer += instance.partialTickTime * (1.0f / 20.0f)
|
||||
state.returnTimer += instance.partialTickTime * (1.0f / 20.0f)
|
||||
return true
|
||||
} else {
|
||||
returnTimer = 1f
|
||||
state.returnTimer = 1f
|
||||
vehicleController.reset()
|
||||
resetDriverRotations(instance, entity)
|
||||
return false
|
||||
@@ -329,7 +457,7 @@ object MountedCameraRenderer {
|
||||
val offset = if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
|
||||
cameraOffsets[locatorName + "_reverse"]!!
|
||||
} else if (thirdPersonReverse && cameraOffsets.containsKey("seat_default_reverse")) {
|
||||
cameraOffsets[locatorName + "seat_default_reverse"]!!
|
||||
cameraOffsets["seat_default_reverse"]!!
|
||||
} else if (cameraOffsets.containsKey(locatorName)) {
|
||||
cameraOffsets[locatorName]!!
|
||||
} else if (cameraOffsets.containsKey("seat_default")) {
|
||||
@@ -354,6 +482,10 @@ object MountedCameraRenderer {
|
||||
}
|
||||
|
||||
|
||||
/** Unit direction of [offset], or zero when it's too short to have one (avoids NaN from 0 / 0). */
|
||||
private fun safeDirection(offset: Vec3, length: Double): Vec3 =
|
||||
if (length > MIN_OFFSET_LENGTH) offset.scale(1 / length) else Vec3.ZERO
|
||||
|
||||
private fun getMaxZoom(maxZoom: Double, directionVector: Vec3, positionVector: Vec3, instance: Camera): Double {
|
||||
var maxZoom = maxZoom
|
||||
for (i in 0 .. 7) {
|
||||
@@ -384,14 +516,14 @@ object MountedCameraRenderer {
|
||||
}
|
||||
|
||||
private fun setCameraRotations(rotation: Quaternionf, instance: Camera, duck: CameraDuck) {
|
||||
val eulerAngs = rotation.getEulerAnglesYXZ(Vector3f())
|
||||
val cameraRotation = instance.rotation()
|
||||
cameraRotation.rotationYXZ(eulerAngs.y, eulerAngs.x, eulerAngs.z)
|
||||
duck.`cobblemon$setXRot`(eulerAngs.x() * Mth.RAD_TO_DEG)
|
||||
duck.`cobblemon$setYRot`(eulerAngs.y() * Mth.RAD_TO_DEG)
|
||||
|
||||
FORWARDS.rotate(rotation, duck.`cobblemon$getForwards`())
|
||||
UP.rotate(rotation, duck.`cobblemon$getUp`())
|
||||
val cameraRotation = instance.rotation().set(rotation).normalize()
|
||||
FORWARDS.rotate(cameraRotation, duck.`cobblemon$getForwards`())
|
||||
UP.rotate(cameraRotation, duck.`cobblemon$getUp`())
|
||||
LEFT.rotate(cameraRotation, duck.`cobblemon$getLeft`())
|
||||
|
||||
// Same yaw and pitch convention vanilla setRotation stores since vanilla reads these back
|
||||
val forward = duck.`cobblemon$getForwards`()
|
||||
duck.`cobblemon$setXRot`(pitchOf(forward))
|
||||
duck.`cobblemon$setYRot`(yawOf(forward, duck.`cobblemon$getYRot`()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.camera
|
||||
|
||||
import com.cobblemon.mod.common.api.riding.camera.RideCameraContext
|
||||
import org.joml.Quaternionf
|
||||
|
||||
/**
|
||||
* Camera state for the ride the local player is on.
|
||||
* Tied to one vehicle so nothing leaks from one ride into the next.
|
||||
*/
|
||||
class RideCameraState {
|
||||
companion object {
|
||||
private const val NO_VEHICLE = -1
|
||||
}
|
||||
|
||||
var vehicleId: Int = NO_VEHICLE
|
||||
private set
|
||||
|
||||
// 0 to 1 progress of the roll going back to level once a roll ride turns off
|
||||
var returnTimer: Float = 0f
|
||||
// Camera roll at the start of that transition
|
||||
var rollAngleStart: Float = 0f
|
||||
// Smoothed rotation the disableRoll camera eases with
|
||||
var smoothRotation: Quaternionf? = null
|
||||
// False until the look has been rebuilt from the player's real look for this stretch of rolling
|
||||
var lookSeeded: Boolean = false
|
||||
// Degrees the first person hands are rolled this frame
|
||||
var handRoll: Float = 0f
|
||||
// Handed to the ride's camera hooks every frame
|
||||
val context = RideCameraContext()
|
||||
|
||||
/** Starts fresh if [vehicleId] isn't the vehicle this state was built for. */
|
||||
fun bindTo(vehicleId: Int) {
|
||||
if (this.vehicleId == vehicleId) return
|
||||
reset()
|
||||
this.vehicleId = vehicleId
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
vehicleId = NO_VEHICLE
|
||||
returnTimer = 0f
|
||||
rollAngleStart = 0f
|
||||
smoothRotation = null
|
||||
lookSeeded = false
|
||||
handRoll = 0f
|
||||
context.reset()
|
||||
}
|
||||
}
|
||||
@@ -770,8 +770,13 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
val controller = entity.orientationController
|
||||
val transformationMatrix = Matrix4f()
|
||||
val center = Vector3f(0f, entity.bbHeight/2, 0f)
|
||||
val orientation = controller.getRenderOrientation(state.getPartialTicks())
|
||||
// Same shift the renderer gives the model so seats stay on it
|
||||
transformationMatrix.translate(entity.ifRidingAvailableSupply(fallback = Vector3f()) { behaviour, settings, rideState ->
|
||||
behaviour.renderOffset(settings, rideState, entity, orientation)
|
||||
})
|
||||
transformationMatrix.translate(center)
|
||||
transformationMatrix.rotate(controller.getRenderOrientation(state.getPartialTicks()))
|
||||
transformationMatrix.rotate(orientation)
|
||||
transformationMatrix.translate(center.negate())
|
||||
matrixStack.mulPose(transformationMatrix)
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ class DusclopsModel (root: ModelPart) : PokemonPosableModel(root) {
|
||||
lateinit var battleidle: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
animations["cry"] = "q.bedrock_stateful('dusclops', 'cry')".asExpressionLike()
|
||||
animations["cry"] = "q.bedrock_stateful('squirtle', 'cry')".asExpressionLike()
|
||||
|
||||
val blink = quirk { bedrockStateful("dusclops", "blink") }
|
||||
|
||||
|
||||
@@ -19,9 +19,16 @@ import net.minecraft.client.model.AnimationUtils
|
||||
import net.minecraft.client.model.HumanoidModel
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.client.player.AbstractClientPlayer
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.HumanoidArm
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.hypot
|
||||
|
||||
/**
|
||||
* @author landonjw
|
||||
@@ -30,6 +37,9 @@ object MountedPlayerRenderer {
|
||||
@JvmField
|
||||
var shouldApplyRootAnimation = false
|
||||
|
||||
// Furthest a passenger's head turns from its body either way. From LivingEntityRenderer.render
|
||||
private const val VANILLA_PASSENGER_HEAD_YAW_LIMIT = 85f
|
||||
|
||||
@JvmField
|
||||
val relevantPartsByName = mutableMapOf(
|
||||
"body" to ModelPart(emptyList(), emptyMap()),
|
||||
@@ -140,6 +150,28 @@ object MountedPlayerRenderer {
|
||||
model.hat.copyFrom(model.head)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rider head yaw and pitch in degrees like vanilla hands them to setupAnim, but measured against the seat
|
||||
* the body is drawn on instead of world yaw. Null if the rider has no seat locator
|
||||
*/
|
||||
@JvmStatic
|
||||
fun riderHeadAngles(pokemon: PokemonEntity, rider: LivingEntity, partialTicks: Float): Vec2? {
|
||||
val delegate = pokemon.delegate as? PokemonClientDelegate ?: return null
|
||||
val locator = delegate.getSeatLocator(rider)?.let { delegate.locatorStates[it] } ?: return null
|
||||
// MountedLivingEntityRenderer cancels the body yaw so the seat's rotation is the whole body frame
|
||||
val seat = locator.matrix.getNormalizedRotation(Quaternionf())
|
||||
val forward = seat.transform(Vector3f(0f, 0f, -1f))
|
||||
val up = seat.transform(Vector3f(0f, 1f, 0f))
|
||||
// Way a positive head yaw turns
|
||||
val side = Vector3f(forward).cross(up)
|
||||
val view = rider.getViewVector(partialTicks).toVector3f()
|
||||
val ahead = view.dot(forward)
|
||||
val across = view.dot(side)
|
||||
val yaw = atan2(across, ahead) * Mth.RAD_TO_DEG
|
||||
val pitch = atan2(-view.dot(up), hypot(across, ahead)) * Mth.RAD_TO_DEG
|
||||
return Vec2(yaw.coerceIn(-VANILLA_PASSENGER_HEAD_YAW_LIMIT, VANILLA_PASSENGER_HEAD_YAW_LIMIT), pitch)
|
||||
}
|
||||
|
||||
const val RIDING_SITTING_OFFSET = -10.0
|
||||
fun animateRoot(stack: PoseStack) {
|
||||
val root = relevantPartsByName["body"] ?: return
|
||||
|
||||
@@ -199,11 +199,16 @@ class PokemonRenderer(
|
||||
val matrix = poseMatrix.last().pose()
|
||||
val yaw = Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot)
|
||||
val center = Vector3f(0f, entity.bbHeight/2, 0f)
|
||||
val orientation = controller.getRenderOrientation(partialTicks)
|
||||
val transformationMatrix = Matrix4f()
|
||||
// Lets the ride slide the model onto what it's standing on
|
||||
transformationMatrix.translate(entity.ifRidingAvailableSupply(fallback = Vector3f()) { behaviour, settings, state ->
|
||||
behaviour.renderOffset(settings, state, entity, orientation)
|
||||
})
|
||||
//Move origin to center of Pokemon
|
||||
transformationMatrix.translate(center)
|
||||
|
||||
transformationMatrix.rotate(controller.getRenderOrientation(partialTicks))
|
||||
transformationMatrix.rotate(orientation)
|
||||
//Move origin to base of the entity
|
||||
transformationMatrix.translate(center.negate(Vector3f()))
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@ import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeatu
|
||||
import com.cobblemon.mod.common.pokemon.feature.StashHandler
|
||||
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import com.google.common.collect.UnmodifiableIterator
|
||||
import com.mojang.serialization.Codec
|
||||
@@ -1866,28 +1865,9 @@ open class PokemonEntity(
|
||||
}
|
||||
}
|
||||
|
||||
// Ridden movement is all in travel which never gets here while someone's steering
|
||||
override fun handleRelativeFrictionAndCalculateMovement(deltaMovement: Vec3, friction: Float): Vec3 {
|
||||
if (!this.hasRider() || this.controllingPassenger == null) {
|
||||
super.handleRelativeFrictionAndCalculateMovement(deltaMovement, friction)
|
||||
} else {
|
||||
val velocity = ifRidingAvailableSupply(fallback = Vec3.ZERO) { behaviour, settings, state ->
|
||||
behaviour.velocity(settings, state, this, this.controllingPassenger as Player, deltaMovement)
|
||||
}
|
||||
// Handle ridden pokemon differently to allow vector lerp instead of simple addition.
|
||||
val v = getInputVector(velocity, 1.0f, this.yRot)
|
||||
// Changing this will give the ride more or less inertia/handling/drift
|
||||
val inertia = ifRidingAvailableSupply(fallback = 0.5) { behaviour, settings, state ->
|
||||
behaviour.inertia(settings, state,this)
|
||||
}
|
||||
|
||||
this.deltaMovement = this.deltaMovement.lerp(v, inertia)
|
||||
var pos = this.deltaMovement.scale(this.speed.toDouble())
|
||||
if (super.onGround() && this.deltaMovement.y == 0.0) {
|
||||
pos = pos.subtract(0.0, 0.0001, 0.0)
|
||||
}
|
||||
this.move(MoverType.SELF, pos)
|
||||
}
|
||||
|
||||
super.handleRelativeFrictionAndCalculateMovement(deltaMovement, friction)
|
||||
return this.deltaMovement
|
||||
}
|
||||
|
||||
@@ -1920,44 +1900,46 @@ open class PokemonEntity(
|
||||
if (!this.hasRider() || this.controllingPassenger == null) {
|
||||
super.travel(movementInput)
|
||||
} else {
|
||||
val inp = ifRidingAvailableSupply(fallback = Vec3.ZERO) { behaviour, settings, state ->
|
||||
behaviour.velocity(settings, state, this, this.controllingPassenger as Player, deltaMovement)
|
||||
// Some rides move themselves, like climbers going over every surface
|
||||
val travelled = ifRidingAvailableSupply(fallback = false) { behaviour, settings, state ->
|
||||
behaviour.travel(settings, state, this, this.controllingPassenger as Player)
|
||||
}
|
||||
if (!travelled) {
|
||||
// movementInput is the world space velocity getRiddenInput already got from the ride this tick
|
||||
val v = ifRidingAvailableSupply(fallback = Vec3.ZERO) { behaviour, settings, state ->
|
||||
if (behaviour.velocityRunsTwicePerTick(settings, state, this)) {
|
||||
behaviour.velocity(settings, state, this, this.controllingPassenger as Player, deltaMovement)
|
||||
} else {
|
||||
movementInput
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate velocity vector to face the current y rotation
|
||||
val f = Mth.sin(this.yRot.toRadians())
|
||||
val g = Mth.cos(this.yRot.toRadians())
|
||||
val v = Vec3(
|
||||
inp.x * g.toDouble() - inp.z * f.toDouble(),
|
||||
inp.y,
|
||||
inp.z * g.toDouble() + inp.x * f.toDouble()
|
||||
)
|
||||
val diff = v.subtract(this.deltaMovement)
|
||||
|
||||
val diff = v.subtract(this.deltaMovement)
|
||||
val inertia = ifRidingAvailableSupply(fallback = 0.5) { behaviour, settings, state ->
|
||||
behaviour.inertia(settings, state,this)
|
||||
}
|
||||
|
||||
val inertia = ifRidingAvailableSupply(fallback = 0.5) { behaviour, settings, state ->
|
||||
behaviour.inertia(settings, state,this)
|
||||
}
|
||||
this.deltaMovement = this.deltaMovement.add( diff.scale(inertia) )
|
||||
val triedMovement = this.deltaMovement
|
||||
|
||||
this.deltaMovement = this.deltaMovement.add( diff.scale(inertia) )
|
||||
val triedMovement = this.deltaMovement
|
||||
this.move(MoverType.SELF, this.deltaMovement)
|
||||
|
||||
this.move(MoverType.SELF, this.deltaMovement)
|
||||
if (this.horizontalCollision && this.isControlledByLocalInstance) {
|
||||
ifRidingAvailable { behaviour, settings, state ->
|
||||
// Tried minus performed = vector pointing at where we *couldn't* go
|
||||
val delta = triedMovement.subtract(this.deltaMovement)
|
||||
if (behaviour.damageOnCollision(settings, state, this, delta)) {
|
||||
DamageOnCollisionPacket(delta).sendToServer()
|
||||
|
||||
if (this.horizontalCollision && this.isControlledByLocalInstance) {
|
||||
ifRidingAvailable { behaviour, settings, state ->
|
||||
// Tried minus performed = vector pointing at where we *couldn't* go
|
||||
val delta = triedMovement.subtract(this.deltaMovement)
|
||||
if (behaviour.damageOnCollision(settings, state, this, delta)) {
|
||||
DamageOnCollisionPacket(delta).sendToServer()
|
||||
for (passenger in this.passengers) {
|
||||
passenger.deltaMovement = this.deltaMovement
|
||||
}
|
||||
DismountPokemonPacket().sendToServer()
|
||||
|
||||
for (passenger in this.passengers) {
|
||||
passenger.deltaMovement = this.deltaMovement
|
||||
// Reset ride velocity
|
||||
state.rideVelocity.set(state.rideVelocity.get().multiply(0.0, 1.0, 0.0))
|
||||
}
|
||||
DismountPokemonPacket().sendToServer()
|
||||
|
||||
// Reset ride velocity
|
||||
state.rideVelocity.set(state.rideVelocity.get().multiply(0.0, 1.0, 0.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2390,6 +2372,11 @@ open class PokemonEntity(
|
||||
|
||||
this.jumpInputStrength = 0
|
||||
}
|
||||
|
||||
// Same spot speed used to get called from so nothing shifts order
|
||||
if (this.isControlledByLocalInstance) {
|
||||
behaviour.driverTick(settings, state, this, driver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.HelicopterSetting
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BoardSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
|
||||
@@ -35,6 +36,7 @@ class RideSettingsSyncPacket(
|
||||
val hover: HoverSettings,
|
||||
val jet: JetSettings,
|
||||
val rocket: RocketSettings,
|
||||
val board: BoardSettings,
|
||||
val horse: HorseSettings,
|
||||
val minekart: MinekartSettings,
|
||||
val vehicle: VehicleSettings,
|
||||
@@ -52,6 +54,7 @@ class RideSettingsSyncPacket(
|
||||
hover.encode(buffer)
|
||||
jet.encode(buffer)
|
||||
rocket.encode(buffer)
|
||||
board.encode(buffer)
|
||||
horse.encode(buffer)
|
||||
minekart.encode(buffer)
|
||||
vehicle.encode(buffer)
|
||||
@@ -71,6 +74,7 @@ class RideSettingsSyncPacket(
|
||||
hover = HoverSettings(),
|
||||
jet = JetSettings(),
|
||||
rocket = RocketSettings(),
|
||||
board = BoardSettings(),
|
||||
horse = HorseSettings(),
|
||||
minekart = MinekartSettings(),
|
||||
vehicle = VehicleSettings(),
|
||||
@@ -85,6 +89,7 @@ class RideSettingsSyncPacket(
|
||||
hover.decode(buffer)
|
||||
jet.decode(buffer)
|
||||
rocket.decode(buffer)
|
||||
board.decode(buffer)
|
||||
horse.decode(buffer)
|
||||
minekart.decode(buffer)
|
||||
vehicle.decode(buffer)
|
||||
|
||||
@@ -17,12 +17,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.FallStrategy
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.JumpStrategy
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.RunStrategy
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.*
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
@@ -50,7 +45,9 @@ object RidingBehaviourSettingsAdapter : JsonDeserializer<RidingBehaviourSettings
|
||||
MinekartBehaviour.KEY to MinekartSettings::class.java,
|
||||
HoverBehaviour.KEY to HoverSettings::class.java,
|
||||
RocketBehaviour.KEY to RocketSettings::class.java,
|
||||
BoardBehaviour.KEY to BoardSettings::class.java,
|
||||
SubmarineBehaviour.KEY to SubmarineSettings::class.java,
|
||||
ClimbBehaviour.KEY to ClimbSettings::class.java,
|
||||
CompositeBehaviour.KEY to CompositeSettings::class.java,
|
||||
|
||||
/*
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"rotation": "math.random_integer(0,3)*90"
|
||||
},
|
||||
"minecraft:particle_motion_parametric": {
|
||||
"relative_position": ["-(v.target_deltax+((v.particle_random_1-0.5)*1.33))*(v.particle_age / v.particle_lifetime)", "(v.target_deltay+((v.particle_random_2-0.5)*1.33))*(v.particle_age / v.particle_lifetime)", "-v.target_deltaz*(v.particle_age / v.particle_lifetime)"]
|
||||
"relative_position": ["-(v.target_deltax+((v.particle_random_1-0.5)*2))*(v.particle_age / v.particle_lifetime)", "(v.target_deltay+((v.particle_random_2-0.5)*2))*(v.particle_age / v.particle_lifetime)", "-v.target_deltaz*(v.particle_age / v.particle_lifetime)"]
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["0.4*v.rsize", "0.4*v.rsize"],
|
||||
|
||||
@@ -227,14 +227,6 @@
|
||||
{"origin": [3.1, 20.8, -0.84], "size": [2, 1, 1], "inflate": 0.1, "uv": [60, 42]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_closed_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [3.6, 19.7, -0.91],
|
||||
"cubes": [
|
||||
{"origin": [2.1, 17.2, -0.66], "size": [3, 5, 1], "inflate": 0.25, "uv": [98, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "face_ditto_left",
|
||||
"parent": "head_left",
|
||||
@@ -340,14 +332,6 @@
|
||||
{"origin": [-5.1, 20.8, -0.84], "size": [2, 1, 1], "inflate": 0.1, "uv": [51, 42], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_closed_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-3.6, 19.7, -0.91],
|
||||
"cubes": [
|
||||
{"origin": [-5.1, 17.2, -0.66], "size": [3, 5, 1], "inflate": 0.25, "uv": [89, 2], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "face_ditto_right",
|
||||
"parent": "head_right",
|
||||
|
||||
@@ -134,246 +134,107 @@
|
||||
"jaw2": [0, 27, -7.35]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "head_left",
|
||||
"parent": "head_angle",
|
||||
"pivot": [3.53, 31, 0.25],
|
||||
"rotation": [5, 0, 0],
|
||||
"cubes": [
|
||||
{"origin": [2.53, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 21], "mirror": true},
|
||||
{"origin": [3.63, 31.5, 0.25], "size": [0, 3, 5], "uv": [40, 55], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.53, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.54, 29, -4.25], "size": [1, 5, 4], "uv": [88, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_neutral_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.54, 33, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.54, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [88, 12]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_angry_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.44, 32.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.44, 31, -4.25], "size": [1, 3, 4], "inflate": 0.021, "uv": [88, 26]},
|
||||
{"origin": [3.44, 31.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [95, 27]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_sad_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.43, 33, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.44, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [88, 19]},
|
||||
{"origin": [3.44, 32.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [95, 20]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.55, 31, -3.05],
|
||||
"cubes": [
|
||||
{"origin": [3.55, 29.5, -4.05], "size": [1, 3, 2], "uv": [95, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_shine_left",
|
||||
"parent": "pupil_left",
|
||||
"pivot": [4.55, 31.5, -3.05],
|
||||
"cubes": [
|
||||
{"origin": [3.56, 30.5, -3.55], "size": [1, 2, 1], "uv": [106, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_eye_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.44, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.44, 29, -4.25], "size": [1, 5, 4], "uv": [99, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_pupil_left",
|
||||
"parent": "open_eye_left",
|
||||
"pivot": [4.45, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.45, 31, -3.75], "size": [1, 1, 3], "uv": [99, 14]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_neutral_left",
|
||||
"parent": "open_eye_left",
|
||||
"pivot": [4.34, 33, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.34, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [88, 12]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_happy_left",
|
||||
"parent": "open_eye_left",
|
||||
"pivot": [4.35, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.35, 31, -3.75], "size": [1, 2, 3], "uv": [99, 20]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.44, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.44, 29, -4.25], "size": [1, 5, 4], "inflate": 0.022, "uv": [99, 26]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "locator_eye_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.54, 31, -2.46],
|
||||
"rotation": [0, -90, 0],
|
||||
"locators": {
|
||||
"eye2": [4.54, 31, -2.46]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "face_ditto_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.435, 32, -2.25]
|
||||
},
|
||||
{
|
||||
"name": "eye_ditto_left",
|
||||
"parent": "face_ditto_left",
|
||||
"pivot": [4.435, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [113, 1]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_closed_ditto_left",
|
||||
"parent": "face_ditto_left",
|
||||
"pivot": [4.335, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [112, 2]},
|
||||
{"origin": [4.345, 32, -2.75], "size": [0, 1, 1], "uv": [113, 5]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "head_right",
|
||||
"parent": "head_angle",
|
||||
"pivot": [-3.53, 31, 0.25],
|
||||
"pivot": [-3.525, 31, 0.25],
|
||||
"rotation": [5, 0, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4.53, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 11]},
|
||||
{"origin": [-3.63, 31.5, 0.25], "size": [0, 3, 5], "uv": [40, 55]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-4.53, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.54, 29, -4.25], "size": [1, 5, 4], "uv": [66, 2], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_neutral_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.54, 33, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.54, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [66, 12], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_angry_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.44, 32.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.44, 31, -4.25], "size": [1, 3, 4], "inflate": 0.021, "uv": [66, 26], "mirror": true},
|
||||
{"origin": [-4.44, 31.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [73, 27], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_sad_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.43, 33, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.44, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [66, 19], "mirror": true},
|
||||
{"origin": [-4.44, 32.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [73, 20], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.55, 31, -3.05],
|
||||
"cubes": [
|
||||
{"origin": [-4.55, 29.5, -4.05], "size": [1, 3, 2], "uv": [73, 0], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_shine_right",
|
||||
"parent": "pupil_right",
|
||||
"pivot": [-4.55, 31.5, -3.05],
|
||||
"cubes": [
|
||||
{"origin": [-4.56, 30.5, -3.55], "size": [1, 2, 1], "uv": [84, 2], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_eye_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-4.44, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.44, 29, -4.25], "size": [1, 5, 4], "uv": [77, 2], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_pupil_right",
|
||||
"parent": "open_eye_right",
|
||||
"pivot": [-4.45, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.45, 31, -3.75], "size": [1, 1, 3], "uv": [77, 14], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_neutral_right",
|
||||
"parent": "open_eye_right",
|
||||
"pivot": [-4.34, 33, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.34, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [66, 12], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open_happy_right",
|
||||
"parent": "open_eye_right",
|
||||
"pivot": [-4.35, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.35, 31, -3.75], "size": [1, 2, 3], "uv": [77, 20], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-4.44, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.44, 29, -4.25], "size": [1, 5, 4], "inflate": 0.022, "uv": [77, 26], "mirror": true}
|
||||
{"origin": [-4.525, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 11]},
|
||||
{"origin": [-3.625, 31.5, 0.25], "size": [0, 3, 5], "uv": [52, 55]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "locator_eye_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-4.54, 31, -2.46],
|
||||
"pivot": [-4.5, 31, -2.5],
|
||||
"rotation": [0, 90, 0],
|
||||
"locators": {
|
||||
"eye1": [-4.54, 31, -2.46]
|
||||
"eye1": [-4.5, 31, -2.5]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "eye_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-4.525, 31.35, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.525, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [66, 2], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_neutral_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.525, 32.85095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [-4.505, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [66, 12], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_angry_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.435, 32.1, -2.5],
|
||||
"cubes": [
|
||||
{"origin": [-4.405, 30.85, -4.25], "size": [1, 3, 4], "inflate": 0.05, "uv": [66, 26], "mirror": true},
|
||||
{"origin": [-4.44, 31.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [73, 27], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_sad_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.435, 32.85, -2.5],
|
||||
"cubes": [
|
||||
{"origin": [-4.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [66, 19], "mirror": true},
|
||||
{"origin": [-4.44, 32.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [73, 20], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_right",
|
||||
"parent": "eye_right",
|
||||
"pivot": [-4.525, 30.85095, -3.02179],
|
||||
"cubes": [
|
||||
{"origin": [-4.525, 29.35, -4.04], "size": [1, 3, 2], "inflate": 0.02, "uv": [73, 0], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_right2",
|
||||
"parent": "pupil_right",
|
||||
"pivot": [-4.55, 31.35095, -3.02179],
|
||||
"cubes": [
|
||||
{"origin": [-4.55, 30.35, -3.55], "size": [1, 2, 1], "uv": [84, 2], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-3.925, 31.35095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [-4.425, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [77, 2], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_eyelid_right",
|
||||
"parent": "eyelid_right",
|
||||
"pivot": [-4.425, 30.80095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [-4.435, 30.3, -3.75], "size": [1, 1, 3], "inflate": 0.01, "uv": [77, 14], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_right2",
|
||||
"parent": "eyelid_right",
|
||||
"pivot": [-3.925, 32.85095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [-4.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [66, 12], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_happy_right",
|
||||
"parent": "eyelid_right",
|
||||
"pivot": [-4.325, 30.80095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [-4.325, 30.30095, -3.77179], "size": [1, 2, 3], "inflate": 0.05, "uv": [77, 20], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "face_ditto_right",
|
||||
"parent": "head_right",
|
||||
@@ -384,7 +245,7 @@
|
||||
"parent": "face_ditto_right",
|
||||
"pivot": [-4.435, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [118, 1], "mirror": true}
|
||||
{"origin": [-4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [113, 1]}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -392,8 +253,131 @@
|
||||
"parent": "face_ditto_right",
|
||||
"pivot": [-4.335, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [-4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [117, 2], "mirror": true},
|
||||
{"origin": [-4.345, 32, -2.75], "size": [0, 1, 1], "uv": [118, 5], "mirror": true}
|
||||
{"origin": [-4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [112, 2]},
|
||||
{"origin": [-4.345, 32, -2.75], "size": [0, 1, 1], "uv": [113, 5]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "head_left",
|
||||
"parent": "head_angle",
|
||||
"pivot": [3.525, 31, 0.25],
|
||||
"rotation": [5, 0, 0],
|
||||
"cubes": [
|
||||
{"origin": [2.525, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 21], "mirror": true},
|
||||
{"origin": [3.625, 31.5, 0.25], "size": [0, 3, 5], "uv": [40, 55], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "locator_eye_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.5, 31, -2.5],
|
||||
"rotation": [0, -90, 0],
|
||||
"locators": {
|
||||
"eye2": [4.5, 31, -2.5]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "eye_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.525, 31.35, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.525, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [88, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_neutral_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.525, 32.85095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [3.505, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [88, 12]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_angry_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.435, 32.1, -2.5],
|
||||
"cubes": [
|
||||
{"origin": [3.405, 30.85, -4.25], "size": [1, 3, 4], "inflate": 0.05, "uv": [88, 26]},
|
||||
{"origin": [3.44, 31.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [95, 27]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_sad_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.435, 32.85, -2.5],
|
||||
"cubes": [
|
||||
{"origin": [3.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [88, 19]},
|
||||
{"origin": [3.44, 32.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [95, 20]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.525, 30.85095, -3.02179],
|
||||
"cubes": [
|
||||
{"origin": [3.525, 29.35, -4.04], "size": [1, 3, 2], "inflate": 0.02, "uv": [95, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_left2",
|
||||
"parent": "pupil_left",
|
||||
"pivot": [4.55, 31.35095, -3.02179],
|
||||
"cubes": [
|
||||
{"origin": [3.55, 30.35, -3.55], "size": [1, 2, 1], "uv": [106, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [3.925, 31.35095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [3.425, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [99, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_eyelid_left",
|
||||
"parent": "eyelid_left",
|
||||
"pivot": [4.425, 30.80095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [3.435, 30.3, -3.75], "size": [1, 1, 3], "inflate": 0.01, "uv": [99, 14]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_left2",
|
||||
"parent": "eyelid_left",
|
||||
"pivot": [3.925, 32.85095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [3.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [88, 12]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_happy_left",
|
||||
"parent": "eyelid_left",
|
||||
"pivot": [4.325, 30.80095, -2.27179],
|
||||
"cubes": [
|
||||
{"origin": [3.325, 30.30095, -3.77179], "size": [1, 2, 3], "inflate": 0.05, "uv": [99, 20]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "face_ditto_left",
|
||||
"parent": "head_left",
|
||||
"pivot": [4.435, 32, -2.25]
|
||||
},
|
||||
{
|
||||
"name": "eye_ditto_left",
|
||||
"parent": "face_ditto_left",
|
||||
"pivot": [4.435, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [118, 1], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_closed_ditto_left",
|
||||
"parent": "face_ditto_left",
|
||||
"pivot": [4.335, 32, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [117, 2], "mirror": true},
|
||||
{"origin": [4.345, 32, -2.75], "size": [0, 1, 1], "uv": [118, 5], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -830,6 +830,11 @@
|
||||
{"origin": [-3.5, 38.3, 33.75], "size": [7, 5, 9], "uv": [106, 33]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tongue6",
|
||||
"parent": "tongue5",
|
||||
"pivot": [0, 40.8, 40.5]
|
||||
},
|
||||
{
|
||||
"name": "muzzle",
|
||||
"parent": "head_angle",
|
||||
@@ -908,15 +913,6 @@
|
||||
{"origin": [-0.1, 44.3, -8.11], "size": [4, 2, 1], "uv": [87, 72]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_base_left",
|
||||
"parent": "eye_left_base",
|
||||
"pivot": [1.9, 43.9, -8.11],
|
||||
"cubes": [
|
||||
{"origin": [-0.1, 45.3, -8.11], "size": [4, 1, 1], "uv": [87, 76]},
|
||||
{"origin": [-0.1, 41.9, -8.11], "size": [4, 4, 1], "uv": [87, 79]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_left",
|
||||
"parent": "eye_left_base",
|
||||
@@ -954,7 +950,7 @@
|
||||
"parent": "eye_left",
|
||||
"pivot": [1.9, 44.2, -8.07],
|
||||
"cubes": [
|
||||
{"origin": [0.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [87, 85]}
|
||||
{"origin": [0.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [87, 76]}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -962,7 +958,7 @@
|
||||
"parent": "eye_left",
|
||||
"pivot": [1.9, 44.2, -8.07],
|
||||
"cubes": [
|
||||
{"origin": [0.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [87, 88]}
|
||||
{"origin": [0.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [87, 79]}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -999,15 +995,6 @@
|
||||
{"origin": [-3.9, 44.3, -8.11], "size": [4, 2, 1], "uv": [76, 72], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eyelid_base_right",
|
||||
"parent": "eye_right_base",
|
||||
"pivot": [-1.9, 43.9, -8.11],
|
||||
"cubes": [
|
||||
{"origin": [-3.9, 45.3, -8.11], "size": [4, 1, 1], "uv": [76, 76], "mirror": true},
|
||||
{"origin": [-3.9, 41.9, -8.11], "size": [4, 4, 1], "uv": [76, 79], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_right",
|
||||
"parent": "eye_right_base",
|
||||
@@ -1045,7 +1032,7 @@
|
||||
"parent": "eye_right",
|
||||
"pivot": [-1.9, 44.2, -8.07],
|
||||
"cubes": [
|
||||
{"origin": [-3.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [76, 85], "mirror": true}
|
||||
{"origin": [-3.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [76, 76], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1053,7 +1040,7 @@
|
||||
"parent": "eye_right",
|
||||
"pivot": [-1.9, 44.2, -8.07],
|
||||
"cubes": [
|
||||
{"origin": [-3.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [76, 88], "mirror": true}
|
||||
{"origin": [-3.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [76, 79], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -846,6 +846,14 @@
|
||||
"cubes": [
|
||||
{"origin": [-2, -1, 27], "size": [4, 1, 4], "inflate": -0.01, "uv": [104, 47]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "locator_seat_1",
|
||||
"parent": "surfboard",
|
||||
"pivot": [0, 14.5, 3],
|
||||
"locators": {
|
||||
"seat_1": [19, 1.5, 0]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"poses": {
|
||||
"standing": {
|
||||
"poseTypes": ["STAND", "NONE", "PORTRAIT", "PROFILE"],
|
||||
"isBattle": false,
|
||||
"animations": [
|
||||
"q.look('neck', 1, 1, 70, -10, 20, -20)",
|
||||
"q.look('head_ai')",
|
||||
@@ -34,9 +35,6 @@
|
||||
"part": "wing_closed_left",
|
||||
"isVisible": true
|
||||
}
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('fearow', 'blink')"
|
||||
]
|
||||
},
|
||||
"walking": {
|
||||
@@ -67,9 +65,6 @@
|
||||
"part": "wing_closed_left",
|
||||
"isVisible": true
|
||||
}
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('fearow', 'blink')"
|
||||
]
|
||||
},
|
||||
"hover": {
|
||||
@@ -101,9 +96,6 @@
|
||||
"part": "wing_closed_left",
|
||||
"isVisible": false
|
||||
}
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('fearow', 'blink')"
|
||||
]
|
||||
},
|
||||
"fly": {
|
||||
@@ -136,9 +128,6 @@
|
||||
"part": "wing_closed_left",
|
||||
"isVisible": false
|
||||
}
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('fearow', 'blink')"
|
||||
]
|
||||
},
|
||||
"sleep": {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('froakie', 'ground_idle')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"walking": {
|
||||
"poseTypes": ["WALK"],
|
||||
@@ -36,7 +36,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('froakie', 'ground_walk')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"swimming": {
|
||||
"poseTypes": ["WALK","SWIM"],
|
||||
@@ -46,7 +46,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('froakie', 'water_swim')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"float": {
|
||||
"poseTypes": ["STAND","FLOAT"],
|
||||
@@ -55,7 +55,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('froakie', 'water_idle')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"sleep": {
|
||||
"poseTypes": ["SLEEP"],
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"q.look('head_ai', 1, 1, 5, -45, 45, -45)",
|
||||
"q.bedrock('frogadier', 'ground_idle')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"walking": {
|
||||
"poseTypes": ["WALK"],
|
||||
@@ -36,7 +36,7 @@
|
||||
"q.look('head_ai', 1, 1, 0, -45, 45, -45)",
|
||||
"q.bedrock('frogadier', 'ground_walk')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"swimming": {
|
||||
"poseTypes": ["WALK","SWIM"],
|
||||
@@ -46,7 +46,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('frogadier', 'water_swim')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"float": {
|
||||
"poseTypes": ["STAND","FLOAT"],
|
||||
@@ -55,7 +55,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('frogadier', 'water_idle')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"sleep": {
|
||||
"poseTypes": ["SLEEP"],
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"q.bedrock('greninja', math.random(0, 1) < 0.4 ? 'ground_idle_alt' : 'ground_idle')"
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('greninja', 'blink')","q.bedrock_quirk('greninja', q.array('quirk_look_left', 'quirk_look_right'), 8, 30, 1)"
|
||||
"q.bedrock_quirk('greninja', q.array('quirk_look_left', 'quirk_look_right'), 8, 30, 1)"
|
||||
]
|
||||
},
|
||||
"walking": {
|
||||
@@ -78,7 +78,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'ground_walk')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"running": {
|
||||
"poseTypes": ["WALK"],
|
||||
@@ -89,7 +89,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'ground_run')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"]
|
||||
"quirks": []
|
||||
},
|
||||
"surface-swimming": {
|
||||
"poseTypes": ["WALK","SWIM"],
|
||||
@@ -101,7 +101,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_swim')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"quirks": [],
|
||||
"transformedParts": [
|
||||
{
|
||||
"part": "body",
|
||||
@@ -118,7 +118,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_idle')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"quirks": [],
|
||||
"transformedParts": [
|
||||
{
|
||||
"part": "body",
|
||||
@@ -136,7 +136,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_swim')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"quirks": [],
|
||||
"transformedParts": [
|
||||
{
|
||||
"part": "body",
|
||||
@@ -153,7 +153,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_idle')"
|
||||
],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"quirks": [],
|
||||
"transformedParts": [
|
||||
{
|
||||
"part": "body",
|
||||
|
||||
@@ -383,7 +383,7 @@
|
||||
"item.cobblemon.eject_pack.tooltip_1": "Switches out the holder if any stats are lowered",
|
||||
"item.cobblemon.eject_pack.tooltip_2": "Consumed after use",
|
||||
"item.cobblemon.electric_seed": "Electric Seed",
|
||||
"item.cobblemon.electric_seed.tooltip": "Boosts the holder's Defense in Electric Terrain",
|
||||
"item.cobblemon.electric_seed.tooltip": "Boosts the holder's Defense in Electric terrain",
|
||||
"item.cobblemon.everstone": "Everstone",
|
||||
"item.cobblemon.everstone.tooltip": "Prevents the holder from evolving",
|
||||
"item.cobblemon.eviolite": "Eviolite",
|
||||
@@ -402,7 +402,7 @@
|
||||
"item.cobblemon.focus_sash.tooltip_1": "Prevents the holder from fainting in a single hit",
|
||||
"item.cobblemon.focus_sash.tooltip_2": "Consumed after use",
|
||||
"item.cobblemon.grassy_seed": "Grassy Seed",
|
||||
"item.cobblemon.grassy_seed.tooltip": "Boosts the holder's Defense in Grassy Terrain",
|
||||
"item.cobblemon.grassy_seed.tooltip": "Boosts the holder's Defense in Grassy terrain",
|
||||
"item.cobblemon.grip_claw": "Grip Claw",
|
||||
"item.cobblemon.hard_stone": "Hard Stone",
|
||||
"item.cobblemon.hard_stone.tooltip": "Boosts the power of the holder's Rock-type moves",
|
||||
@@ -507,7 +507,7 @@
|
||||
"item.cobblemon.mirror_herb.tooltip_1": "Allows the holder to mirror an opponent's stat increases to boost its own stats",
|
||||
"item.cobblemon.mirror_herb.tooltip_2": "Consumed after use",
|
||||
"item.cobblemon.misty_seed": "Misty Seed",
|
||||
"item.cobblemon.misty_seed.tooltip": "Boosts the holder's Sp. Def in Misty Terrain",
|
||||
"item.cobblemon.misty_seed.tooltip": "Boosts the holder's Sp. Def in Misty terrain",
|
||||
"item.cobblemon.power_herb": "Power Herb",
|
||||
"item.cobblemon.power_herb.tooltip_1": "Allows the holder to immediately use a move that normally requires a change of turn to charge",
|
||||
"item.cobblemon.power_herb.tooltip_2": "Consumed after use",
|
||||
@@ -535,7 +535,7 @@
|
||||
"item.cobblemon.power_weight": "Power Weight",
|
||||
"item.cobblemon.power_weight.tooltip": "Grants 8 HP EVs when gaining experience in battle",
|
||||
"item.cobblemon.psychic_seed": "Psychic Seed",
|
||||
"item.cobblemon.psychic_seed.tooltip": "Boosts the holder's Sp. Def in Psychic Terrain",
|
||||
"item.cobblemon.psychic_seed.tooltip": "Boosts the holder's Sp. Def in Psychic terrain",
|
||||
"item.cobblemon.flame_orb": "Flame Orb",
|
||||
"item.cobblemon.flame_orb.tooltip": "Burns the holder",
|
||||
"item.cobblemon.smoke_ball": "Smoke Ball",
|
||||
|
||||
|
Before Width: | Height: | Size: 417 B After Width: | Height: | Size: 788 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 13 KiB |
@@ -42,8 +42,7 @@
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"condition": "q.target_fainted == false",
|
||||
"pause": 1.25
|
||||
"pause": 0.5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -141,8 +141,7 @@
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"condition": "q.target_fainted == false",
|
||||
"pause": 1.25
|
||||
"pause": 0.5
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"infiniteAltitude": "false",
|
||||
"infiniteStamina": "false",
|
||||
"timeToRollCorrect": "0.5",
|
||||
"timeToRollCorrect": "0.25",
|
||||
"handlingExpr": "q.get_ride_stats('SKILL', 'AIR', 45.0, 10.0)",
|
||||
"horizontalPitchExpr": "q.get_ride_stats('SKILL', 'AIR', 0.2, 0.1)",
|
||||
"mouseInputMult": "q.get_ride_stats('SKILL', 'AIR', 1.2, 0.8)",
|
||||
@@ -11,5 +11,13 @@
|
||||
"stamDrainHorzMod": "1.5",
|
||||
"stamDrainPercRateGlide": "0.25",
|
||||
"stamDrainPerRateDive": "0.25",
|
||||
"glideSpeedExpr": "q.get_ride_stats('JUMP', 'AIR', 2.0, 1.0)"
|
||||
"glideSpeedExpr": "q.get_ride_stats('JUMP', 'AIR', 2.0, 1.0)",
|
||||
"cameraRollEngage": "46.0",
|
||||
"cameraRollRelease": "30.0",
|
||||
"cameraRollHalflife": "0.25",
|
||||
"hoverLookYawLimit": "90.0",
|
||||
"turnRollLimit": "45.0",
|
||||
"maxRoll": "45.0",
|
||||
"rollSettleAngle": "5.0",
|
||||
"handRollShare": "0.5"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"infiniteStamina": "false",
|
||||
"canJump": "true",
|
||||
"canSprint": "true",
|
||||
"lookYawLimit": "105.0",
|
||||
"speedExpr": "q.get_ride_stats('SPEED', 'LAND', 0.45, 0.2)",
|
||||
"accelerationExpr": "q.get_ride_stats('ACCELERATION', 'LAND', 0.05, 6.0)",
|
||||
"staminaExpr": "q.get_ride_stats('STAMINA', 'LAND', 10.0, 4.0)",
|
||||
"jumpExpr": "q.get_ride_stats('JUMP', 'LAND', 5.0, 2.0)",
|
||||
"handlingExpr": "q.get_ride_stats('SKILL', 'LAND', 180.0, 100.0)",
|
||||
"handRollShare": "0.5",
|
||||
"cameraTiltEngage": "50.0",
|
||||
"cameraTiltRelease": "40.0",
|
||||
"cameraHalflife": "0.3",
|
||||
"maxTilt": "50.0",
|
||||
"sideGrip": "0.05",
|
||||
"topSpeedTurnShare": "0.6",
|
||||
"topSpeedFovBoost": "0.1",
|
||||
"lookRecenterDelay": "1.0",
|
||||
"hoverHeight": "1.0",
|
||||
"jumpChargeSeconds": "0.75",
|
||||
"sprintSpeedFactor": "2.0",
|
||||
"sprintTurnShare": "0.5",
|
||||
"sprintFovBump": "0.1"
|
||||
}
|
||||
@@ -53,7 +53,7 @@
|
||||
"riding": {
|
||||
"behaviours": {
|
||||
"LAND": {
|
||||
"key": "cobblemon:land/horse",
|
||||
"key": "cobblemon:land/climb",
|
||||
"rideSounds": [],
|
||||
"stats": {
|
||||
"ACCELERATION": "55-85",
|
||||
|
||||
@@ -48,6 +48,26 @@
|
||||
"height": 2.15,
|
||||
"fixed": false
|
||||
},
|
||||
"riding": {
|
||||
"behaviours": {
|
||||
"LAND": {
|
||||
"key": "cobblemon:air/board",
|
||||
"rideSounds": [],
|
||||
"stats": {
|
||||
"ACCELERATION": "50-100",
|
||||
"JUMP": "50-100",
|
||||
"SKILL": "50-100",
|
||||
"SPEED": "50-100",
|
||||
"STAMINA": "50-100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"seats": [
|
||||
{
|
||||
"locator": "seat_1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"behaviour": {
|
||||
"resting": {
|
||||
"canSleep": true,
|
||||
|
||||