Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
087a167bfa | ||
|
|
461d21a87d | ||
|
|
78953357a8 | ||
|
|
bdfabb3724 | ||
|
|
3adb6144a3 | ||
|
|
f4140a097d | ||
|
|
b79dbcca80 | ||
|
|
5ac12a897a | ||
|
|
ae5f7c83f2 | ||
|
|
f14de9ab58 | ||
|
|
8a1c846c2c | ||
|
|
459e7d3490 | ||
|
|
5a9be8944f | ||
|
|
3bb941b087 | ||
|
|
384b496d76 | ||
|
|
4caa736e0b | ||
|
|
c3e9051918 | ||
|
|
fe940caafc | ||
|
|
a1b930a21f | ||
|
|
7728ff3892 | ||
|
|
0b38e063d2 | ||
|
|
236b98fa78 | ||
|
|
ac61e0d4d0 | ||
|
|
d8956fd66f | ||
|
|
50a5a78dd4 | ||
|
|
76c2654399 | ||
|
|
65bcd118a3 | ||
|
|
bf6f97e740 | ||
|
|
4e48e2ff9b | ||
|
|
faa488b153 | ||
|
|
93dbc015fb | ||
|
|
7edf6669d8 |
No files matched your search
@@ -1,31 +1,28 @@
|
||||
# Changelog
|
||||
## [1.8.x (MONTH xth, 2026)](#1-8-x)
|
||||
## [1.8.5 (MONTH xth, 2026)](#1-8-5)
|
||||
|
||||
### Additions
|
||||
|
||||
### New rideable Pokémon
|
||||
### Particles
|
||||
|
||||
### Pokémon Added
|
||||
### Cries added/updated for the following Pokémon
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
|
||||
### Changes
|
||||
- Reworked spawn presets and added structure tags for spawns
|
||||
|
||||
### Fixes
|
||||
- Fixed blank Cooking Pot recipe book entries and missing tabs on Fabric.
|
||||
- Fixed Minior's "Shields Down" ability not working as intended
|
||||
|
||||
### Developer
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `q.target_fainted` function to move action effects that checks if any opponents fainted from the move.
|
||||
- Added a `loot_table` drop entry type.
|
||||
- Accepts an `id` field, pointing to a vanilla loot table.
|
||||
- All base mod Pokémon have been moved to this new system.
|
||||
- Added the `cobblemon:biome` and `cobblemon:pokemon_properties` loot conditions.
|
||||
- `cobblemon:biome` returns true if the biome of death matches the `biome` field.
|
||||
- `cobblemon:pokemon_properties` returns true if the Pokémon matches the `properties` field.
|
||||
|
||||
### Particles
|
||||
|
||||
#### New Move Animations
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
### Localization
|
||||
@@ -0,0 +1,25 @@
|
||||
# Changelog
|
||||
## [1.8.4 (October 27th, 2026)](#1-8-4)
|
||||
|
||||
### Changes
|
||||
- The Froakie line now blinks. The world will never be the same.
|
||||
|
||||
### Fixes
|
||||
- Fixed heaps of spawns not working. This was caused by us missing something before release. RIP.
|
||||
- Fixed a crash caused by Fearow blinking while you're riding one. Makes sense to me.
|
||||
- Fixed Minior's "Shields Down" ability being very broken.
|
||||
- Fixed Dusclops using Squirtle's cry sound (lol).
|
||||
- Fixed blank Cooking Pot recipe book entries and missing tabs on Fabric.
|
||||
- Fixed a hitbox issue with placeable Orb held items attached to a ceiling.
|
||||
- Fixed something sneaky. I'll tell you about it later.
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `q.target_fainted` function to move action effects that checks if any opponents fainted from the move.
|
||||
|
||||
### Particles
|
||||
- Updated Water Shuriken effect to be timed better when the opponent faints.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Korean
|
||||
- Simplified Chinese
|
||||
@@ -75,10 +75,9 @@ 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) {
|
||||
// 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);
|
||||
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
|
||||
Seat seat = pokemonEntity.getSeats().get(seatIndex);
|
||||
MatrixWrapper locator = delegate.getLocatorStates().get(seat.getLocator());
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
|
||||
@@ -100,8 +99,7 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
|
||||
@Unique
|
||||
private Vec3 cobblemon$getEyePosition(float partialTicks, PokemonEntity pokemonEntity, Vec3 locatorOffset) {
|
||||
// The ride owns the orientation, the passenger's own controller is never active
|
||||
OrientationController controller = ((OrientationControllable) pokemonEntity).getOrientationController();
|
||||
OrientationController controller = this.getOrientationController();
|
||||
|
||||
float currEyeHeight = this.getEyeHeight();
|
||||
Vec3 offset = locatorOffset.add(pokemonEntity.position());
|
||||
@@ -109,7 +107,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 = offset.add(rotatedEyeHeight);
|
||||
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.reset();
|
||||
MountedCameraRenderer.INSTANCE.setSmoothRotation(null);
|
||||
original.call(instance, x, y, z);
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,11 @@
|
||||
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;
|
||||
@@ -43,14 +40,6 @@ 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,6 +32,7 @@ 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;
|
||||
@@ -49,13 +50,11 @@ 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;
|
||||
@@ -82,28 +81,30 @@ 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()) {
|
||||
// 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));
|
||||
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()));
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
// Look angles get seeded by the camera on its first rolling frame
|
||||
// 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()));
|
||||
}
|
||||
|
||||
vehicleController.setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,6 @@ 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;
|
||||
@@ -24,12 +20,13 @@ 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.Unit;
|
||||
import kotlin.Pair;
|
||||
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;
|
||||
@@ -45,11 +42,6 @@ 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;
|
||||
|
||||
@@ -168,48 +160,74 @@ public abstract class MouseHandlerMixin {
|
||||
}
|
||||
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
var pokemonVehicle = (PokemonEntity) vehicle;
|
||||
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
|
||||
);
|
||||
});
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// 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;
|
||||
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;
|
||||
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));
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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;
|
||||
});
|
||||
|
||||
// The camera sets the player's rotation from the look every frame so vanilla turn would only get overwritten
|
||||
return false;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@Inject(method = "handleAccumulatedMovement", at = @At("HEAD"))
|
||||
@@ -230,9 +248,28 @@ 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 RideSteerInput cobblemon$getSteerInput(OrientationController controller, double mouseX, double mouseY, double deltaTime) {
|
||||
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;
|
||||
|
||||
var invertYaw = Cobblemon.config.getInvertYaw();
|
||||
var invertPitch = Cobblemon.config.getInvertPitch();
|
||||
|
||||
@@ -245,18 +282,21 @@ public abstract class MouseHandlerMixin {
|
||||
|
||||
var pitch = (swapXAndY ? xValue : yValue) * (invertPitch? -1.0f : 1.0f);
|
||||
|
||||
return new RideSteerInput(
|
||||
controller,
|
||||
yaw,
|
||||
pitch,
|
||||
xMouseSmoother,
|
||||
yMouseSmoother,
|
||||
cobblemon$getRidingSensitivity(),
|
||||
deltaTime,
|
||||
yawSmoother,
|
||||
pitchSmoother,
|
||||
rollSmoother
|
||||
);
|
||||
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
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Unique
|
||||
@@ -265,33 +305,31 @@ 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$addLook(LocalPlayer player, float yawDelta, float pitchDelta) {
|
||||
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)
|
||||
var playerRotater = (RidePassenger)player;
|
||||
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()));
|
||||
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));
|
||||
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 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);
|
||||
}
|
||||
private boolean cobblemon$shouldUseAngVelSmoothing() {
|
||||
var player = minecraft.player;
|
||||
if (player == null) return true;
|
||||
|
||||
@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);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,6 @@ 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)
|
||||
@@ -94,9 +91,6 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.drop.CommandDropEntry
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.drop.EvolutionItemDropEntry
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.drop.LootTableDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.habitats.HabitatPools
|
||||
@@ -90,6 +91,7 @@ import com.cobblemon.mod.common.events.EntityCallbackHandler
|
||||
import com.cobblemon.mod.common.events.PokedexHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
import com.cobblemon.mod.common.events.StatHandler
|
||||
import com.cobblemon.mod.common.loot.CobblemonLootItemConditions
|
||||
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
|
||||
import com.cobblemon.mod.common.permission.LaxPermissionValidator
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
@@ -225,10 +227,12 @@ object Cobblemon {
|
||||
implementation.registerEntitySubPredicates()
|
||||
implementation.registerMobEffects()
|
||||
DispenserBehaviorRegistry.registerDispenserBehaviors()
|
||||
CobblemonLootItemConditions.touch()
|
||||
|
||||
DropEntry.register("command", CommandDropEntry::class.java)
|
||||
DropEntry.register("item", ItemDropEntry::class.java, isDefault = true)
|
||||
DropEntry.register("evolution", EvolutionItemDropEntry::class.java)
|
||||
DropEntry.register("loot_table", LootTableDropEntry::class.java)
|
||||
|
||||
ExperienceGroups.registerDefaults()
|
||||
CaptureCalculators.registerDefaults()
|
||||
|
||||
@@ -118,6 +118,7 @@ interface CobblemonImplementation {
|
||||
fun registerMenu()
|
||||
|
||||
fun registerEntitySubPredicates()
|
||||
fun registerLootItemConditions()
|
||||
|
||||
/**
|
||||
* TODO
|
||||
|
||||
@@ -20,7 +20,6 @@ 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
|
||||
@@ -57,7 +56,6 @@ object CobblemonRideSettings : DataRegistry {
|
||||
var hover = HoverSettings()
|
||||
var jet = JetSettings()
|
||||
var rocket = RocketSettings()
|
||||
var board = BoardSettings()
|
||||
|
||||
var horse = HorseSettings()
|
||||
var minekart = MinekartSettings()
|
||||
@@ -77,7 +75,6 @@ object CobblemonRideSettings : DataRegistry {
|
||||
hover = hover,
|
||||
jet = jet,
|
||||
rocket = rocket,
|
||||
board = board,
|
||||
horse = horse,
|
||||
minekart = minekart,
|
||||
vehicle = vehicle,
|
||||
@@ -96,7 +93,6 @@ 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)
|
||||
|
||||
@@ -10,9 +10,14 @@ package com.cobblemon.mod.common.api.drop
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.registries.Registries
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -105,7 +110,11 @@ class DropTable {
|
||||
) {
|
||||
LOOT_DROPPED.postThen(
|
||||
event = LootDroppedEvent(this, player, entity, drops),
|
||||
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
|
||||
ifSucceeded = {
|
||||
val pokemon = (entity as? PokemonEntity)?.pokemon?.species?.resourceIdentifier ?: return@postThen
|
||||
it.drops.forEach { it.drop(entity, world, pos, player) }
|
||||
// val lootKey = ResourceKey.create(Registries.LOOT_TABLE, cobblemonResource("pokemon/${pokemon}"))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.cobblemon.mod.common.api.drop
|
||||
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* A drop entry that points to a vanilla loot table.
|
||||
*/
|
||||
class LootTableDropEntry(
|
||||
override var percentage: Float,
|
||||
override var quantity: Int,
|
||||
override var maxSelectableTimes: Int,
|
||||
val id: ResourceLocation
|
||||
) : DropEntry {
|
||||
override fun drop(
|
||||
entity: LivingEntity?,
|
||||
world: ServerLevel,
|
||||
pos: Vec3,
|
||||
player: ServerPlayer?
|
||||
) {
|
||||
val key = ResourceKey.create(Registries.LOOT_TABLE, id)
|
||||
val playerEntity = player as? LivingEntity
|
||||
if (entity == null) return
|
||||
if (playerEntity != null) {
|
||||
val params = LootParams.Builder(world)
|
||||
.withParameter(LootContextParams.ATTACKING_ENTITY, playerEntity)
|
||||
.withParameter(LootContextParams.DAMAGE_SOURCE, playerEntity.damageSources().genericKill())
|
||||
.withParameter(LootContextParams.ORIGIN, pos)
|
||||
.withParameter(LootContextParams.THIS_ENTITY, entity)
|
||||
.create(LootContextParamSets.ENTITY)
|
||||
val drops = world.server.reloadableRegistries().getLootTable(key)
|
||||
.getRandomItems(params)
|
||||
for (stack in drops) {
|
||||
if (stack.isEmpty) continue
|
||||
|
||||
val itemEntity = ItemEntity(world, pos.x, pos.y, pos.z, stack)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
world.addFreshEntity(itemEntity)
|
||||
}
|
||||
} else {
|
||||
val params = LootParams.Builder(world)
|
||||
.create(LootContextParamSets.EMPTY)
|
||||
val drops = world.server.reloadableRegistries().getLootTable(key)
|
||||
.getRandomItems(params)
|
||||
for (stack in drops) {
|
||||
if (stack.isEmpty) continue
|
||||
|
||||
val itemEntity = ItemEntity(world, pos.x, pos.y, pos.z, stack)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
world.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,7 @@ 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
|
||||
// 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
|
||||
private const val RENDER_SMOOTH_HALFLIFE = 0.1f // seconds to close ~half the gap
|
||||
}
|
||||
|
||||
open var active: Boolean = false
|
||||
@@ -102,7 +101,7 @@ 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, RENDER_SMOOTH_HALFLIFE, TICK_DT)
|
||||
simpleSpringDamperQuat(renderSpring, targetQuat, 0.15f, TICK_DT)
|
||||
|
||||
val newRenderOrientation = Matrix3f()
|
||||
renderSpring.quat.get(newRenderOrientation)
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
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,20 +12,15 @@ 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
|
||||
|
||||
/**
|
||||
* Represents the behaviour of a Pokemon when being ridden.
|
||||
@@ -46,39 +41,30 @@ 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
|
||||
|
||||
/**
|
||||
* 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 angRollVel(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, deltaTime: Double): Vec3
|
||||
|
||||
/**
|
||||
* 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 rotationOnMouseXY(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3
|
||||
|
||||
fun canJump(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean
|
||||
|
||||
@@ -90,6 +76,8 @@ 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
|
||||
@@ -123,22 +111,10 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
fun applyRenderRotation(settings: Settings, state: State, vehicle: PokemonEntity, partialTicks: Float) {}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
|
||||
/** Look limits and carry for riders while the ride rolls. */
|
||||
fun lookSettings(settings: Settings, state: State, vehicle: PokemonEntity): RideLookSettings = RideLookSettings.DEFAULT
|
||||
fun mouseModifiesDriverRotation(settings: Settings, state: State, vehicle: PokemonEntity): Pair<Boolean, Boolean> = Pair(false, false)
|
||||
|
||||
/**
|
||||
* Used in MoLang functions for querying states for animations or other magical MoLang effects.
|
||||
|
||||
@@ -10,7 +10,6 @@ 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
|
||||
@@ -36,9 +35,7 @@ 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,13 +15,8 @@ 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
|
||||
@@ -29,9 +24,7 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.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
|
||||
@@ -41,8 +34,6 @@ 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.*
|
||||
|
||||
@@ -51,28 +42,6 @@ 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
|
||||
}
|
||||
@@ -115,17 +84,25 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
checkStaminaDive(state)
|
||||
checkUpsideDown(state, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,8 +161,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
//Perform ride velocity update
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
// Pulls the bird down once it's out of stamina
|
||||
val extraDownwardForce = state.staminaDiveForce.get().pow(2) * -0.3 // 6 blocks a second downward
|
||||
// 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
|
||||
|
||||
// Convert the local velocity vector into a world vector
|
||||
val localVelVec = Vector3f(
|
||||
@@ -193,7 +170,8 @@ 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
|
||||
)
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d()
|
||||
// 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())
|
||||
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
|
||||
|
||||
return worldVelVec
|
||||
@@ -304,23 +282,19 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
state.rideVelocity.set(newVelocity)
|
||||
}
|
||||
|
||||
private fun rotateOverTime(
|
||||
override fun angRollVel(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
if (vehicle !is OrientationControllable) return
|
||||
): Vec3 {
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
|
||||
|
||||
// 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 yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble()))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))).coerceIn(-1.0, 1.0)
|
||||
|
||||
// Dampen yaw when upside down
|
||||
@@ -335,6 +309,8 @@ 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(
|
||||
@@ -344,28 +320,45 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
controller: OrientationController,
|
||||
deltaTime: Double
|
||||
) {
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
val maxRateChange = ROLL_SETTLE_ACCEL * deltaTime
|
||||
val currentRollRate = state.currRollCorrectionForce.get()
|
||||
state.currRollCorrectionForce.set(currentRollRate + (desiredRollRate - currentRollRate).coerceIn(-maxRateChange, maxRateChange))
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = if (state.staminaDiveForce.get() != 0.0) 15.0f * state.staminaDiveForce.get().toFloat() else 30.0f
|
||||
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 15.0f * state.currUpsideDownForce.get().toFloat() else 30.0f
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0f
|
||||
val pitchArrivalDeg = 35.0f
|
||||
val desiredPitch = if (state.staminaDiveForce.get() != 0.0) 90.0f else 0.0f
|
||||
val desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0f else 0.0f
|
||||
|
||||
if((state.rideVelocity.get().length() < 0.1) || state.staminaDiveForce.get() != 0.0) {
|
||||
if((state.rideVelocity.get().length() < 0.1) || state.currUpsideDownForce.get() != 0.0) {
|
||||
val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
|
||||
@@ -388,18 +381,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
override fun rotationOnMouseXY(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -412,10 +394,15 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
// Time since the rider last moved the mouse, wings only settle once it passes the settle delay
|
||||
if (abs(mouseX) < 1 && abs(mouseY) < 1) {
|
||||
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) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime)
|
||||
} else {
|
||||
} else if (cos(controller.roll.toRadians()) < 0.1) {
|
||||
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime * 0.5)
|
||||
}
|
||||
else {
|
||||
state.noInputTimeRoll.set(0.0)
|
||||
}
|
||||
|
||||
@@ -456,42 +443,46 @@ 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!!)
|
||||
val tuning = frameTuning(settings, state, vehicle)
|
||||
// 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
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 2.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
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 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 = HOVER_SPEED
|
||||
val hoverSpeed = 0.1
|
||||
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())
|
||||
|
||||
// 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)
|
||||
// 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)
|
||||
else 0.0
|
||||
|
||||
// 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
|
||||
// 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
|
||||
val isInputtingTowardsRoll = if ( xInput.sign == controller.roll.sign.toDouble()) 1.0 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
|
||||
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
|
||||
|
||||
// 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
|
||||
@@ -555,6 +546,14 @@ 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
|
||||
}
|
||||
@@ -608,139 +607,6 @@ 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,
|
||||
@@ -793,39 +659,6 @@ 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) {
|
||||
@@ -844,14 +677,6 @@ 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) {
|
||||
@@ -870,14 +695,6 @@ 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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -888,9 +705,7 @@ 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 staminaDiveForce = ridingState(0.0, Side.CLIENT)
|
||||
// Client only cache of the knobs read every frame, never synced
|
||||
var frameTuning = ridingState<BirdTuning?>(null, Side.CLIENT)
|
||||
var currUpsideDownForce = ridingState(0.0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
@@ -910,7 +725,6 @@ 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 {
|
||||
@@ -947,26 +761,3 @@ 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()
|
||||
}
|
||||
@@ -1,938 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.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.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
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
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.DistanceFieldLayer
|
||||
import com.cobblemon.mod.common.api.riding.util.DistanceFieldTuning
|
||||
import com.cobblemon.mod.common.api.riding.util.PrimitiveShape
|
||||
import com.cobblemon.mod.common.api.riding.util.RideFrame
|
||||
import com.cobblemon.mod.common.api.riding.util.collectPrimitivesFromBlocks
|
||||
import com.cobblemon.mod.common.api.riding.util.estimateGradient
|
||||
import com.cobblemon.mod.common.api.riding.util.sampleField
|
||||
import com.cobblemon.mod.common.api.riding.util.visualizeDistanceField
|
||||
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.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.client.Minecraft
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
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
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class BoardBehaviour : RidingBehaviour<BoardSettings, BoardState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/board")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalBoard: BoardSettings
|
||||
get() = CobblemonRideSettings.board
|
||||
|
||||
override fun getRidingStyle(settings: BoardSettings, state: BoardState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BoardSettings, BoardState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, vehicle ->
|
||||
return@PoseOption state.walking.get()
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
// Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
// This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !blockState.isAir && blockState.fluidState.isEmpty
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): PoseType {
|
||||
return this.poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if (vehicle.level().isClientSide) {
|
||||
handleSprinting(state, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
state.walking.set(state.rideVelocity.get().horizontalDistance() > 0.1 || driver.zza != 0.0f)
|
||||
//surfDistCheck(state, vehicle)
|
||||
}
|
||||
|
||||
//TODO: should be a config somewhere
|
||||
val debugVisualizeField = true;
|
||||
|
||||
if (false && debugVisualizeField && !vehicle.level().isClientSide && vehicle.tickCount % 5 == 0) {
|
||||
visualizeDistanceField(
|
||||
referenceEntity = vehicle,
|
||||
center = vehicle.position().add(0.0, (vehicle.bbHeight * 0.5f).toDouble(), 0.0),
|
||||
layers = listOf(
|
||||
DistanceFieldLayer(isoValue = 2.5f, color = Vector3f(1f, 0.2f, 0.2f))
|
||||
),
|
||||
shape = PrimitiveShape.ELLIPSOID,
|
||||
horizontalRadius = 4.0,
|
||||
verticalRange = 4.0,
|
||||
sampleSpacing = 0.3,
|
||||
marchStep = 0.15,
|
||||
tuning = DistanceFieldTuning(smoothingRange = 0.25f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check how far the vehicle is from the "surface"
|
||||
*/
|
||||
fun surfDistCheck(
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
val center = vehicle.position()
|
||||
val horizontalRadius = 4.0;
|
||||
val verticalRange = 4.0;
|
||||
val level = vehicle.level()
|
||||
|
||||
//if (level !is ServerLevel) return
|
||||
|
||||
// Take a cubic area around the vehicle and gather SDF primitives from it.
|
||||
val searchBox = AABB(
|
||||
center.x - horizontalRadius, center.y - verticalRange, center.z - horizontalRadius,
|
||||
center.x + horizontalRadius, center.y + verticalRange, center.z + horizontalRadius
|
||||
)
|
||||
val primitives = collectPrimitivesFromBlocks(level,
|
||||
searchBox,
|
||||
PrimitiveShape.ELLIPSOID,
|
||||
includeFluids = true
|
||||
)
|
||||
val sp = center.toVector3f() // Sample Point
|
||||
val tuning = DistanceFieldTuning(smoothingRange = 0.25f)
|
||||
val sd = sampleField(primitives, sp.x, sp.y, sp.z, tuning)
|
||||
state.surfDist.set(sd)
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
// Check both vertical movement and if there are blocks below.
|
||||
val posBelow = vehicle.blockPosition().below()
|
||||
val blockStateBelow = vehicle.level().getBlockState(posBelow)
|
||||
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
|
||||
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(true)
|
||||
}
|
||||
|
||||
fun handleSprinting(
|
||||
state: BoardState,
|
||||
driver: Player
|
||||
) {
|
||||
val sprintTimerTickLength = 7 // Emulate vanillas tick timer length
|
||||
var hasDoubleTapped = false
|
||||
|
||||
// If you are not sprinting and you just pressed down and theres no
|
||||
if (!state.forwardHeldLastTick.get() && Minecraft.getInstance().options.keyUp.isDown() && !state.sprinting.get() && state.sprintTickTimer.get() == 0) {
|
||||
state.sprintTickTimer.set(sprintTimerTickLength)
|
||||
} else if (!state.forwardHeldLastTick.get() && Minecraft.getInstance().options.keyUp.isDown() && state.sprintTickTimer.get() != 0) { // If has double tapped
|
||||
hasDoubleTapped = true
|
||||
}else if(!state.sprinting.get() && state.sprintTickTimer.get() > 0) {
|
||||
state.sprintTickTimer.set(state.sprintTickTimer.get() - 1)
|
||||
} else {
|
||||
state.sprintTickTimer.set(0)
|
||||
}
|
||||
|
||||
val tryingToSprint = (Minecraft.getInstance().options.keySprint.isDown() && Minecraft.getInstance().options.keyUp.isDown()) || hasDoubleTapped
|
||||
|
||||
if (state.stamina.get() <= 0.0f || !Minecraft.getInstance().options.keyUp.isDown()) {
|
||||
// If stamina runs out or the player is not holding forward then stop sprinting
|
||||
state.sprinting.set(false)
|
||||
if (state.stamina.get() <= 0.0f) {
|
||||
state.sprintToggleable.set(false)
|
||||
}
|
||||
} else if (!state.sprinting.get() && !state.sprintToggleable.get() && state.stamina.get() > 0.33f) {
|
||||
// If you are not sprinting and sprint is not toggleable and you're not trying to sprint and stamina is above 0.3 then enable the toggle
|
||||
state.sprintToggleable.set(true)
|
||||
} else if (tryingToSprint && state.sprintToggleable.get()) {
|
||||
// If you are trying to sprint and the toggle allows it then start sprinting and disable toggle
|
||||
state.sprinting.set(true)
|
||||
}
|
||||
|
||||
// Take not of whether forward was held last tick
|
||||
state.forwardHeldLastTick.set(Minecraft.getInstance().options.keyUp.isDown())
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalBoard.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalBoard.staminaExpr!!)).toFloat() / 20.0f
|
||||
|
||||
val newStam = if (state.sprinting.get()) {
|
||||
max(0.0f,stam - stamDrainRate)
|
||||
} else {
|
||||
min(1.0f,stam + stamDrainRate * 4)
|
||||
}
|
||||
|
||||
state.stamina.set(newStam.coerceIn(0F, 1F))
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) { return }
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) { }
|
||||
|
||||
|
||||
override fun rotation(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount )
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the rotation amount given the difference between
|
||||
* the current player y rot and entity y rot. This gives the
|
||||
* affect of influencing a rides rotation through the mouse
|
||||
* without it being instant.
|
||||
*/
|
||||
fun calcRotAmount(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBoard.speedExpr!!)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBoard.handlingExpr!!)
|
||||
val walkHandlingBoost = 5
|
||||
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalBoard.lookYawLimit!!)
|
||||
|
||||
// Normalize the current rotation diff
|
||||
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
|
||||
val rotDiffNorm = rotDiff / maxYawDiff//(rotDiff / maxYawDiff).coerceIn(-0.2f, 0.2f)
|
||||
|
||||
// Take the square root so that the ride levels out quicker when at lower differences between entity
|
||||
// y and driver y
|
||||
// This influences the speed of the turn based on how far in one direction you're looking
|
||||
val minRotMod = 0.4f // Min speed that the rotation modulation converges towards
|
||||
val rotDiffMod = ((sqrt(abs(rotDiffNorm)) * (1.0f - minRotMod)) + minRotMod) * rotDiffNorm.sign
|
||||
|
||||
// Turn rate should be quick when walking and slower when sprinting.
|
||||
// Smoothly move between these based on speed
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val w = max(walkSpeed, vehicle.deltaMovement.horizontalDistance())
|
||||
val invRelSpeed = (RidingBehaviour.scaleToRange(w, walkSpeed, topSpeed ) - 1.0f) * -1.0f
|
||||
val turnRate = ((handling.toFloat() / 20.0f) * max(walkHandlingBoost * sqrt(abs(invRelSpeed)), 1.0)).toFloat()
|
||||
|
||||
// Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
}
|
||||
|
||||
private fun spawnGradientDebugParticles(vehicle: PokemonEntity, normal: Vec3) {
|
||||
val level = vehicle.level()
|
||||
val headPos = vehicle.position().add(0.0, vehicle.bbHeight.toDouble(), 0.0)
|
||||
|
||||
val steps = 20
|
||||
val length = 2.0
|
||||
for (i in 0..steps) {
|
||||
val t = i.toDouble() / steps
|
||||
val point = headPos.add(normal.scale(length * t))
|
||||
level.addParticle(ParticleTypes.END_ROD, point.x, point.y, point.z, 0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
return state.rideVelocity.get()
|
||||
}
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint ?: globalBoard.canSprint!!)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalBoard.canJump!!)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalBoard.jumpExpr!!) * 0.75
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBoard.speedExpr!!)
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
val accelTime = if(state.sprinting.get()) (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBoard.accelerationExpr!!) * 20.0) else 0.5 * 20.0
|
||||
val accel = topSpeed / accelTime
|
||||
var activeInput = false
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
if (vehicle.horizontalCollision) {
|
||||
var horizontalVec = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
horizontalVec = horizontalVec.normalize().scale(min(horizontalVec.length(), 0.3))
|
||||
newVelocity = Vec3(horizontalVec.x, newVelocity.y, horizontalVec.z)
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Speed up and slow down based on input
|
||||
*****************************************************/
|
||||
if (driver.zza != 0.0f) {
|
||||
|
||||
// If on a tight turn then do not speed up past half of top speed in order to turn quicker
|
||||
// Also determine how fast to be slowing down based on how far turned you are
|
||||
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalBoard.lookYawLimit!!)
|
||||
val percOfMaxTurnSpeed = abs(Mth.wrapDegrees(driver.yRot - vehicle.yRot) / lookYawLimit) * 100.0f
|
||||
val turnPercThresh = 0.0f
|
||||
val s = min(((percOfMaxTurnSpeed - turnPercThresh) / (100.0f - turnPercThresh)).pow(1),1.0f)
|
||||
val effectiveTopSpeed = if (percOfMaxTurnSpeed > turnPercThresh) topSpeed / max(2.0f*s,1.0f) else topSpeed
|
||||
val turningSlowDown = s * 0.1
|
||||
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > effectiveTopSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-effectiveTopSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
// Add extra friction if trying to slow down to turn faster
|
||||
if (newVelocity.z > effectiveTopSpeed) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(turningSlowDown * newVelocity.z.sign, newVelocity.z))
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Gravity logic
|
||||
*****************************************************/
|
||||
// if(state.surfDist.get() > 1.6f) {
|
||||
// val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
// val terminalVel = 2.0
|
||||
// newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
// } else if(state.surfDist.get() > 1.4f && state.surfDist.get() <= 1.6f) {
|
||||
// newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
// } else if(state.surfDist.get() <= 1.4f) {
|
||||
// val gravity = -(9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
// val terminalVel = -2.0
|
||||
// newVelocity = Vec3(newVelocity.x, min(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
// }
|
||||
|
||||
/******************************************************
|
||||
* Hover logic - non-predictive: correct against current position
|
||||
*****************************************************/
|
||||
val correctionGain = 0.5
|
||||
val maxCorrectionSpeed = 0.3
|
||||
val center = vehicle.position()
|
||||
val horizontalRadius = 4.0;
|
||||
val verticalRange = 4.0;
|
||||
val level = vehicle.level()
|
||||
|
||||
// Take a cubic area around the vehicle and gather SDF primitives from it.
|
||||
val searchBox = AABB(
|
||||
center.x - horizontalRadius, center.y - verticalRange, center.z - horizontalRadius,
|
||||
center.x + horizontalRadius, center.y + vehicle.bbHeight, center.z + horizontalRadius
|
||||
)
|
||||
val primitives = collectPrimitivesFromBlocks(level,
|
||||
searchBox,
|
||||
PrimitiveShape.ELLIPSOID,
|
||||
includeFluids = true
|
||||
)
|
||||
val tuning = DistanceFieldTuning(smoothingRange = 0.25f)
|
||||
|
||||
val currentPos = vehicle.position()
|
||||
val currentDist = sampleField(primitives, currentPos.x.toFloat(), currentPos.y.toFloat(), currentPos.z.toFloat(), tuning)
|
||||
val normal = estimateGradient(primitives, currentPos, tuning)
|
||||
state.surfaceNormal.set(normal)
|
||||
//spawnGradientDebugParticles(vehicle, normal) // LETS SEE IT!
|
||||
|
||||
// Only correct when under the hover surface.
|
||||
// Otherwise allow gravity to do its thing
|
||||
// if (currentDist < targetHoverDist) {
|
||||
// val error = currentDist - targetHoverDist
|
||||
// val normalSpeed = newVelocity.dot(normal)
|
||||
// val tangentialVelocity = newVelocity.subtract(normal.scale(normalSpeed))
|
||||
//
|
||||
// val correctionSpeed = (-error * correctionGain).coerceIn(-maxCorrectionSpeed, maxCorrectionSpeed)
|
||||
// newVelocity = tangentialVelocity.add(normal.scale(correctionSpeed))
|
||||
// } else {
|
||||
// val hoverCoolnessLowGravFactor = 0.5
|
||||
// val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6 * hoverCoolnessLowGravFactor
|
||||
// val terminalVel = 2.0 * hoverCoolnessLowGravFactor
|
||||
// newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
// }
|
||||
|
||||
/******************************************************
|
||||
* Hover logic
|
||||
*****************************************************/
|
||||
val hoverActivationDist = 3.0f
|
||||
val targetHoverDist = 1.0f
|
||||
val hoverCoolnessLowGravFactor = 0.70
|
||||
val gravity = (9.8 / 20.0) * 0.2 * 0.6 * hoverCoolnessLowGravFactor
|
||||
val terminalVel = 2.0 * hoverCoolnessLowGravFactor
|
||||
// val repelStrength = 0.05 // tested and was alright
|
||||
// val hoverDamping = 0.15
|
||||
val repelStrength = 0.1
|
||||
val hoverDamping = 0.2
|
||||
|
||||
val surfDist = currentDist
|
||||
|
||||
if (surfDist > hoverActivationDist) {
|
||||
// Free fall - no surface influence yet, same as before.
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
} else {
|
||||
// Getting close - push back toward targetHoverDist, harder the nearer we are.
|
||||
val closeness = ((hoverActivationDist - surfDist) / (hoverActivationDist - targetHoverDist)).coerceIn(0f, 1.5f)
|
||||
val repelForce = closeness * repelStrength
|
||||
val dampingForce = -newVelocity.y * hoverDamping // resists overshoot/bounce
|
||||
|
||||
newVelocity = Vec3(newVelocity.x, newVelocity.y + repelForce + dampingForce - gravity * 0.3, newVelocity.z)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Ground Friction
|
||||
// *****************************************************/
|
||||
// if( (newVelocity.horizontalDistance() > 0 && vehicle.onGround() && !activeInput) || newVelocity.horizontalDistance() > topSpeed) {
|
||||
// newVelocity = newVelocity.subtract(0.0, 0.0, min(0.03 * newVelocity.z.sign, newVelocity.z))
|
||||
// }
|
||||
|
||||
/******************************************************
|
||||
* Jump Logic
|
||||
*****************************************************/
|
||||
if (state.jumpTicks.get() > 0 || (state.jumpTicks.get() >= 0 && driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1)) {
|
||||
// Spread out jumpforce so that variable height jumps are possible
|
||||
val jumpInputTicks = 6
|
||||
if (driver.jumping && (state.jumpTicks.get() >= 0 && state.jumpTicks.get() < jumpInputTicks)) {
|
||||
val appliedJumpForce = ((jumpForce*1.5) / jumpInputTicks) //* (1 - state.jumpTicks.get() / jumpInputTicks)
|
||||
newVelocity = Vec3(newVelocity.x, newVelocity.y + appliedJumpForce, newVelocity.z)
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
} else {
|
||||
// Set delay before next jump is possible after hitting ground
|
||||
val tickJumpDelay = 3
|
||||
state.jumpTicks.set(-tickJumpDelay)
|
||||
}
|
||||
} else if (vehicle.onGround() && state.jumpTicks.get() < 0) {
|
||||
// Tick off the delay once reaching the ground
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
}
|
||||
|
||||
// Zero out lateral velocity possibly picked up from a controller transition
|
||||
newVelocity = Vec3(0.0, newVelocity.y, newVelocity.z)
|
||||
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
private fun getWalkSpeed( vehicle: PokemonEntity ): Double {
|
||||
val walkspeed = vehicle.runtime.resolveDouble(vehicle.behaviour.moving.walk.walkSpeed)
|
||||
val movementSpeed = vehicle.attributes.getValue(Attributes.MOVEMENT_SPEED)
|
||||
val speedModifier = 1.2 * 0.35
|
||||
return walkspeed * movementSpeed * speedModifier
|
||||
|
||||
}
|
||||
|
||||
private fun rotateOverTime(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
if (vehicle !is OrientationControllable) return
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
// turning
|
||||
val turnMaxRate = 5.5
|
||||
val turnAccel = 1.0
|
||||
val turnFriction = 0.75
|
||||
|
||||
val targetYawRate = -driver.xxa.toDouble() * turnMaxRate
|
||||
val currentYawRate = state.yawAngularVelocity.get()
|
||||
val newYawRate = if (abs(targetYawRate) > 0.001) {
|
||||
moveToward(currentYawRate, targetYawRate, turnAccel * deltaTime * 20.0)
|
||||
} else {
|
||||
moveToward(currentYawRate, 0.0, turnFriction * deltaTime * 20.0)
|
||||
}
|
||||
state.yawAngularVelocity.set(newYawRate)
|
||||
controller.applyGlobalYaw((newYawRate * deltaTime * 20.0).toFloat())
|
||||
|
||||
// up-vector alignment framerate-independent
|
||||
val upAlignmentRatePerTick = 0.1 // fraction of the remaining gap closed per tick
|
||||
val frameBlend = 1.0 - (1.0 - upAlignmentRatePerTick).pow(deltaTime * 20.0)
|
||||
alignUpToSurfaceNormal(controller, state.surfaceNormal.get(), frameBlend.toFloat())
|
||||
|
||||
// lean into turns (cosmetic local roll)
|
||||
val maxLeanDeg = 35.0
|
||||
val leanPerYawRate = 4.0 * 3.0
|
||||
val leanRatePerTick = 0.25 * 1.5
|
||||
|
||||
val desiredLean = (newYawRate * leanPerYawRate).coerceIn(-maxLeanDeg, maxLeanDeg)
|
||||
val leanFrameBlend = 1.0 - (1.0 - leanRatePerTick).pow(deltaTime * 20.0)
|
||||
val leanError = controller.roll.angleDifference(desiredLean.toFloat())
|
||||
controller.rotateRoll((leanError * leanFrameBlend).toFloat())
|
||||
|
||||
}
|
||||
|
||||
private fun moveToward(current: Double, target: Double, maxDelta: Double): Double {
|
||||
val diff = target - current
|
||||
return if (abs(diff) <= maxDelta) target else current + maxDelta * diff.sign
|
||||
}
|
||||
|
||||
private fun alignUpToSurfaceNormal(controller: OrientationController, targetUp: Vec3, alignmentBlend: Float) {
|
||||
if (targetUp.lengthSqr() < 1e-6) return
|
||||
val targetUpVec = Vector3f(targetUp.x.toFloat(), targetUp.y.toFloat(), targetUp.z.toFloat()).normalize()
|
||||
|
||||
controller.updateOrientation { current ->
|
||||
val currentUp = current.transform(Vector3f(OrientationController.UP), Vector3f())
|
||||
if (currentUp.lengthSquared() < 1e-6f) return@updateOrientation current
|
||||
|
||||
// Full rotation that would exactly align currentUp to targetUp...
|
||||
val fullCorrection = Quaternionf().rotationTo(currentUp, targetUpVec)
|
||||
// ...but only take a partial step toward it, so alignment is smooth, not instant.
|
||||
val steppedCorrection = Quaternionf().slerp(fullCorrection, alignmentBlend)
|
||||
|
||||
// Pre-multiply (world-space correction), same pattern applyGlobalYaw uses.
|
||||
Matrix3f().set(steppedCorrection).mul(current, Matrix3f())
|
||||
}
|
||||
}
|
||||
|
||||
override fun steer(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
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: BoardSettings,
|
||||
state: BoardState,
|
||||
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: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return if (state.sprinting.get()) 1.15f else 1.0f
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun maxUpStep(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): Float? {
|
||||
return if (state.inAir.get()) 0.0f else 1.2f
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: BoardSettings) = BoardState()
|
||||
|
||||
override fun asMoLangValue(
|
||||
settings: BoardSettings,
|
||||
state: BoardState,
|
||||
vehicle: PokemonEntity
|
||||
): ObjectValue<RidingBehaviour<BoardSettings, BoardState>> {
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalBoard.canJump!!)
|
||||
val value = super.asMoLangValue(settings, state, vehicle)
|
||||
value.functions.put("sprinting") { DoubleValue(state.sprinting.get()) }
|
||||
value.functions.put("walking") { DoubleValue(state.walking.get()) }
|
||||
value.functions.put("in_air") { DoubleValue(state.inAir.get() || (state.rideVelocity.get().y >= 0.0 && (vehicle.controllingPassenger as? Player)?.jumping == true && canJump) || (state.rideVelocity.get().y > 0.0)) }
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
class BoardSettings : RidingBehaviourSettings {
|
||||
override val key = BoardBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
var canJump: Expression? = null
|
||||
private set
|
||||
|
||||
var canSprint: Expression? = null
|
||||
private set
|
||||
|
||||
var lookYawLimit: Expression? = null
|
||||
private set
|
||||
|
||||
var speedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
//Between a one block jump and a six block jump
|
||||
var jumpExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(canSprint)
|
||||
buffer.writeNullableExpression(lookYawLimit)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(canSprint)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
|
||||
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
canSprint = buffer.readNullableExpression()
|
||||
lookYawLimit = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
canJump = buffer.readNullableExpression()
|
||||
canSprint = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class BoardState : RidingBehaviourState() {
|
||||
var sprinting = ridingState(false, Side.CLIENT)
|
||||
var walking = ridingState(false, Side.BOTH)
|
||||
var sprintToggleable = ridingState(false, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
var jumpTicks = ridingState(0, Side.CLIENT)
|
||||
var sprintTickTimer = ridingState(0, Side.CLIENT)
|
||||
var forwardHeldLastTick = ridingState(false, Side.CLIENT)
|
||||
var surfDist = ridingState(Float.POSITIVE_INFINITY, Side.BOTH)
|
||||
var surfaceNormal = ridingState(Vec3(0.0, 1.0, 0.0), Side.CLIENT)
|
||||
var yawAngularVelocity = ridingState(0.0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(sprinting.get())
|
||||
buffer.writeBoolean(walking.get())
|
||||
buffer.writeBoolean(sprintToggleable.get())
|
||||
buffer.writeBoolean(inAir.get())
|
||||
buffer.writeInt(jumpTicks.get())
|
||||
buffer.writeInt(sprintTickTimer.get())
|
||||
buffer.writeBoolean(forwardHeldLastTick.get())
|
||||
buffer.writeFloat(surfDist.get())
|
||||
buffer.writeVec3(surfaceNormal.get())
|
||||
buffer.writeDouble(yawAngularVelocity.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
sprinting.set(buffer.readBoolean(), forced = true)
|
||||
walking.set(buffer.readBoolean(), forced = true)
|
||||
sprintToggleable.set(buffer.readBoolean(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
jumpTicks.set(buffer.readInt(), forced = true)
|
||||
sprintTickTimer.set(buffer.readInt(), forced = true)
|
||||
forwardHeldLastTick.set(buffer.readBoolean(), forced = true)
|
||||
surfDist.set(buffer.readFloat(), forced = true)
|
||||
surfaceNormal.set(buffer.readVec3(), forced = true)
|
||||
yawAngularVelocity.set(buffer.readDouble(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
sprinting.set(false, forced = true)
|
||||
walking.set(false, forced = true)
|
||||
sprintToggleable.set(false, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
jumpTicks.set(0, forced = true)
|
||||
sprintTickTimer.set(0, forced = true)
|
||||
forwardHeldLastTick.set(false, forced = true)
|
||||
surfDist.set(Float.POSITIVE_INFINITY, forced = true)
|
||||
surfaceNormal.set(Vec3(0.0, 1.0, 0.0), forced = true)
|
||||
yawAngularVelocity.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = BoardState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.sprinting.set(this.sprinting.get(), forced = true)
|
||||
it.walking.set(this.sprinting.get(), forced = true)
|
||||
it.sprintToggleable.set(this.sprintToggleable.get(), forced = true)
|
||||
it.inAir.set(this.inAir.get(), forced = true)
|
||||
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
|
||||
it.sprintTickTimer.set(this.sprintTickTimer.get(), forced = true)
|
||||
it.forwardHeldLastTick.set(this.forwardHeldLastTick.get(), forced = true)
|
||||
it.surfDist.set(this.surfDist.get(), forced = true)
|
||||
it.surfaceNormal.set(this.surfaceNormal.get(), forced = true)
|
||||
it.yawAngularVelocity.set(this.yawAngularVelocity.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is BoardState) return false
|
||||
if (previous.sprinting.get() != sprinting.get()) return true
|
||||
if (previous.walking.get() != walking.get()) return true
|
||||
if (previous.sprintToggleable.get() != sprintToggleable.get()) return true
|
||||
if (previous.inAir.get() != inAir.get()) return true
|
||||
if (previous.surfDist.get() != surfDist.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ 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
|
||||
@@ -18,11 +19,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
|
||||
@@ -66,18 +67,7 @@ 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,
|
||||
@@ -90,6 +80,34 @@ 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
|
||||
}
|
||||
@@ -131,6 +149,10 @@ 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,7 +13,6 @@ 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
|
||||
@@ -21,7 +20,6 @@ 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
|
||||
@@ -93,23 +91,12 @@ 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
|
||||
|
||||
@@ -133,7 +120,7 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
return Vec3(xVel, yVel, zVel)
|
||||
}
|
||||
|
||||
private fun rotationOverTime(
|
||||
override fun angRollVel(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -188,18 +175,7 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
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(
|
||||
override fun rotationOnMouseXY(
|
||||
settings: HelicopterSettings,
|
||||
state: RidingBehaviourState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -265,6 +241,10 @@ 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,7 +18,6 @@ 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,6 +26,7 @@ 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,23 +190,12 @@ 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)
|
||||
@@ -294,6 +283,31 @@ 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,
|
||||
@@ -341,6 +355,14 @@ 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,7 +15,6 @@ 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
|
||||
@@ -29,6 +28,7 @@ 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()
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians()) // Unrotate preemptively as this vector gets rotate later down the line in MC logic.
|
||||
worldVelVec = 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> {
|
||||
|
||||
}
|
||||
|
||||
private fun rotationOverTime(
|
||||
override fun angRollVel(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -292,18 +292,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return Vec3(yawForce, 0.0, 0.0)
|
||||
}
|
||||
|
||||
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(
|
||||
override fun rotationOnMouseXY(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -401,6 +390,10 @@ 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,7 +19,6 @@ 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.*
|
||||
@@ -28,6 +27,7 @@ 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,17 +75,13 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
|
||||
override fun driverTick(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
handleBoosting(settings, state, vehicle, driver)
|
||||
tickStamina(settings, state, vehicle, driver)
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
fun handleBoosting(
|
||||
@@ -212,23 +208,12 @@ 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)
|
||||
@@ -333,6 +318,37 @@ 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,
|
||||
@@ -387,6 +403,14 @@ 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,10 +11,6 @@ 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
|
||||
@@ -26,11 +22,11 @@ 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
|
||||
|
||||
class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
companion object {
|
||||
@@ -174,6 +170,16 @@ 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,
|
||||
@@ -230,6 +236,46 @@ 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,
|
||||
@@ -242,28 +288,6 @@ 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 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,
|
||||
@@ -286,17 +310,6 @@ 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,
|
||||
@@ -348,49 +361,6 @@ 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 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,7 +13,6 @@ 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
|
||||
@@ -64,8 +63,7 @@ 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)
|
||||
// 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)
|
||||
return driver.isSprinting && input.z > 0.5 && alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, entity)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,797 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
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.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.util.DistanceFieldLayer
|
||||
import com.cobblemon.mod.common.api.riding.util.FieldPrimitive
|
||||
import com.cobblemon.mod.common.api.riding.util.PrimitiveShape
|
||||
import com.cobblemon.mod.common.api.riding.util.collectPrimitivesFromBlocks
|
||||
import com.cobblemon.mod.common.api.riding.util.estimateGradient
|
||||
import com.cobblemon.mod.common.api.riding.util.sampleField
|
||||
import com.cobblemon.mod.common.api.riding.util.visualizeDistanceField
|
||||
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.DistanceFieldTuning
|
||||
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 com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
|
||||
class ClimbBehaviour : RidingBehaviour<ClimbSettings, ClimbState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/climb")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: ClimbSettings, state: ClimbState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<ClimbSettings, ClimbState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, vehicle ->
|
||||
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
// Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
// This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !blockState.isAir && blockState.fluidState.isEmpty
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): PoseType {
|
||||
return this.poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
handleSprinting(state, driver)
|
||||
inAirCheck(state, vehicle)
|
||||
tickStamina(settings, state, vehicle)
|
||||
|
||||
// attachmentNormal: discrete, physics-facing - which face are we touching, used
|
||||
// for the sticking force (newDownForce) in velocity().
|
||||
// renderSurfaceNormal: smoothed field-gradient - purely cosmetic, used to slowly
|
||||
// rotate the model's up vector in rotateOverTime(). The two are intentionally
|
||||
// separate and should not be conflated.
|
||||
updateAttachment(state, vehicle)
|
||||
updateRenderSurfaceNormal(state, vehicle)
|
||||
|
||||
//TODO: should be a config somewhere
|
||||
val debugVisualizeField = true;
|
||||
|
||||
if (debugVisualizeField && !vehicle.level().isClientSide && vehicle.tickCount % 5 == 0) {
|
||||
visualizeDistanceField(
|
||||
referenceEntity = vehicle,
|
||||
center = vehicle.position().add(0.0, (vehicle.bbHeight * 0.5f).toDouble(), 0.0),
|
||||
layers = listOf(
|
||||
DistanceFieldLayer(isoValue = 0.0f, color = Vector3f(1f, 0.2f, 0.2f)),
|
||||
DistanceFieldLayer(isoValue = 1.0f, color = Vector3f(0.2f, 0.6f, 1f))
|
||||
),
|
||||
horizontalRadius = 2.0,
|
||||
verticalRange = 2.0,
|
||||
sampleSpacing = 0.35,
|
||||
marchStep = 0.15,
|
||||
tuning = DistanceFieldTuning(smoothingRange = 0.5f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the smoothed field-gradient normal used for rendering only (up-vector
|
||||
* alignment in rotateOverTime). Cached here at tick rate since collecting primitives
|
||||
* and sampling the field is real work - actually applying the rotation happens
|
||||
* separately at frame rate, same pattern as HorseBehaviour's surfDist/BoardBehaviour's
|
||||
* surfaceNormal.
|
||||
*/
|
||||
fun updateRenderSurfaceNormal(state: ClimbState, vehicle: PokemonEntity) {
|
||||
val level = vehicle.level()
|
||||
val center = vehicle.position().add(0.0, (vehicle.bbHeight * 0.5f).toDouble(), 0.0)
|
||||
val horizontalRadius = 1.5
|
||||
val verticalRange = 1.5
|
||||
|
||||
val searchBox = AABB(
|
||||
center.x - horizontalRadius, center.y - verticalRange, center.z - horizontalRadius,
|
||||
center.x + horizontalRadius, center.y + verticalRange, center.z + horizontalRadius
|
||||
)
|
||||
|
||||
val primitives: List<FieldPrimitive> = collectPrimitivesFromBlocks(level, searchBox, PrimitiveShape.ELLIPSOID)
|
||||
if (primitives.isEmpty()) return
|
||||
|
||||
val tuning = DistanceFieldTuning(smoothingRange = 0.5f)
|
||||
val gradient = estimateGradient(primitives, center, tuning)
|
||||
state.renderSurfaceNormal.set(gradient)
|
||||
}
|
||||
|
||||
/** Debug viz: draws a short line of particles from the vehicle out along the current attachment normal. */
|
||||
fun spawnAttachmentNormalParticles(vehicle: PokemonEntity, normal: Vec3) {
|
||||
val level = vehicle.level()
|
||||
if (!level.isClientSide) return
|
||||
if (normal.lengthSqr() < 1e-6) return
|
||||
|
||||
val origin = vehicle.position().add(0.0, (vehicle.bbHeight * 0.5f).toDouble(), 0.0)
|
||||
val length = 1.5
|
||||
val steps = 15
|
||||
for (i in 0..steps) {
|
||||
val t = i.toDouble() / steps
|
||||
val point = origin.add(normal.scale(length * t))
|
||||
level.addParticle(ParticleTypes.END_ROD, point.x, point.y, point.z, 0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
// Check both vertical movement and if there are blocks below.
|
||||
val posBelow = vehicle.blockPosition().below()
|
||||
val blockStateBelow = vehicle.level().getBlockState(posBelow)
|
||||
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
|
||||
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(inAir)
|
||||
}
|
||||
|
||||
fun handleSprinting(
|
||||
state: ClimbState,
|
||||
driver: Player
|
||||
) {
|
||||
if (state.sprinting.get()) {
|
||||
state.sprinting.set(driver.isSprinting && state.stamina.get() > 0.0f)
|
||||
} else {
|
||||
// Only allow sprinting to start if over x percentage of stamina left
|
||||
val stamSprintPerc = 25.0f
|
||||
state.sprinting.set(driver.isSprinting && state.stamina.get() > stamSprintPerc / 100.0f)
|
||||
}
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
var newStam = stam
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
|
||||
|
||||
if (state.sprinting.get()) {
|
||||
newStam = max(0.0f,stam - stamDrainRate)
|
||||
|
||||
} else {
|
||||
newStam = min(1.0f,stam + stamDrainRate * 4)
|
||||
}
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) { return }
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) { }
|
||||
|
||||
|
||||
override fun rotation(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(vehicle.xRot, vehicle.yRot )
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the rotation amount given the difference between
|
||||
* the current player y rot and entity y rot. This gives the
|
||||
* affect of influencing a rides rotation through the mouse
|
||||
* without it being instant.
|
||||
*
|
||||
* THIS IS ADAPTED FOR 3D ORIENTATION
|
||||
*/
|
||||
fun calcRotAmount(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val walkHandlingBoost = 5
|
||||
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit)
|
||||
|
||||
// Normalize the current rotation diff
|
||||
val controller = (vehicle as? OrientationControllable)?.orientationController
|
||||
if (controller == null || controller.orientation == null) return 0.0f
|
||||
|
||||
val rotDiff = Mth.wrapDegrees(Mth.wrapDegrees(driver.yRot) - controller.yaw).coerceIn(-maxYawDiff,maxYawDiff)
|
||||
val rotDiffNorm = rotDiff / maxYawDiff
|
||||
|
||||
// Take the square root so that the ride levels out quicker when at lower differences between entity
|
||||
// y and driver y
|
||||
// This influences the speed of the turn based on how far in one direction you're looking
|
||||
val minRotMod = 0.4f // Min speed that the rotation modulation converges towards
|
||||
val rotDiffMod = ((sqrt(abs(rotDiffNorm)) * (1.0f - minRotMod)) + minRotMod) * rotDiffNorm.sign
|
||||
|
||||
// Turn rate should be quick when walking and slower when sprinting.
|
||||
// Smoothly move between these based on speed
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val w = max(walkSpeed, vehicle.deltaMovement.horizontalDistance())
|
||||
val invRelSpeed = (RidingBehaviour.scaleToRange(w, walkSpeed, topSpeed ) - 1.0f) * -1.0f
|
||||
val turnRate = ((handling.toFloat() / 20.0f) * max(walkHandlingBoost * invRelSpeed, 1.0)).toFloat()
|
||||
|
||||
// Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
}
|
||||
|
||||
|
||||
// This ride works along its yaw so its velocity gets turned into world space here
|
||||
override fun velocity(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
return RideFrame.yawToWorld(localVelocity(settings, state, vehicle, driver, input), vehicle.yRot)
|
||||
}
|
||||
|
||||
private fun localVelocity(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
return state.rideVelocity.get()
|
||||
}
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val globalHorse: HorseSettings = CobblemonRideSettings.horse
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint ?: globalHorse.canSprint!!)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalHorse.canJump!!)
|
||||
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHorse.jumpExpr!!) * 0.75
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
val accelTime = if(state.sprinting.get()) (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHorse.accelerationExpr!!) * 20.0) else 0.5 * 20.0
|
||||
val accel = topSpeed / accelTime
|
||||
var activeInput = false
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
|
||||
|
||||
/******************************************************
|
||||
* Gather the previous velocity and check for horizontal
|
||||
* collisions
|
||||
*****************************************************/
|
||||
if (vehicle.horizontalCollision) {
|
||||
var horizontalVec = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
horizontalVec = horizontalVec.normalize().scale(min(horizontalVec.length(), 0.3))
|
||||
newVelocity = Vec3(horizontalVec.x, newVelocity.y, horizontalVec.z)
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Speed up and slow down based on input
|
||||
*****************************************************/
|
||||
if (driver.zza != 0.0f) {
|
||||
|
||||
// If on a tight turn then do not speed up past half of top speed in order to turn quicker
|
||||
// Also determine how fast to be slowing down based on how far turned you are
|
||||
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
|
||||
val percOfMaxTurnSpeed = abs(Mth.wrapDegrees(driver.yRot - vehicle.yRot) / lookYawLimit) * 100.0f
|
||||
val turnPercThresh = 0.0f
|
||||
val s = min(((percOfMaxTurnSpeed - turnPercThresh) / (100.0f - turnPercThresh)).pow(1),1.0f)
|
||||
val effectiveTopSpeed = if (percOfMaxTurnSpeed > turnPercThresh) topSpeed / max(2.0f*s,1.0f) else topSpeed
|
||||
val turningSlowDown = s * 0.1
|
||||
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > effectiveTopSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-effectiveTopSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
// Add extra friction if trying to slow down to turn faster
|
||||
if (newVelocity.z > effectiveTopSpeed) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(turningSlowDown * newVelocity.z.sign, newVelocity.z))
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Gravity logic
|
||||
*****************************************************/
|
||||
val isOnTopSurface = state.attachmentNormal.get() == Vec3(0.0, 1.0, 0.0)
|
||||
if (isOnTopSurface) {
|
||||
if (!vehicle.onGround() && state.jumpTicks.get() <= 0) {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.6
|
||||
val terminalVel = 2.0
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - gravity, -terminalVel), newVelocity.z)
|
||||
} else if(vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
}
|
||||
} else {
|
||||
// Wall/ceiling - small constant pull to stay stuck, not real gravity.
|
||||
val stickForce = 0.08
|
||||
newVelocity = Vec3(newVelocity.x, newVelocity.y - stickForce, newVelocity.z)
|
||||
}
|
||||
|
||||
|
||||
/******************************************************
|
||||
* Ground Friction
|
||||
*****************************************************/
|
||||
if( (newVelocity.horizontalDistance() > 0 && vehicle.onGround() && !activeInput) || newVelocity.horizontalDistance() > topSpeed) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.03 * newVelocity.z.sign, newVelocity.z))
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Jump Logic
|
||||
*****************************************************/
|
||||
if (state.jumpTicks.get() > 0 || (state.jumpTicks.get() >= 0 && driver.jumping && vehicle.onGround() && canJump && driver.deltaMovement.y <= 0.1)) {
|
||||
// Spread out jumpforce so that variable height jumps are possible
|
||||
val jumpInputTicks = 6
|
||||
if (driver.jumping && (state.jumpTicks.get() >= 0 && state.jumpTicks.get() < jumpInputTicks)) {
|
||||
val appliedJumpForce = ((jumpForce*1.5) / jumpInputTicks) //* (1 - state.jumpTicks.get() / jumpInputTicks)
|
||||
newVelocity = Vec3(newVelocity.x, newVelocity.y + appliedJumpForce, newVelocity.z)
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
} else {
|
||||
// Set delay before next jump is possible after hitting ground
|
||||
val tickJumpDelay = 3
|
||||
state.jumpTicks.set(-tickJumpDelay)
|
||||
}
|
||||
} else if (vehicle.onGround() && state.jumpTicks.get() < 0) {
|
||||
// Tick off the delay once reaching the ground
|
||||
state.jumpTicks.set(state.jumpTicks.get() + 1)
|
||||
}
|
||||
|
||||
// Zero out lateral velocity possibly picked up from a controller transition
|
||||
newVelocity = Vec3(0.0, newVelocity.y, newVelocity.z)
|
||||
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
private fun getWalkSpeed( vehicle: PokemonEntity ): Double {
|
||||
val walkspeed = vehicle.runtime.resolveDouble(vehicle.behaviour.moving.walk.walkSpeed)
|
||||
val movementSpeed = vehicle.attributes.getValue(Attributes.MOVEMENT_SPEED)
|
||||
val speedModifier = 1.2 * 0.35
|
||||
return walkspeed * movementSpeed * speedModifier
|
||||
|
||||
}
|
||||
|
||||
private fun rotateOverTime(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
if (vehicle !is OrientationControllable) return
|
||||
val controller = vehicle.orientationController
|
||||
|
||||
// Smoothly rotate the model's up vector toward the (separately computed,
|
||||
// smoothed) field-gradient normal. Purely cosmetic - does not affect attachment
|
||||
// or movement, which are driven by state.attachmentNormal instead.
|
||||
val upAlignmentRatePerTick = 0.2
|
||||
val frameBlend = 1.0 - (1.0 - upAlignmentRatePerTick).pow(deltaTime * 20.0)
|
||||
alignUpToSurfaceNormal(controller, state.renderSurfaceNormal.get(), frameBlend.toFloat())
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates [controller]'s orientation so its up vector moves a fraction of the way
|
||||
* toward [targetUp] - partial per call, not instant, so alignment settles smoothly
|
||||
* rather than snapping every time the target changes.
|
||||
*/
|
||||
private fun alignUpToSurfaceNormal(controller: OrientationController, targetUp: Vec3, alignmentBlend: Float) {
|
||||
if (targetUp.lengthSqr() < 1e-6) return
|
||||
val targetUpVec = Vector3f(targetUp.x.toFloat(), targetUp.y.toFloat(), targetUp.z.toFloat()).normalize()
|
||||
|
||||
controller.updateOrientation { current ->
|
||||
val currentUp = current.transform(Vector3f(UP), Vector3f())
|
||||
if (currentUp.lengthSquared() < 1e-6f) return@updateOrientation current
|
||||
|
||||
// Full rotation that would exactly align currentUp to targetUp...
|
||||
val fullCorrection = Quaternionf().rotationTo(currentUp, targetUpVec)
|
||||
// ...but only take a partial step toward it, so alignment is smooth, not instant.
|
||||
val steppedCorrection = Quaternionf().slerp(fullCorrection, alignmentBlend)
|
||||
|
||||
Matrix3f().set(steppedCorrection).mul(current, Matrix3f())
|
||||
}
|
||||
}
|
||||
|
||||
override fun steer(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
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: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val vehicleController = vehicle.orientationController
|
||||
|
||||
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(xInput, 0.0, 0.0)
|
||||
}
|
||||
|
||||
|
||||
override fun canJump(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: ClimbSettings) = ClimbState()
|
||||
}
|
||||
|
||||
class ClimbSettings : RidingBehaviourSettings {
|
||||
override val key = ClimbBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var canSprint = "true".asExpression()
|
||||
private set
|
||||
|
||||
var lookYawLimit = "90.0f".asExpression()
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.4)".asExpression()
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', 0.5, 2.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
//Between a one block jump and a six block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.0, 0.38)".asExpression()
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(canSprint)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
canJump = buffer.readExpression()
|
||||
canSprint = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class ClimbState : RidingBehaviourState() {
|
||||
var sprinting = ridingState(false, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
var jumpTicks = ridingState(0, Side.CLIENT)
|
||||
|
||||
// Discrete, physics-facing: which of the 6 faces we're attached to right now.
|
||||
// Drives newDownForce in velocity(). Set by updateAttachment().
|
||||
var attachmentNormal = ridingState(Vec3(0.0, 1.0, 0.0), Side.CLIENT)
|
||||
var attachmentPoint = ridingState(Vec3(0.0, 1.0, 0.0), Side.CLIENT)
|
||||
|
||||
// Smoothed, rendering-facing: the field-gradient normal used only to slowly rotate
|
||||
// the model's visual up vector in rotateOverTime(). Deliberately separate from
|
||||
// attachmentNormal - see comments in tick(). Set by updateRenderSurfaceNormal().
|
||||
var renderSurfaceNormal = ridingState(Vec3(0.0, 1.0, 0.0), Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(sprinting.get())
|
||||
buffer.writeBoolean(inAir.get())
|
||||
buffer.writeInt(jumpTicks.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
sprinting.set(buffer.readBoolean(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
jumpTicks.set(buffer.readInt(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
sprinting.set(false, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
jumpTicks.set(0, forced = true)
|
||||
attachmentNormal.set(Vec3(0.0, 1.0, 0.0), forced = true)
|
||||
attachmentPoint.set(Vec3(0.0, 1.0, 0.0), forced = true)
|
||||
renderSurfaceNormal.set(Vec3(0.0, 1.0, 0.0), forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = ClimbState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.sprinting.set(this.sprinting.get(), forced = true)
|
||||
it.inAir.set(this.inAir.get(), forced = true)
|
||||
it.jumpTicks.set(this.jumpTicks.get(), forced = true)
|
||||
it.attachmentNormal.set(this.attachmentNormal.get(), forced = true)
|
||||
it.attachmentPoint.set(this.attachmentNormal.get(), forced = true)
|
||||
it.renderSurfaceNormal.set(this.renderSurfaceNormal.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is ClimbState) return false
|
||||
if (previous.sprinting.get() != sprinting.get()) return true
|
||||
if (previous.inAir.get() != inAir.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAttachment(state: ClimbState, vehicle: PokemonEntity) {
|
||||
val probeMargin = 0.15
|
||||
val currentFace = AttachmentFace.entries.firstOrNull { it.normal == state.attachmentNormal.get() }
|
||||
val face = determineAttachmentFace(vehicle, currentFace, probeMargin)
|
||||
if (face != null) {
|
||||
state.attachmentNormal.set(face.normal)
|
||||
}
|
||||
// face == null means nothing's currently in reach - leave attachmentNormal as
|
||||
// whatever it last was until the jump/detach state takes over.
|
||||
}
|
||||
|
||||
enum class AttachmentFace(val normal: Vec3) {
|
||||
UP(Vec3(0.0, 1.0, 0.0)),
|
||||
DOWN(Vec3(0.0, -1.0, 0.0)),
|
||||
NORTH(Vec3(0.0, 0.0, -1.0)),
|
||||
SOUTH(Vec3(0.0, 0.0, 1.0)),
|
||||
EAST(Vec3(1.0, 0.0, 0.0)),
|
||||
WEST(Vec3(-1.0, 0.0, 0.0))
|
||||
}
|
||||
|
||||
/** Thin slab test on one face of the vehicle's actual bounding box - true if anything solid is touching that side. */
|
||||
fun probeFaceContact(vehicle: PokemonEntity, face: AttachmentFace, probeMargin: Double): Boolean {
|
||||
val box = vehicle.boundingBox
|
||||
val testBox = when (face) {
|
||||
AttachmentFace.UP -> AABB(box.minX, box.maxY, box.minZ, box.maxX, box.maxY + probeMargin, box.maxZ)
|
||||
AttachmentFace.DOWN -> AABB(box.minX, box.minY - probeMargin, box.minZ, box.maxX, box.minY, box.maxZ)
|
||||
AttachmentFace.NORTH -> AABB(box.minX, box.minY, box.minZ - probeMargin, box.maxX, box.maxY, box.minZ)
|
||||
AttachmentFace.SOUTH -> AABB(box.minX, box.minY, box.maxZ, box.maxX, box.maxY, box.maxZ + probeMargin)
|
||||
AttachmentFace.EAST -> AABB(box.maxX, box.minY, box.minZ, box.maxX + probeMargin, box.maxY, box.maxZ)
|
||||
AttachmentFace.WEST -> AABB(box.minX - probeMargin, box.minY, box.minZ, box.minX, box.maxY, box.maxZ)
|
||||
}
|
||||
return !vehicle.level().noCollision(vehicle, testBox)
|
||||
}
|
||||
|
||||
/** Stays on the current face if it's still touching, otherwise scans for a new one. */
|
||||
fun determineAttachmentFace(vehicle: PokemonEntity, currentFace: AttachmentFace?, probeMargin: Double): AttachmentFace? {
|
||||
if (currentFace != null && probeFaceContact(vehicle, currentFace, probeMargin)) {
|
||||
return currentFace
|
||||
}
|
||||
return AttachmentFace.entries.firstOrNull { probeFaceContact(vehicle, it, probeMargin) }
|
||||
}
|
||||
@@ -22,7 +22,6 @@ 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,6 +44,7 @@ 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,23 +267,12 @@ 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()
|
||||
@@ -409,6 +398,37 @@ 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,
|
||||
@@ -455,6 +475,14 @@ 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,6 +12,7 @@ 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
|
||||
@@ -24,7 +25,6 @@ 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,6 +45,7 @@ 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
|
||||
@@ -97,15 +98,7 @@ 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
|
||||
@@ -134,6 +127,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
}
|
||||
|
||||
inAirCheck(state, vehicle)
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
@@ -203,23 +197,12 @@ 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()
|
||||
@@ -325,6 +308,34 @@ 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,
|
||||
@@ -376,6 +387,14 @@ 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,6 +12,7 @@ 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
|
||||
@@ -24,7 +25,6 @@ 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,6 +44,7 @@ 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
|
||||
@@ -97,15 +98,7 @@ 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) {
|
||||
@@ -119,6 +112,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
}
|
||||
|
||||
inAirCheck(state, vehicle)
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
@@ -178,23 +172,12 @@ 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()
|
||||
@@ -295,6 +278,34 @@ 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,
|
||||
@@ -341,6 +352,14 @@ 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,6 +11,7 @@ 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
|
||||
@@ -22,7 +23,6 @@ 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,6 +40,7 @@ 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
|
||||
@@ -132,23 +133,12 @@ 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)
|
||||
|
||||
@@ -269,6 +259,34 @@ 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,
|
||||
@@ -316,6 +334,10 @@ 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,6 +10,7 @@ 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
|
||||
@@ -21,7 +22,6 @@ 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,6 +29,7 @@ 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
|
||||
@@ -95,23 +96,12 @@ 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
|
||||
@@ -122,6 +112,34 @@ 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,
|
||||
@@ -168,6 +186,10 @@ 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,7 +16,6 @@ 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
|
||||
@@ -55,8 +54,6 @@ 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")
|
||||
}
|
||||
|
||||
@@ -204,7 +201,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
state.lastVelocity.set(prevVel)
|
||||
return state.lastVelocity.get()
|
||||
return state.lastVelocity.get().yRot(vehicle.yRot.toRadians())
|
||||
} else {
|
||||
if(state.hasBreached.get()) {
|
||||
state.clearMouseSmoothers.set(true)
|
||||
@@ -241,17 +238,18 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val worldVelVec = localVelVec.mul(orientation).toVec3d()
|
||||
state.lastVelocity.set(worldVelVec) // Set last velocity to be used during breaching
|
||||
|
||||
return worldVelVec
|
||||
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
|
||||
return worldVelVec.yRot(vehicle.yRot.toRadians())
|
||||
}
|
||||
|
||||
private fun rotateOverTime(
|
||||
override fun angRollVel(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
if (vehicle !is OrientationControllable) return
|
||||
): Vec3 {
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
@@ -269,8 +267,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, driver, deltaTime)
|
||||
|
||||
val correction = deltaTime * CORRECTION_FORCE_TO_DEGREES_PER_SECOND
|
||||
controller.rotate(0f, (state.currPitchCorrectionForce.get() * correction).toFloat(), (state.currRollCorrectionForce.get() * correction).toFloat())
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
@@ -356,18 +354,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
override fun rotationOnMouseXY(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -500,6 +487,10 @@ 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,16 +17,14 @@ 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
|
||||
@@ -256,17 +254,29 @@ 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()) {
|
||||
return RideFrame.yawToWorld(Vec3(currVel.x, min(0.0, currVel.y), currVel.z), vehicle.yRot)
|
||||
val worldVelVec = Vec3(currVel.x, min(0.0, currVel.y), currVel.z)
|
||||
return worldVelVec
|
||||
} else {
|
||||
val vertVec = Vec3(0.0, currVel.y, 0.0)
|
||||
val horzVec = RideFrame.yawToWorld(Vec3(currVel.x, 0.0, 0.0), vehicle.yRot)
|
||||
val horzVec = Vec3(currVel.x, 0.0, 0.0)
|
||||
val localZVelVec = Vector3f(0.0f, 0.0f, currVel.z.toFloat() * -1.0f) // Flip the z axis to make this left handed
|
||||
val worldVelVec = localZVelVec.mul(orientation).toVec3d()
|
||||
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
|
||||
val worldVelVec = localZVelVec.mul(orientation).toVec3d().yRot(vehicle.yRot.toRadians())
|
||||
// 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,
|
||||
@@ -313,17 +323,7 @@ class SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
|
||||
|
||||
}
|
||||
|
||||
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(
|
||||
override fun rotationOnMouseXY(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
@@ -432,6 +432,10 @@ 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
|
||||
}
|
||||
@@ -448,10 +452,8 @@ class SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 mouseModifiesDriverRotation(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Pair<Boolean, Boolean> {
|
||||
return Pair(false, state.onSurface.get())
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.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.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
|
||||
}
|
||||
|
||||
/** 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
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.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
|
||||
)
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.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()
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.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)
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.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,7 +198,17 @@ interface Spawner {
|
||||
val mid = chunk.getBlockState(startPos)
|
||||
val above = chunk.getBlockState(abovePos)
|
||||
|
||||
return above.isPathfindable(PathComputationType.AIR) && mid.isAir
|
||||
// Above must be non-solid
|
||||
if (!above.isPathfindable(PathComputationType.AIR)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Position must be non-air
|
||||
if (mid.isAir) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
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.375, 0.6875, 1.0, 0.6875)
|
||||
val CEILING_NORTH = Shapes.box(0.3125, 0.625, 0.3125, 0.6875, 1.0, 0.6875)
|
||||
}
|
||||
|
||||
init {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client
|
||||
|
||||
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,7 +182,6 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
|
||||
showHorizontalMouse = false
|
||||
}
|
||||
VehicleBehaviour.KEY -> {}
|
||||
BoardBehaviour.KEY -> {}
|
||||
}
|
||||
|
||||
val centerX = minecraft.window.guiScaledWidth / 2
|
||||
|
||||
@@ -748,6 +748,7 @@ class PokedexGUI private constructor(
|
||||
}
|
||||
TAB_DROPS -> {
|
||||
(tabInfoElement as DropsScrollingWidget).dropTable = form.drops
|
||||
(tabInfoElement as DropsScrollingWidget).aspects = aspects.toSet()
|
||||
(tabInfoElement as DropsScrollingWidget).setEntries()
|
||||
}
|
||||
TAB_MOVES -> {
|
||||
|
||||
@@ -10,6 +10,9 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.drop.LootTableDropEntry
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.AbstractChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
||||
@@ -17,17 +20,26 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
|
||||
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import java.text.DecimalFormat
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.tags.TagKey
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import java.text.DecimalFormat
|
||||
|
||||
class DropsScrollingWidget(val pX: Int, val pY: Int): ScrollingWidget<DropsScrollingWidget.DropWidgetEntry>(
|
||||
width = PokedexGUIConstants.HALF_OVERLAY_WIDTH - 2,
|
||||
@@ -38,6 +50,7 @@ class DropsScrollingWidget(val pX: Int, val pY: Int): ScrollingWidget<DropsScrol
|
||||
) {
|
||||
|
||||
var dropTable: DropTable = DropTable()
|
||||
var aspects: Set<String> = emptySet()
|
||||
|
||||
companion object {
|
||||
val df = DecimalFormat("#.##")
|
||||
@@ -50,9 +63,157 @@ class DropsScrollingWidget(val pX: Int, val pY: Int): ScrollingWidget<DropsScrol
|
||||
override fun getX() = pX
|
||||
override fun getY() = pY
|
||||
|
||||
private fun checkPokemonProperty(propertiesString: String): Boolean {
|
||||
println("CHECK PROPERTY CALLED: $propertiesString")
|
||||
val split = propertiesString.split("=", limit = 2)
|
||||
if (split.size != 2) return true
|
||||
val key = split[0]
|
||||
val value = split[1]
|
||||
val provider = SpeciesFeatures.getFeatures()
|
||||
.filterIsInstance<AbstractChoiceSpeciesFeatureProvider<*>>()
|
||||
.firstOrNull { key in it.keys }
|
||||
?: return true
|
||||
|
||||
val feature = provider.fromString(value) ?: return false
|
||||
if (!provider.isAspect) return true
|
||||
val requiredAspect = provider.getAspect(feature)
|
||||
|
||||
return requiredAspect in aspects
|
||||
}
|
||||
|
||||
private fun checkPokemonConditions(condition: JsonObject): Boolean {
|
||||
val conditionType = condition["condition"].asString
|
||||
if (conditionType == "cobblemon:pokemon_properties") {
|
||||
return checkPokemonProperty(
|
||||
condition["properties"].asString
|
||||
)
|
||||
}
|
||||
if (conditionType == "minecraft:all_of") {
|
||||
val terms = condition["terms"]?.asJsonArray ?: return true
|
||||
return terms.all {
|
||||
checkPokemonConditions(it.asJsonObject)
|
||||
}
|
||||
}
|
||||
if (conditionType == "minecraft:any_of") {
|
||||
val terms = condition["terms"]?.asJsonArray ?: return true
|
||||
return terms.any {
|
||||
checkPokemonConditions(it.asJsonObject)
|
||||
}
|
||||
}
|
||||
if (conditionType == "minecraft:inverted") {
|
||||
val term = condition["term"]?.asJsonObject ?: return true
|
||||
return !checkPokemonConditions(term)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun setEntries() {
|
||||
dropTable.entries.forEach {
|
||||
if (it is ItemDropEntry) addEntry(DropWidgetEntry(it))
|
||||
clearEntries()
|
||||
dropTable.entries.forEach { it ->
|
||||
if (it is ItemDropEntry) {
|
||||
addEntry(DropWidgetEntry(it, mutableSetOf(it.item)))
|
||||
} else if (it is LootTableDropEntry) { // LootTable doesn't expose pools, so we gotta do all this bullshit
|
||||
val server = server() ?: return
|
||||
|
||||
val lootTableResource = ResourceLocation.fromNamespaceAndPath(
|
||||
it.id.namespace,
|
||||
"loot_table/${it.id.path}.json"
|
||||
)
|
||||
|
||||
val resource = server.resourceManager
|
||||
.getResource(lootTableResource)
|
||||
.orElse(null)
|
||||
?: return@forEach
|
||||
|
||||
val json = resource.openAsReader().use { reader ->
|
||||
JsonParser.parseReader(reader).asJsonObject
|
||||
}
|
||||
|
||||
json.getAsJsonArray("pools")?.forEach { poolElement ->
|
||||
val pool = poolElement.asJsonObject
|
||||
val entries = pool.getAsJsonArray("entries") ?: return@forEach
|
||||
|
||||
val validEntries = entries
|
||||
.map { entryElement ->
|
||||
entryElement.asJsonObject
|
||||
}.filter { entry ->
|
||||
val conditions = entry["conditions"]?.asJsonArray ?: JsonArray()
|
||||
|
||||
conditions.all { condition ->
|
||||
val conditionJson = condition.asJsonObject
|
||||
val conditionType = conditionJson["condition"].asString
|
||||
|
||||
conditionType != "cobblemon:biome" && checkPokemonConditions(conditionJson)
|
||||
}
|
||||
}
|
||||
|
||||
val totalWeight = validEntries.sumOf { entry ->
|
||||
entry["weight"]?.asInt ?: 1
|
||||
}
|
||||
|
||||
for (entry in validEntries) {
|
||||
val dropEntry = ItemDropEntry()
|
||||
val conditions = entry["conditions"]?.asJsonArray ?: JsonArray()
|
||||
val functions = entry["functions"]?.asJsonArray ?: JsonArray()
|
||||
val items = mutableSetOf<ResourceLocation>()
|
||||
var tag: ResourceLocation? = null
|
||||
|
||||
if (validEntries.size > 1 && totalWeight > 0) {
|
||||
val weight = entry["weight"]?.asInt ?: 1
|
||||
dropEntry.percentage = weight.toFloat() / totalWeight.toFloat() * 100F
|
||||
}
|
||||
|
||||
conditions.forEach { condition ->
|
||||
val conditionJson = condition.asJsonObject
|
||||
val conditionType = conditionJson["condition"].asString
|
||||
|
||||
if (conditionType == "minecraft:random_chance") {
|
||||
val chance = conditionJson["chance"].asFloat * 100F
|
||||
|
||||
if (validEntries.size > 1) {
|
||||
dropEntry.percentage *= chance / 100F
|
||||
} else {
|
||||
dropEntry.percentage = chance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
functions.forEach { function ->
|
||||
val functionObj = function.asJsonObject
|
||||
if (functionObj["function"].asString == "set_count") {
|
||||
val count = functionObj["count"].asJsonObject
|
||||
val min = count["min"].asInt
|
||||
val max = count["max"].asInt
|
||||
dropEntry.quantityRange = IntRange(min, max)
|
||||
}
|
||||
}
|
||||
|
||||
if (entry["type"].asString == "minecraft:tag") {
|
||||
val itemRegistry = Minecraft.getInstance()
|
||||
.level!!
|
||||
.registryAccess()
|
||||
.lookupOrThrow(Registries.ITEM)
|
||||
val tagKey = TagKey.create(Registries.ITEM, ResourceLocation.parse(entry["name"].asString))
|
||||
tag = tagKey.location
|
||||
|
||||
val tagItems = itemRegistry.getOrThrow(tagKey)
|
||||
.stream()
|
||||
.map { holder ->
|
||||
holder.unwrapKey().map {
|
||||
it.location()
|
||||
}.orElse(null)
|
||||
}.toList().filterNotNull()
|
||||
|
||||
items.addAll(tagItems)
|
||||
} else if (entry["type"].asString == "minecraft:item") {
|
||||
items.add(ResourceLocation.parse(entry["name"].asString))
|
||||
}
|
||||
|
||||
addEntry(DropWidgetEntry(dropEntry, items, tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +251,7 @@ class DropsScrollingWidget(val pX: Int, val pY: Int): ScrollingWidget<DropsScrol
|
||||
shadow = true
|
||||
)
|
||||
|
||||
if (dropTable.entries.size == 0) {
|
||||
if (dropTable.entries.isEmpty()) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.pokedex.info.drops_empty"),
|
||||
@@ -128,7 +289,7 @@ class DropsScrollingWidget(val pX: Int, val pY: Int): ScrollingWidget<DropsScrol
|
||||
return children()[index] as DropWidgetEntry
|
||||
}
|
||||
|
||||
class DropWidgetEntry(val entry: ItemDropEntry): Slot<DropWidgetEntry>() {
|
||||
class DropWidgetEntry(val entry: ItemDropEntry, val items: MutableSet<ResourceLocation>, val tag: ResourceLocation? = null): Slot<DropWidgetEntry>() {
|
||||
override fun render(
|
||||
context: GuiGraphics,
|
||||
index: Int,
|
||||
@@ -141,9 +302,14 @@ class DropsScrollingWidget(val pX: Int, val pY: Int): ScrollingWidget<DropsScrol
|
||||
hovered: Boolean,
|
||||
tickDelta: Float
|
||||
) {
|
||||
if (items.isEmpty()) return
|
||||
context.pose().pushPose()
|
||||
context.pose().translate(0f, 0f, 100f)
|
||||
val itemStack = Minecraft.getInstance().player?.level()?.itemRegistry?.get(entry.item)?.defaultInstance ?: ItemStack.EMPTY
|
||||
|
||||
val index = ((Util.getMillis() / 1000L) % items.size).toInt()
|
||||
val itemLocation = items.elementAt(index)
|
||||
val itemStack = Minecraft.getInstance().player?.level()?.itemRegistry?.get(itemLocation)?.defaultInstance ?: ItemStack.EMPTY
|
||||
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = itemStack,
|
||||
x = x.toDouble(),
|
||||
@@ -151,28 +317,28 @@ class DropsScrollingWidget(val pX: Int, val pY: Int): ScrollingWidget<DropsScrol
|
||||
matrixStack = context.pose(),
|
||||
scale = PokedexGUIConstants.SCALE.toDouble()
|
||||
)
|
||||
|
||||
context.pose().pushPose()
|
||||
|
||||
val min = entry.quantityRange?.min() ?: entry.quantity
|
||||
val max = entry.quantityRange?.max() ?: entry.quantity
|
||||
|
||||
// var itemName: String = Component.translatable(itemStack.hoverName).string
|
||||
// itemName = if (itemName.length > 24) {
|
||||
// itemName.substring(0, 24 - 3) + "..."
|
||||
// } else {
|
||||
// itemName
|
||||
// }
|
||||
val displayName = if (tag == null) {
|
||||
itemStack.hoverName.copy().withStyle(ChatFormatting.BOLD)
|
||||
} else {
|
||||
Component.translatable("tag.item.${tag.namespace}.${tag.path.replace('/', '.')}").withStyle(ChatFormatting.BOLD)
|
||||
}
|
||||
|
||||
val displayText: MutableComponent = if (entry.quantityRange != null) {
|
||||
lang(
|
||||
"ui.pokedex.info.drops_display",
|
||||
itemStack.hoverName.copy().withStyle(ChatFormatting.BOLD),
|
||||
displayName,
|
||||
lang("ui.pokedex.info.drops_range", min, max)
|
||||
)
|
||||
} else {
|
||||
lang(
|
||||
"ui.pokedex.info.drops_display",
|
||||
itemStack.hoverName.copy().withStyle(ChatFormatting.BOLD),
|
||||
displayName,
|
||||
lang("ui.pokedex.info.drops_amount", entry.quantity)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@ 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) {
|
||||
@@ -24,7 +26,21 @@ object ClientboundUpdateOrientationHandler : ClientNetworkPacketHandler<Clientbo
|
||||
// of truth on vehicle orientation updates.
|
||||
if (vehicle.controllingPassenger != player) {
|
||||
packet.active?.let { shouldUseCustomOrientation ->
|
||||
// Passenger look angles get seeded by the camera on its first rolling frame
|
||||
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
vehicle.orientationController.active = shouldUseCustomOrientation
|
||||
}
|
||||
vehicle.orientationController.updateOrientation { _ -> packet.orientation }
|
||||
|
||||
@@ -14,7 +14,6 @@ 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
|
||||
@@ -37,29 +36,17 @@ 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.min
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
|
||||
object MountedCameraRenderer {
|
||||
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
|
||||
|
||||
private var returnTimer: Float = 0f
|
||||
private var lastHandledRotationTime: Double = Double.MIN_VALUE
|
||||
private var frameTime: Double = 0.0
|
||||
private var rollAngleStart: Float = 0f
|
||||
|
||||
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
|
||||
var smoothRotation: Quaternionf? = null
|
||||
|
||||
fun getCameraPosition(
|
||||
instance: Camera,
|
||||
@@ -92,7 +79,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(), eyeHeightOld, eyeHeight)
|
||||
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
|
||||
var offset = Vec3(0.0, currEyeHeight - (driver.bbHeight / 2), 0.0)
|
||||
var eyeOffset = Vec3(0.0, currEyeHeight - (driver.bbHeight / 2), 0.0)
|
||||
|
||||
@@ -139,10 +126,10 @@ object MountedCameraRenderer {
|
||||
} */
|
||||
|
||||
val offsetDistance = offset.length()
|
||||
val offsetDirection = safeDirection(offset, offsetDistance)
|
||||
val offsetDirection = offset.scale(1 / offsetDistance)
|
||||
|
||||
val eyeOffsetDistance = eyeOffset.length()
|
||||
val eyeOffsetDirection = safeDirection(eyeOffset, eyeOffsetDistance)
|
||||
val eyeOffsetDirection = eyeOffset.scale(1 / eyeOffsetDistance)
|
||||
|
||||
// Use getMaxZoom to calculate clipping
|
||||
val maxZoom: Double = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
|
||||
@@ -156,7 +143,6 @@ object MountedCameraRenderer {
|
||||
// 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()
|
||||
@@ -166,7 +152,6 @@ 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
|
||||
@@ -176,12 +161,11 @@ 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)
|
||||
state.returnTimer = 0f
|
||||
state.rollAngleStart = instance.rotation().getEulerAnglesYXZ(Vector3f()).z()
|
||||
returnTimer = 0f
|
||||
rollAngleStart = instance.rotation().getEulerAnglesYXZ(Vector3f()).z()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -198,31 +182,25 @@ object MountedCameraRenderer {
|
||||
val vehicleController: OrientationController = controllableVehicle.getOrientationController()
|
||||
|
||||
val isDriving = driver.controlledVehicle != null
|
||||
val ride = vehicle as? PokemonEntity
|
||||
|
||||
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()
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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 (state.smoothRotation == null) {
|
||||
state.smoothRotation = Quaternionf().set(instance.rotation())
|
||||
if (smoothRotation == null) {
|
||||
smoothRotation = Quaternionf().set(instance.rotation())
|
||||
}
|
||||
val rideAngs = newRotation.getEulerAnglesYXZ(Vector3f())
|
||||
val cameraAngs = state.smoothRotation!!.getEulerAnglesYXZ(Vector3f())
|
||||
val cameraAngs = smoothRotation!!.getEulerAnglesYXZ(Vector3f())
|
||||
|
||||
val degDiff = Mth.degreesDifference(
|
||||
Math.toDegrees(cameraAngs.y().toDouble()).toFloat(),
|
||||
@@ -259,103 +237,37 @@ object MountedCameraRenderer {
|
||||
|
||||
// Set rotations
|
||||
val smoothedRot = Quaternionf().rotateYXZ(newYaw.toFloat(), newPitch.toFloat(), newRoll)
|
||||
state.smoothRotation?.set(smoothedRot)
|
||||
smoothRotation?.set(smoothedRot)
|
||||
newRotation.set(smoothedRot)
|
||||
|
||||
}
|
||||
|
||||
// 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 carry = ride?.let { pokemon ->
|
||||
pokemon.ifRidingAvailableSupply(RideLookSettings.DEFAULT) { behaviour, settings, rideState ->
|
||||
behaviour.lookSettings(settings, rideState, pokemon)
|
||||
}.carry
|
||||
} ?: RideLookSettings.FULL_CARRY
|
||||
val previousBase = context.previousBase
|
||||
// Full carry is exactly what the look angles already do so skip the work
|
||||
if (carry < RideLookSettings.FULL_CARRY && previousBase != null) {
|
||||
carryLook(playerRotater, previousBase, newRotation, carry)
|
||||
}
|
||||
var rotationOffset = 0
|
||||
if (Minecraft.getInstance().options.cameraType.isMirrored) {
|
||||
newRotation.rotateY(Math.toRadians(180.0).toFloat())
|
||||
rotationOffset = 180
|
||||
}
|
||||
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()
|
||||
|
||||
// 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
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
val eulerAngs = newRotation.getEulerAnglesYXZ(Vector3f())
|
||||
setCameraRotations(newRotation, instance, cameraDuck)
|
||||
|
||||
/** 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)
|
||||
}
|
||||
|
||||
/** 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
|
||||
// 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)
|
||||
}
|
||||
|
||||
private fun applyTransitionRotation(
|
||||
@@ -365,14 +277,14 @@ object MountedCameraRenderer {
|
||||
val cameraDuck = instance as CameraDuck
|
||||
val entity = cameraDuck.`cobblemon$getEntity`()!!
|
||||
// If the transition has just started then
|
||||
if (state.returnTimer == 0f) {
|
||||
if (returnTimer == 0f) {
|
||||
resetDriverRotations(instance, entity)
|
||||
}
|
||||
|
||||
if (state.returnTimer < 1f) {
|
||||
if (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(state.returnTimer, state.rollAngleStart, 0.0f)
|
||||
val interpolatedRoll = Mth.lerp(returnTimer, rollAngleStart, 0.0f)
|
||||
val pitch = Math.toRadians(-entity.xRot.toDouble()).toFloat()
|
||||
val yaw = Math.toRadians((180 - entity.yRot).toDouble()).toFloat()
|
||||
|
||||
@@ -381,16 +293,16 @@ object MountedCameraRenderer {
|
||||
|
||||
setCameraRotations(interRot, instance, cameraDuck)
|
||||
|
||||
if (state.rollAngleStart == 0f) {
|
||||
state.rollAngleStart = 1f
|
||||
if (rollAngleStart == 0f) {
|
||||
rollAngleStart = 1f
|
||||
vehicleController.reset()
|
||||
resetDriverRotations(instance, entity)
|
||||
return false
|
||||
}
|
||||
state.returnTimer += instance.partialTickTime * (1.0f / 20.0f)
|
||||
returnTimer += instance.partialTickTime * (1.0f / 20.0f)
|
||||
return true
|
||||
} else {
|
||||
state.returnTimer = 1f
|
||||
returnTimer = 1f
|
||||
vehicleController.reset()
|
||||
resetDriverRotations(instance, entity)
|
||||
return false
|
||||
@@ -417,7 +329,7 @@ object MountedCameraRenderer {
|
||||
val offset = if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
|
||||
cameraOffsets[locatorName + "_reverse"]!!
|
||||
} else if (thirdPersonReverse && cameraOffsets.containsKey("seat_default_reverse")) {
|
||||
cameraOffsets["seat_default_reverse"]!!
|
||||
cameraOffsets[locatorName + "seat_default_reverse"]!!
|
||||
} else if (cameraOffsets.containsKey(locatorName)) {
|
||||
cameraOffsets[locatorName]!!
|
||||
} else if (cameraOffsets.containsKey("seat_default")) {
|
||||
@@ -442,10 +354,6 @@ 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) {
|
||||
@@ -476,14 +384,14 @@ object MountedCameraRenderer {
|
||||
}
|
||||
|
||||
private fun setCameraRotations(rotation: Quaternionf, instance: Camera, duck: CameraDuck) {
|
||||
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`())
|
||||
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)
|
||||
|
||||
// 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`()))
|
||||
FORWARDS.rotate(rotation, duck.`cobblemon$getForwards`())
|
||||
UP.rotate(rotation, duck.`cobblemon$getUp`())
|
||||
LEFT.rotate(cameraRotation, duck.`cobblemon$getLeft`())
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.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()
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ class DusclopsModel (root: ModelPart) : PokemonPosableModel(root) {
|
||||
lateinit var battleidle: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
animations["cry"] = "q.bedrock_stateful('squirtle', 'cry')".asExpressionLike()
|
||||
animations["cry"] = "q.bedrock_stateful('dusclops', 'cry')".asExpressionLike()
|
||||
|
||||
val blink = quirk { bedrockStateful("dusclops", "blink") }
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ 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
|
||||
@@ -116,6 +117,7 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.component.DataComponents
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.nbt.NbtUtils
|
||||
@@ -131,6 +133,7 @@ import net.minecraft.network.protocol.game.DebugPackets
|
||||
import net.minecraft.network.syncher.EntityDataAccessor
|
||||
import net.minecraft.network.syncher.EntityDataSerializers
|
||||
import net.minecraft.network.syncher.SynchedEntityData
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerEntity
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -172,6 +175,7 @@ import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.EmptyFluid
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.level.storage.loot.LootTable
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
@@ -241,6 +245,12 @@ open class PokemonEntity(
|
||||
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
|
||||
override fun getDefaultLootTable(): ResourceKey<LootTable?>? {
|
||||
println(cobblemonResource("pokemon/${this.pokemon.species.resourceIdentifier.path}"))
|
||||
// return ResourceKey.create(Registries.LOOT_TABLE, )
|
||||
return super.getDefaultLootTable()
|
||||
}
|
||||
|
||||
val form: FormData
|
||||
get() = pokemon.form
|
||||
val behaviour: FormPokemonBehaviour
|
||||
@@ -1865,9 +1875,28 @@ 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 {
|
||||
super.handleRelativeFrictionAndCalculateMovement(deltaMovement, friction)
|
||||
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)
|
||||
}
|
||||
|
||||
return this.deltaMovement
|
||||
}
|
||||
|
||||
@@ -1900,15 +1929,19 @@ open class PokemonEntity(
|
||||
if (!this.hasRider() || this.controllingPassenger == null) {
|
||||
super.travel(movementInput)
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
val inp = ifRidingAvailableSupply(fallback = Vec3.ZERO) { behaviour, settings, state ->
|
||||
behaviour.velocity(settings, state, this, this.controllingPassenger as Player, deltaMovement)
|
||||
}
|
||||
|
||||
// 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 inertia = ifRidingAvailableSupply(fallback = 0.5) { behaviour, settings, state ->
|
||||
@@ -2366,11 +2399,6 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.cobblemon.mod.common.loot
|
||||
|
||||
import com.cobblemon.mod.common.loot.conditions.BiomeLootItemCondition
|
||||
import com.cobblemon.mod.common.loot.conditions.PokemonPropertiesLootItemCondition
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
|
||||
|
||||
object CobblemonLootItemConditions :
|
||||
PlatformRegistry<Registry<LootItemConditionType>, ResourceKey<Registry<LootItemConditionType>>, LootItemConditionType>() {
|
||||
|
||||
@JvmField
|
||||
val POKEMON_PROPERTIES_CONDITION = create(
|
||||
cobblemonResource("pokemon_properties"),
|
||||
LootItemConditionType(PokemonPropertiesLootItemCondition.CODEC)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BIOME_CONDITION = create(
|
||||
cobblemonResource("biome"),
|
||||
LootItemConditionType(BiomeLootItemCondition.CODEC)
|
||||
)
|
||||
|
||||
fun touch() = Unit
|
||||
|
||||
override val registry: Registry<LootItemConditionType>
|
||||
get() = BuiltInRegistries.LOOT_CONDITION_TYPE
|
||||
override val resourceKey: ResourceKey<Registry<LootItemConditionType>>
|
||||
get() = Registries.LOOT_CONDITION_TYPE
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.cobblemon.mod.common.loot.conditions
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.conditional.makeCodecForRegistryLikeCondition
|
||||
import com.cobblemon.mod.common.loot.CobblemonLootItemConditions
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.storage.loot.LootContext
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
|
||||
|
||||
class BiomeLootItemCondition(
|
||||
val biome: RegistryLikeCondition<Biome>
|
||||
) : LootItemCondition {
|
||||
override fun getType(): LootItemConditionType {
|
||||
return CobblemonLootItemConditions.BIOME_CONDITION
|
||||
}
|
||||
|
||||
override fun test(t: LootContext): Boolean {
|
||||
val temp = biome.fits(t.level.getBiome(t.getParam(LootContextParams.ORIGIN).toBlockPos()))
|
||||
return temp
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BIOME_CODEC = makeCodecForRegistryLikeCondition("biome", Registries.BIOME)
|
||||
|
||||
val CODEC: MapCodec<BiomeLootItemCondition> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
BIOME_CODEC.fieldOf("biome")
|
||||
.forGetter(BiomeLootItemCondition::biome)
|
||||
).apply(instance, ::BiomeLootItemCondition)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.cobblemon.mod.common.loot.conditions
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.loot.CobblemonLootItemConditions
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.world.level.storage.loot.LootContext
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType
|
||||
|
||||
class PokemonPropertiesLootItemCondition(
|
||||
val properties: PokemonProperties
|
||||
) : LootItemCondition {
|
||||
override fun getType(): LootItemConditionType {
|
||||
return CobblemonLootItemConditions.POKEMON_PROPERTIES_CONDITION
|
||||
}
|
||||
|
||||
override fun test(t: LootContext): Boolean {
|
||||
val pokemon = t.getParamOrNull(LootContextParams.THIS_ENTITY) as? PokemonEntity ?: return false
|
||||
return properties.matches(pokemon)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<PokemonPropertiesLootItemCondition> = RecordCodecBuilder.mapCodec { instance ->
|
||||
instance.group(
|
||||
PokemonProperties.CODEC.fieldOf("properties")
|
||||
.forGetter(PokemonPropertiesLootItemCondition::properties)
|
||||
).apply(instance, ::PokemonPropertiesLootItemCondition)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ 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
|
||||
@@ -36,7 +35,6 @@ class RideSettingsSyncPacket(
|
||||
val hover: HoverSettings,
|
||||
val jet: JetSettings,
|
||||
val rocket: RocketSettings,
|
||||
val board: BoardSettings,
|
||||
val horse: HorseSettings,
|
||||
val minekart: MinekartSettings,
|
||||
val vehicle: VehicleSettings,
|
||||
@@ -54,7 +52,6 @@ class RideSettingsSyncPacket(
|
||||
hover.encode(buffer)
|
||||
jet.encode(buffer)
|
||||
rocket.encode(buffer)
|
||||
board.encode(buffer)
|
||||
horse.encode(buffer)
|
||||
minekart.encode(buffer)
|
||||
vehicle.encode(buffer)
|
||||
@@ -74,7 +71,6 @@ class RideSettingsSyncPacket(
|
||||
hover = HoverSettings(),
|
||||
jet = JetSettings(),
|
||||
rocket = RocketSettings(),
|
||||
board = BoardSettings(),
|
||||
horse = HorseSettings(),
|
||||
minekart = MinekartSettings(),
|
||||
vehicle = VehicleSettings(),
|
||||
@@ -89,7 +85,6 @@ class RideSettingsSyncPacket(
|
||||
hover.decode(buffer)
|
||||
jet.decode(buffer)
|
||||
rocket.decode(buffer)
|
||||
board.decode(buffer)
|
||||
horse.decode(buffer)
|
||||
minekart.decode(buffer)
|
||||
vehicle.decode(buffer)
|
||||
|
||||
@@ -17,7 +17,12 @@ 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.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.*
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
@@ -45,9 +50,7 @@ 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)*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)"]
|
||||
"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)"]
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["0.4*v.rsize", "0.4*v.rsize"],
|
||||
|
||||
@@ -227,6 +227,14 @@
|
||||
{"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",
|
||||
@@ -332,6 +340,14 @@
|
||||
{"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,229 +134,114 @@
|
||||
"jaw2": [0, 27, -7.35]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "head_right",
|
||||
"parent": "head_angle",
|
||||
"pivot": [-3.525, 31, 0.25],
|
||||
"rotation": [5, 0, 0],
|
||||
"cubes": [
|
||||
{"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.5, 31, -2.5],
|
||||
"rotation": [0, 90, 0],
|
||||
"locators": {
|
||||
"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",
|
||||
"pivot": [-4.435, 32, -2.25]
|
||||
},
|
||||
{
|
||||
"name": "eye_ditto_right",
|
||||
"parent": "face_ditto_right",
|
||||
"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_right",
|
||||
"parent": "face_ditto_right",
|
||||
"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_left",
|
||||
"parent": "head_angle",
|
||||
"pivot": [3.525, 31, 0.25],
|
||||
"pivot": [3.53, 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}
|
||||
{"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": "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],
|
||||
"pivot": [4.53, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.525, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [88, 2]}
|
||||
{"origin": [3.54, 29, -4.25], "size": [1, 5, 4], "uv": [88, 2]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_neutral_left",
|
||||
"parent": "eye_left",
|
||||
"pivot": [4.525, 32.85095, -2.27179],
|
||||
"pivot": [4.54, 33, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.505, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [88, 12]}
|
||||
{"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.435, 32.1, -2.5],
|
||||
"pivot": [4.44, 32.5, -2.25],
|
||||
"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]}
|
||||
{"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.435, 32.85, -2.5],
|
||||
"pivot": [4.43, 33, -2.25],
|
||||
"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]}
|
||||
{"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.525, 30.85095, -3.02179],
|
||||
"pivot": [4.55, 31, -3.05],
|
||||
"cubes": [
|
||||
{"origin": [3.525, 29.35, -4.04], "size": [1, 3, 2], "inflate": 0.02, "uv": [95, 0]}
|
||||
{"origin": [3.55, 29.5, -4.05], "size": [1, 3, 2], "uv": [95, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pupil_left2",
|
||||
"name": "eye_shine_left",
|
||||
"parent": "pupil_left",
|
||||
"pivot": [4.55, 31.35095, -3.02179],
|
||||
"pivot": [4.55, 31.5, -3.05],
|
||||
"cubes": [
|
||||
{"origin": [3.55, 30.35, -3.55], "size": [1, 2, 1], "uv": [106, 2]}
|
||||
{"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": [3.925, 31.35095, -2.27179],
|
||||
"pivot": [4.44, 31.5, -2.25],
|
||||
"cubes": [
|
||||
{"origin": [3.425, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [99, 2]}
|
||||
{"origin": [3.44, 29, -4.25], "size": [1, 5, 4], "inflate": 0.022, "uv": [99, 26]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "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",
|
||||
@@ -368,7 +253,7 @@
|
||||
"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}
|
||||
{"origin": [4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [113, 1]}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -376,8 +261,139 @@
|
||||
"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}
|
||||
{"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],
|
||||
"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}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "locator_eye_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-4.54, 31, -2.46],
|
||||
"rotation": [0, 90, 0],
|
||||
"locators": {
|
||||
"eye1": [-4.54, 31, -2.46]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "face_ditto_right",
|
||||
"parent": "head_right",
|
||||
"pivot": [-4.435, 32, -2.25]
|
||||
},
|
||||
{
|
||||
"name": "eye_ditto_right",
|
||||
"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}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "eye_closed_ditto_right",
|
||||
"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}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -830,11 +830,6 @@
|
||||
{"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",
|
||||
@@ -913,6 +908,15 @@
|
||||
{"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",
|
||||
@@ -950,7 +954,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, 76]}
|
||||
{"origin": [0.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [87, 85]}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -958,7 +962,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, 79]}
|
||||
{"origin": [0.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [87, 88]}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -995,6 +999,15 @@
|
||||
{"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",
|
||||
@@ -1032,7 +1045,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, 76], "mirror": true}
|
||||
{"origin": [-3.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [76, 85], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1040,7 +1053,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, 79], "mirror": true}
|
||||
{"origin": [-3.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [76, 88], "mirror": true}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -846,14 +846,6 @@
|
||||
"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,7 +8,6 @@
|
||||
"poses": {
|
||||
"standing": {
|
||||
"poseTypes": ["STAND", "NONE", "PORTRAIT", "PROFILE"],
|
||||
"isBattle": false,
|
||||
"animations": [
|
||||
"q.look('neck', 1, 1, 70, -10, 20, -20)",
|
||||
"q.look('head_ai')",
|
||||
@@ -35,6 +34,9 @@
|
||||
"part": "wing_closed_left",
|
||||
"isVisible": true
|
||||
}
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('fearow', 'blink')"
|
||||
]
|
||||
},
|
||||
"walking": {
|
||||
@@ -65,6 +67,9 @@
|
||||
"part": "wing_closed_left",
|
||||
"isVisible": true
|
||||
}
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('fearow', 'blink')"
|
||||
]
|
||||
},
|
||||
"hover": {
|
||||
@@ -96,6 +101,9 @@
|
||||
"part": "wing_closed_left",
|
||||
"isVisible": false
|
||||
}
|
||||
],
|
||||
"quirks": [
|
||||
"q.bedrock_quirk('fearow', 'blink')"
|
||||
]
|
||||
},
|
||||
"fly": {
|
||||
@@ -128,6 +136,9 @@
|
||||
"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": []
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
},
|
||||
"walking": {
|
||||
"poseTypes": ["WALK"],
|
||||
@@ -36,7 +36,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('froakie', 'ground_walk')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
},
|
||||
"swimming": {
|
||||
"poseTypes": ["WALK","SWIM"],
|
||||
@@ -46,7 +46,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('froakie', 'water_swim')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
},
|
||||
"float": {
|
||||
"poseTypes": ["STAND","FLOAT"],
|
||||
@@ -55,7 +55,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('froakie', 'water_idle')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('froakie', 'blink')"]
|
||||
},
|
||||
"sleep": {
|
||||
"poseTypes": ["SLEEP"],
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"q.look('head_ai', 1, 1, 5, -45, 45, -45)",
|
||||
"q.bedrock('frogadier', 'ground_idle')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
},
|
||||
"walking": {
|
||||
"poseTypes": ["WALK"],
|
||||
@@ -36,7 +36,7 @@
|
||||
"q.look('head_ai', 1, 1, 0, -45, 45, -45)",
|
||||
"q.bedrock('frogadier', 'ground_walk')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
},
|
||||
"swimming": {
|
||||
"poseTypes": ["WALK","SWIM"],
|
||||
@@ -46,7 +46,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('frogadier', 'water_swim')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
},
|
||||
"float": {
|
||||
"poseTypes": ["STAND","FLOAT"],
|
||||
@@ -55,7 +55,7 @@
|
||||
"q.look('head_ai')",
|
||||
"q.bedrock('frogadier', 'water_idle')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('frogadier', 'blink')"]
|
||||
},
|
||||
"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', q.array('quirk_look_left', 'quirk_look_right'), 8, 30, 1)"
|
||||
"q.bedrock_quirk('greninja', 'blink')","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": []
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"]
|
||||
},
|
||||
"running": {
|
||||
"poseTypes": ["WALK"],
|
||||
@@ -89,7 +89,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'ground_run')"
|
||||
],
|
||||
"quirks": []
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"]
|
||||
},
|
||||
"surface-swimming": {
|
||||
"poseTypes": ["WALK","SWIM"],
|
||||
@@ -101,7 +101,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_swim')"
|
||||
],
|
||||
"quirks": [],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"transformedParts": [
|
||||
{
|
||||
"part": "body",
|
||||
@@ -118,7 +118,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_idle')"
|
||||
],
|
||||
"quirks": [],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"transformedParts": [
|
||||
{
|
||||
"part": "body",
|
||||
@@ -136,7 +136,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_swim')"
|
||||
],
|
||||
"quirks": [],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"transformedParts": [
|
||||
{
|
||||
"part": "body",
|
||||
@@ -153,7 +153,7 @@
|
||||
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
|
||||
"q.bedrock('greninja', 'water_idle')"
|
||||
],
|
||||
"quirks": [],
|
||||
"quirks": ["q.bedrock_quirk('greninja', 'blink')"],
|
||||
"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",
|
||||
@@ -3671,6 +3671,12 @@
|
||||
"tag.item.cobblemon.terrain_plants": "Terrain Plants",
|
||||
"tag.item.cobblemon.tall_flowers": "Tall Flowers",
|
||||
"tag.item.cobblemon.small_flowers": "Small Flowers",
|
||||
"tag.item.cobblemon.drops.coral": "Any Coral",
|
||||
"tag.item.cobblemon.drops.dead_coral": "Any Dead Coral",
|
||||
"tag.item.cobblemon.drops.dyes": "Any Dye",
|
||||
"tag.item.cobblemon.drops.ink_sacs": "Any Ink Sac",
|
||||
"tag.item.cobblemon.drops.flowers": "Any Flower",
|
||||
"tag.item.cobblemon.drops.apricorn_sprouts": "Any Apricorn Sprout",
|
||||
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_hisuibiome": "Hisuian",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_kalosbiome": "Kalosian",
|
||||
|
||||
@@ -6526,7 +6526,7 @@
|
||||
"cobblemon.move.10000000voltthunderbolt": "1000만볼트",
|
||||
"cobblemon.species.unown-x.desc": "고대 문자를 닮은 모습의 포켓몬이다. 먼저 생겨난 것은 문자인가 안농인가. 연구 중이지만 아직도 수수께끼다.",
|
||||
"cobblemon.ui.pokedex.info.form.qwilfish-hisui": "히스이의 모습",
|
||||
"cobblemon.ui.pokedex.info.form.groudon-primal": "원시그란돈",
|
||||
"cobblemon.ui.pokedex.info.form.groudon-primal": "원시회귀",
|
||||
"cobblemon.ui.pokedex.info.form.burmy-sandy": "모래땅도롱",
|
||||
"cobblemon.ui.pokedex.info.form.abomasnow-mega": "메가눈설왕",
|
||||
"cobblemon.species.rotom-frost.desc": "특수한 모터로 움직이는 냉장고에 들어갔다. 냉기를 뿜으며 싸운다.",
|
||||
@@ -6758,7 +6758,7 @@
|
||||
"cobblemon.species.salamence-mega.desc": "앞다리를 껍질 틈에 얹어서 비행 태세를 갖춘다. 복잡한 지형도 고속으로 날아다닌다.",
|
||||
"cobblemon.species.metagross-mega.desc": "이길 수 없다고 판단하면 발톱으로 상대를 붙잡고 대폭발의 카운트를 시작한다.",
|
||||
"cobblemon.species.latios-mega.desc": "본 것이나 생각한 이미지를 상대방에게 영상으로 보여주는 능력을 지녔다. 인간의 말을 이해할 수 있다.",
|
||||
"cobblemon.ui.pokedex.info.form.kyogre-primal": "원시가이오가",
|
||||
"cobblemon.ui.pokedex.info.form.kyogre-primal": "원시회귀",
|
||||
"cobblemon.species.deoxys-attack.desc": "공격적인 형상으로 변화한 테오키스. 다른 모습을 보여 적을 혼란시키는 힘을 가졌다.",
|
||||
"cobblemon.ui.pokedex.info.form.deoxys-speed": "스피드폼",
|
||||
"cobblemon.ui.pokedex.info.form.burmy-trash": "슈레도롱",
|
||||
@@ -9549,5 +9549,125 @@
|
||||
"cobblemon.config.ui.world_spawning_blocklist": "월드별 스폰 차단 목록",
|
||||
"cobblemon.config.ui.world_spawning_blocklist.tooltip": "포켓몬 생성 로직을 비활성화하는 월드입니다.",
|
||||
"advancements.cobblemon.obtain_tm": "기술을 보여줘!",
|
||||
"advancements.cobblemon.obtain_tm.description": "TM을 찾거나 제작하세요"
|
||||
"advancements.cobblemon.obtain_tm.description": "TM을 찾거나 제작하세요",
|
||||
"itemGroup.cobblemon.tm_item": "코블몬: 기술머신",
|
||||
"block.cobblemon.pomohana": "이글이글꽃",
|
||||
"block.cobblemon.pauvillea": "파칙파칙꽃",
|
||||
"block.cobblemon.lobaila": "훌라훌라꽃",
|
||||
"block.cobblemon.wistensu": "하늘하늘꽃",
|
||||
"block.cobblemon.voltaconia": "찌릿꽃",
|
||||
"block.cobblemon.verdroot": "풀빛뿌리",
|
||||
"block.cobblemon.lucibell": "빛방울꽃",
|
||||
"block.cobblemon.mistleaf": "안개잎",
|
||||
"cobblemon.ui.pokedex.info.evolution_tree": "진화 계통",
|
||||
"cobblemon.ui.pokedex.evolution.item_interact.tooltip": "진화하려면 %1$s 필요",
|
||||
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_species": "%1$s와(과) 교환 시 진화",
|
||||
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_any_species": "교환 시 진화",
|
||||
"cobblemon.ui.pokedex.evolution.shedder.tooltip": "남는 몬스터볼과 파티 빈칸도 필요",
|
||||
"cobblemon.ui.pokedex.evolution.or": "또는",
|
||||
"cobblemon.ui.pokedex.evolution.one_of": "다음 중 하나:",
|
||||
"cobblemon.ui.pokedex.requirement.advancement.tooltip": "발전 과제 %1$s 달성 필요",
|
||||
"cobblemon.ui.pokedex.requirement.battle_critical_hits.tooltip": "배틀에서 급소 %1$s회 맞히기",
|
||||
"cobblemon.ui.pokedex.requirement.biome.anticondition.tooltip": "%1$s 바이옴 밖에 있기",
|
||||
"cobblemon.ui.pokedex.requirement.biome.both.tooltip": "%1$s 바이옴 안, %2$s 바이옴 밖에 있기",
|
||||
"cobblemon.ui.pokedex.requirement.biome.tooltip": "%1$s 바이옴에 있기",
|
||||
"cobblemon.ui.pokedex.requirement.blocks_traveled.tooltip": "%1$s블록 이동하기",
|
||||
"cobblemon.ui.pokedex.requirement.damage_taken.tooltip": "배틀에서 기절하지 않고 데미지 %1$s 받기",
|
||||
"cobblemon.ui.pokedex.requirement.defeat.tooltip": "배틀에서 %1$s을(를) 쓰러뜨리기",
|
||||
"cobblemon.ui.pokedex.requirement.friendship.tooltip": "친밀도 %1$s 이상 필요",
|
||||
"cobblemon.ui.pokedex.requirement.has_move.tooltip": "기술 %1$s 배우고 있기",
|
||||
"cobblemon.ui.pokedex.requirement.has_move_type.tooltip": "%1$s 타입 기술 배우고 있기",
|
||||
"cobblemon.ui.pokedex.requirement.held_item.tooltip": "%1$s 지니고 있기",
|
||||
"cobblemon.ui.pokedex.requirement.level.tooltip.min": "레벨 %1$s부터 진화",
|
||||
"cobblemon.ui.pokedex.requirement.level.tooltip.between": "레벨 %1$s~%2$s 사이에 진화",
|
||||
"cobblemon.ui.pokedex.requirement.moon_phase.tooltip": "달 모양이 %1$s일 때 진화",
|
||||
"cobblemon.ui.pokedex.requirement.nature.toxtricity_amped": "§a노력§r, §a용감§r, §a고집§r, §a개구쟁이§r, §a온순§r, §a장난꾸러기§r, §a촐랑§r, §a성급§r, §a명랑§r, §a천진난만§r, §a덜렁§r, §a건방§r, §a변덕§r 성격 중 하나",
|
||||
"cobblemon.ui.pokedex.requirement.nature.toxtricity_low_key": "§a외로움§r, §a대담§r, §a무사태평§r, §a겁쟁이§r, §a성실§r, §a조심§r, §a의젓§r, §a냉정§r, §a수줍음§r, §a차분§r, §a얌전§r, §a신중§r 성격 중 하나",
|
||||
"cobblemon.ui.pokedex.requirement.negate.tooltip": "아님: %1$s",
|
||||
"cobblemon.ui.pokedex.requirement.nickname.malamar": "이름이 §aDinnerbone§r 또는 §aGrumm§r이어야 함",
|
||||
"cobblemon.ui.pokedex.requirement.party_member.anticondition.tooltip": "파티에 %1$s 포켓몬이 없기",
|
||||
"cobblemon.ui.pokedex.requirement.party_member.tooltip": "파티에 %1$s 포켓몬이 있기",
|
||||
"cobblemon.ui.pokedex.requirement.pokemon_properties.tooltip": "조건: %1$s",
|
||||
"cobblemon.ui.pokedex.requirement.properties.and": "%1$s, %2$s",
|
||||
"cobblemon.ui.pokedex.requirement.properties.custom.cocoon_species": "%1$s 고치",
|
||||
"cobblemon.ui.pokedex.requirement.properties.held_item": "%1$s 지닌",
|
||||
"cobblemon.ui.pokedex.requirement.properties.int_feature": "%1$s %2$s",
|
||||
"cobblemon.ui.pokedex.requirement.properties.nature": "%1$s 성격",
|
||||
"cobblemon.ui.pokedex.requirement.properties.nickname": "이름이 %1$s인",
|
||||
"cobblemon.ui.pokedex.requirement.properties.type": "%1$s 타입",
|
||||
"cobblemon.ui.pokedex.requirement.property_range.tooltip": "%1$s이(가) %2$s~%3$s 사이여야 함",
|
||||
"cobblemon.ui.pokedex.requirement.recoil.tooltip": "배틀에서 반동 데미지 %1$s 받기",
|
||||
"cobblemon.ui.pokedex.requirement.stat_compare.tooltip": "%1$s이(가) %2$s보다 높아야 함",
|
||||
"cobblemon.ui.pokedex.requirement.stat_equal.tooltip": "%1$s와(과) %2$s이(가) 같아야 함",
|
||||
"cobblemon.ui.pokedex.requirement.structure.anticondition.tooltip": "%1$s 구조물 밖에 있기",
|
||||
"cobblemon.ui.pokedex.requirement.structure.both.tooltip": "%1$s 구조물 안, %2$s 구조물 밖에 있기",
|
||||
"cobblemon.ui.pokedex.requirement.structure.tooltip": "%1$s 구조물에 있기",
|
||||
"cobblemon.ui.pokedex.requirement.time_range.tooltip": "%1$s에 진화",
|
||||
"cobblemon.ui.pokedex.requirement.use_move.tooltip": "배틀에서 기술 %1$s 사용하기",
|
||||
"cobblemon.ui.pokedex.requirement.weather.tooltip.raining": "비가 올 때 진화",
|
||||
"cobblemon.ui.pokedex.requirement.weather.tooltip.thundering": "뇌우가 칠 때 진화",
|
||||
"cobblemon.ui.moon_phase.full_moon": "보름달",
|
||||
"cobblemon.ui.moon_phase.waning_gibbous": "기우는 볼록달",
|
||||
"cobblemon.ui.moon_phase.third_quarter": "하현달",
|
||||
"cobblemon.ui.moon_phase.waning_crescent": "그믐달",
|
||||
"cobblemon.ui.moon_phase.new_moon": "삭",
|
||||
"cobblemon.ui.moon_phase.waxing_crescent": "초승달",
|
||||
"cobblemon.ui.moon_phase.first_quarter": "상현달",
|
||||
"cobblemon.ui.moon_phase.waxing_gibbous": "차오르는 볼록달",
|
||||
"cobblemon.ui.time_range.any": "언제든",
|
||||
"cobblemon.ui.time_range.day": "낮",
|
||||
"cobblemon.ui.time_range.night": "밤",
|
||||
"cobblemon.ui.time_range.noon": "정오",
|
||||
"cobblemon.ui.time_range.midnight": "자정",
|
||||
"cobblemon.ui.time_range.dawn": "새벽",
|
||||
"cobblemon.ui.time_range.dusk": "해 질 녘",
|
||||
"cobblemon.ui.time_range.twilight": "황혼",
|
||||
"cobblemon.ui.time_range.morning": "아침",
|
||||
"cobblemon.ui.time_range.afternoon": "오후",
|
||||
"cobblemon.ui.time_range.predawn": "동트기 전",
|
||||
"cobblemon.ui.time_range.evening": "저녁",
|
||||
"cobblemon.subtitle.item.bright_powder.use": "반짝가루가 빛남",
|
||||
"cobblemon.config.ui.type_gem_max_connected": "연결된 타입주얼 블록 최대 수",
|
||||
"cobblemon.config.ui.type_gem_min_growth": "타입주얼 최소 성장",
|
||||
"cobblemon.config.ui.type_gem_growth_chance": "타입주얼 성장 확률",
|
||||
"cobblemon.config.ui.type_gem_initial_expansion_cycles": "타입주얼 초기 확장 횟수",
|
||||
"cobblemon.battle.activate.ficklebeam": "%1$s은(는) 전력을 다해 공격한다!",
|
||||
"tag.item.cobblemon.oricorio_flowers": "꽃꿀 꽃",
|
||||
"tag.item.cobblemon.terrain_plants": "필드 식물",
|
||||
"tag.item.cobblemon.tall_flowers": "큰 꽃",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_hisuibiome": "히스이",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_kalosbiome": "칼로스",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.cubone_alolabiome": "알로라",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.cubone_kantobiome": "관동",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.dartrix_alolabiome": "알로라",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.dartrix_hisuibiome": "히스이",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.dewott_hisuibiome": "히스이",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.dewott_unovabiome": "하나",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.exeggcute_alolabiome": "알로라",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.exeggcute_kantobiome": "관동",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.goomy_hisuibiome": "히스이",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.goomy_kalosbiome": "칼로스",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.koffing_galarbiome": "가라르",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.koffing_kantobiome": "관동",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.mimejr_galarbiome": "가라르",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.mimejr_sinnohbiome": "신오",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.petilil_hisuibiome": "히스이",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.petilil_unovabiome": "하나",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.pikachu_alolabiome": "알로라",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.pikachu_kantobiome": "관동",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.quilava_hisuibiome": "히스이",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.quilava_johtobiome": "성도",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.rufflet_hisuibiome": "히스이",
|
||||
"tag.worldgen.biome.cobblemon.evolution.regional.rufflet_unovabiome": "하나",
|
||||
"cobblemon.ui.pokedex.info.form.vivillon-icy snow": "빙설의 모양",
|
||||
"cobblemon.species.vivillon-icy-snow.desc": "태어난 땅의 기후나 풍토에 영향을 받아 모양이 달라진다. 얼어붙은 땅에서의 모습이다.",
|
||||
"cobblemon.ui.pokedex.info.form.vivillon-high plains": "황야의 모양",
|
||||
"cobblemon.species.vivillon-high-plains.desc": "태어난 땅의 기후나 풍토에 영향을 받아 모양이 달라진다. 비가 적게 내리는 땅에서의 모습이다.",
|
||||
"cobblemon.ui.pokedex.info.form.vivillon-inferno": "지옥의 모양",
|
||||
"cobblemon.species.vivillon-inferno.desc": "살고 있는 장소의 기후나 풍토에 따라 날개의 모양이 다른 포켓몬이다. 화려한 색의 날개 가루를 뿌린다.",
|
||||
"cobblemon.ui.pokedex.info.form.vivillon-void": "공허의 모양",
|
||||
"cobblemon.species.vivillon-void.desc": "살고 있는 장소의 기후나 풍토에 따라 날개의 모양이 다른 포켓몬이다. 화려한 색의 날개 가루를 뿌린다.",
|
||||
"cobblemon.ui.pokedex.info.form.vivillon-forsaken": "망각의 모양",
|
||||
"cobblemon.species.vivillon-forsaken.desc": "살고 있는 장소의 기후나 풍토에 따라 날개의 모양이 다른 포켓몬이다. 화려한 색의 날개 가루를 뿌린다.",
|
||||
"cobblemon.species.minior-red.desc": "겉껍질이 벗겨진 모습. 코어의 색이 다른 이유는 먹은 먹이의 성분이 다르기 때문이다."
|
||||
}
|
||||
@@ -9677,5 +9677,6 @@
|
||||
"cobblemon.config.ui.world_spawning_blocklist": "维度生成黑名单",
|
||||
"cobblemon.config.ui.world_spawning_blocklist.tooltip": "禁用宝可梦生成逻辑的维度。",
|
||||
"advancements.cobblemon.obtain_tm": "让我看看你的招式",
|
||||
"advancements.cobblemon.obtain_tm.description": "发现或刻写一个招式学习器"
|
||||
"advancements.cobblemon.obtain_tm.description": "发现或刻写一个招式学习器",
|
||||
"itemGroup.cobblemon.tm_item": "招式学习器"
|
||||
}
|
||||
|
Before Width: | Height: | Size: 788 B After Width: | Height: | Size: 417 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -42,7 +42,8 @@
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.5
|
||||
"condition": "q.target_fainted == false",
|
||||
"pause": 1.25
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -141,7 +141,8 @@
|
||||
},
|
||||
{
|
||||
"type": "pause",
|
||||
"pause": 0.5
|
||||
"condition": "q.target_fainted == false",
|
||||
"pause": 1.25
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:snowball",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:sweet_berries",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 2,
|
||||
"max": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:never_melt_ice",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:occa_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/abomasnow"
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:ender_pearl",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:twisted_spoon",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.025
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:kasib_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.025
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/abra"
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:razor_claw",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:life_orb",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:roseli_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/absol"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:shed_shell",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/accelgor"
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:iron_nugget",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:iron_sword",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:dusk_stone",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:ability_shield",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/aegislash"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:bone",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:rocky_helmet",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/aerodactyl"
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:raw_iron",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:hard_stone",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:chople_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/aggron"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:nanab_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.025
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:tag",
|
||||
"name": "cobblemon:drops/apricorn_sprouts",
|
||||
"expand": true,
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.025
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/aipom"
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:ender_pearl",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:twisted_spoon",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:kasib_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/alakazam"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:sugar",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:cake",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/alcremie"
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:cod",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:any_of",
|
||||
"terms": [
|
||||
{
|
||||
"condition": "minecraft:entity_properties",
|
||||
"entity": "this",
|
||||
"predicate": {
|
||||
"flags": {
|
||||
"is_on_fire": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"condition": "minecraft:entity_properties",
|
||||
"entity": "direct_attacker",
|
||||
"predicate": {
|
||||
"equipment": {
|
||||
"mainhand": {
|
||||
"predicates": {
|
||||
"minecraft:enchantments": [
|
||||
{
|
||||
"enchantments": "#minecraft:smelts_loot"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"function": "minecraft:furnace_smelt"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:bone_meal",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:deep_sea_scale",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/alomomola"
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:white_wool",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:chicken",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:any_of",
|
||||
"terms": [
|
||||
{
|
||||
"condition": "minecraft:entity_properties",
|
||||
"entity": "this",
|
||||
"predicate": {
|
||||
"flags": {
|
||||
"is_on_fire": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"condition": "minecraft:entity_properties",
|
||||
"entity": "direct_attacker",
|
||||
"predicate": {
|
||||
"equipment": {
|
||||
"mainhand": {
|
||||
"predicates": {
|
||||
"minecraft:enchantments": [
|
||||
{
|
||||
"enchantments": "#minecraft:smelts_loot"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"function": "minecraft:furnace_smelt"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:yache_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/altaria"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:never_melt_ice",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/amaura"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:nanab_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:tag",
|
||||
"name": "cobblemon:drops/apricorn_sprouts",
|
||||
"expand": true,
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/ambipom"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:red_mushroom",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 0,
|
||||
"max": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:cheri_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/amoonguss"
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "minecraft:mutton",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 2,
|
||||
"max": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:any_of",
|
||||
"terms": [
|
||||
{
|
||||
"condition": "minecraft:entity_properties",
|
||||
"entity": "this",
|
||||
"predicate": {
|
||||
"flags": {
|
||||
"is_on_fire": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"condition": "minecraft:entity_properties",
|
||||
"entity": "direct_attacker",
|
||||
"predicate": {
|
||||
"equipment": {
|
||||
"mainhand": {
|
||||
"predicates": {
|
||||
"minecraft:enchantments": [
|
||||
{
|
||||
"enchantments": "#minecraft:smelts_loot"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"function": "minecraft:furnace_smelt"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:cheri_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/ampharos"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:muscle_band",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:payapa_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/annihilape"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:quick_claw",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/anorith"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"type": "minecraft:generic",
|
||||
"pools": [
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:sweet_apple",
|
||||
"functions": [
|
||||
{
|
||||
"function": "set_count",
|
||||
"count": {
|
||||
"min": 1,
|
||||
"max": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"rolls": 1,
|
||||
"entries": [
|
||||
{
|
||||
"type": "minecraft:item",
|
||||
"name": "cobblemon:yache_berry",
|
||||
"functions": [
|
||||
{
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max": 1,
|
||||
"min": 0
|
||||
},
|
||||
"enchantment": "minecraft:looting",
|
||||
"function": "minecraft:enchanted_count_increase"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"condition": "minecraft:random_chance",
|
||||
"chance": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"random_sequence": "cobblemon:pokemon/appletun"
|
||||
}
|
||||