mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-11-02 22:38:54 -05:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e18a02a7d | ||
|
|
3d7757cad5 | ||
|
|
48e9a82085 | ||
|
|
4caa736e0b | ||
|
|
c3e9051918 | ||
|
|
0b38e063d2 |
No files matched your search
+27
-12
@@ -1,31 +1,46 @@
|
||||
# Changelog
|
||||
## [1.8.x (MONTH xth, 2026)](#1-8-x)
|
||||
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
|
||||
|
||||
### Additions
|
||||
|
||||
### New rideable Pokémon
|
||||
|
||||
### Pokémon Added
|
||||
#### Generation 4
|
||||
|
||||
#### Generation 5
|
||||
|
||||
#### Generation 6
|
||||
|
||||
#### Generation 7
|
||||
|
||||
#### Generation 8
|
||||
|
||||
#### Generation 9
|
||||
|
||||
### 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
|
||||
- Fixed datapacks with item interaction evolutions that don't specify an item crashing the world on load.
|
||||
- Fixed the Pokédex evolution tree crashing when a datapack's dex entry is missing an evolution's form.
|
||||
- Fixed the Pokédex evolution tree crashing with a stack overflow on evolutions that loop back on themselves.
|
||||
- Fixed the Pokédex evolution tree crashing when looking at a dex that is not part of the "National Dex" aggregate.
|
||||
- Fixed evolution trees not building properly when a pokemon has several pokedex entries for different regions.
|
||||
- Fixed form-changing evolutions sometimes resolving to the source form in the Pokédex evolution tree.
|
||||
- Broken datapack evolutions are now logged and skipped in the Pokédex evolution tree instead of crashing.
|
||||
- Fixed friendship booster items not being recognised as such
|
||||
- Fixed conflicts with other mods that would make use of recipe books in certain ways (Farmer's Delight Cooking Pot)
|
||||
- Fixed Pokémon battles stalling when a sidemod cancels the `THROWN_POKEBALL_HIT` hit event
|
||||
- Fixed choice feature tab completion in `spawnpokemon/pokegive/pokeedit` commands
|
||||
|
||||
### Developer
|
||||
- Bumped NeoForge to `21.1.195` to get access to MixinExtras `0.5` and the expressions annotation
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `q.target_fainted` function to move action effects that checks if any opponents fainted from the move.
|
||||
|
||||
### Particles
|
||||
|
||||
#### New Move Animations
|
||||
- Fixed `q.pokemon.nature` returning class instance instead of proper string value (`cobblemon:adamant`)
|
||||
- Fixed `q.entity.is_looking_at` result being always `1` for whatever entity passed as target
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Updated translations for:
|
||||
@@ -1,107 +0,0 @@
|
||||
# Changelog
|
||||
## [1.8.3 (October 25th, 2026)](#1-8-3)
|
||||
|
||||
### Additions
|
||||
- Added compatability with AppleSkin.
|
||||
|
||||
### New Rideable Pokémon
|
||||
- Vikavolt
|
||||
- Fearow
|
||||
|
||||
### Particles
|
||||
## New Move Animations
|
||||
- Water Shuriken
|
||||
|
||||
### Cries added/updated for the following Pokémon
|
||||
- Hoppip
|
||||
- Skiploom
|
||||
- Jumpluff
|
||||
- Houndour
|
||||
- Houndoom
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Surskit
|
||||
- Masquerain
|
||||
- Torkoal
|
||||
- Starly
|
||||
- Staravia
|
||||
- Staraptor
|
||||
- Combee
|
||||
- Vespiquen
|
||||
- Dusknoir
|
||||
- Helioptile
|
||||
- Heliolisk
|
||||
- Grubbin
|
||||
- Charjabug
|
||||
- Vikavolt
|
||||
- Applin (updated)
|
||||
- Flapple (updated)
|
||||
- Appletun
|
||||
- Runerigus
|
||||
- Dipplin
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Vikavolt
|
||||
- Magby
|
||||
- Magmar
|
||||
- Ninetales (+ Alolan)
|
||||
- Spearow
|
||||
- Fearow
|
||||
- Squawkabilly
|
||||
- Mudkip
|
||||
- Marshtomp
|
||||
- Swampert
|
||||
|
||||
### Changes
|
||||
- Changed the Misc icon in the Campfire Pot Recipe Book to Health Candy.
|
||||
- Moved EXP Candies from the Medicine recipe book category to the Misc category.
|
||||
- Removed Dynamic Lights data from the Magmarizer item for consistency with the block.
|
||||
- Added `doPokemonPredation` game rule, which controls aggressiveness of Pokémon with the `hate_entity` brain task defined.
|
||||
- Added `predation_chance` server config value, which controls the possibility of attacking the hated entity found in `hate_entity` brain tasks.
|
||||
- Prehistoric Eroded Pillar generates slightly more frequently.
|
||||
- Togekiss was changed from a jet-style mount to bird-style.
|
||||
|
||||
### Fixes
|
||||
- Fixed datapacks with item interaction evolutions that don't specify an item crashing the world on load.
|
||||
- Fixed the Pokédex evolution tree crashing when a datapack's dex entry is missing an evolution's form.
|
||||
- Fixed the Pokédex evolution tree crashing with a stack overflow on evolutions that loop back on themselves.
|
||||
- Fixed the Pokédex evolution tree crashing when looking at a dex that is not part of the "National Dex" aggregate.
|
||||
- Fixed some display names in the Pokédex evolution tree having unintended font colours.
|
||||
- Fixed evolution trees not building properly when a Pokémon has several Pokédex entries for different regions.
|
||||
- Fixed form-changing evolutions sometimes resolving to the source form in the Pokédex evolution tree.
|
||||
- Broken datapack evolutions are now logged and skipped in the Pokédex evolution tree instead of crashing.
|
||||
- Fixed the `alpha` property having no tab completion in commands such as `/pokespawn`, `/pokegive` and `/pokeedit`, and added the missing `dmax_level`, `gmax_factor` and `tradable` aliases to tab completion.
|
||||
- Fixed friendship booster items not being recognised as such.
|
||||
- Fixed conflicts with other mods that would make use of recipe books in certain ways (Farmer's Delight Cooking Pot).
|
||||
- Fixed Pokémon battles stalling when a sidemod cancels the `THROWN_POKEBALL_HIT` hit event.
|
||||
- Fixed choice feature tab completion in `spawnpokemon/pokegive/pokeedit` commands.
|
||||
- Fixed MoLang that's targeting players intermittedly failing when they have died, requiring them to relog.
|
||||
- Fixed a NeoForge bug where none of the Cobblemon plants/berries could be placed into pots.
|
||||
- Fixed party overlay not hiding when the player opens the Pokédex scanner.
|
||||
- Fixed `hate_entity` task focusing on its own entity, therefore enabling deadly warfare between Zangoose and Seviper.
|
||||
- Prehistoric Frozen Pond, Prehistoric Frozen Spike, and Prehistoric Lush Den now actually generate (oops).
|
||||
- Fixed Frogadier floating in its `battle_idle` animation.
|
||||
- Fixed absence or incorrect mergers in spawning conditions, causing the max height/depth of conditions to use the min value instead.
|
||||
- Added stricter riding checks on server side to prevent possible packet forgery when a player tries to ride Pokémon.
|
||||
- Fixed Pokémon cries and animation sounds played from GUIs, such as the starter selection screen and the Pokédex, being tied to the Master volume slider instead of Friendly Creatures.
|
||||
- Fixed Archeops T-posing during its `air_cry` animation.
|
||||
|
||||
### Developer
|
||||
- Bumped NeoForge to `21.1.195` to get access to MixinExtras `0.5` and the expressions annotation.
|
||||
- New Platform Event `PLAYER_RESPAWN` which is fired when a player respawns (either through death or returning from the end).
|
||||
- Platform Events `SERVER_PLAYER_TICK_PRE` and `SERVER_PLAYER_TICK_POST` no longer fire when the player is dead.
|
||||
- It is now tied much closer to the actual entity rather than the player or their connection.
|
||||
|
||||
### Molang & Datapacks
|
||||
- Fixed `q.pokemon.nature` returning class instance instead of proper string value (`cobblemon:adamant`).
|
||||
- Fixed `q.entity.is_looking_at` result being always `1` for whatever entity passed as the target parameter.
|
||||
- Added hate chance optional parameter to `hate_entity` task. `1.0` will make an entity always attack the target.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Russian
|
||||
- Japanese
|
||||
- Brazilian Portuguese
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
|
||||
public interface GameRendererDuck {
|
||||
/** Vertical field of view in degrees this frame is drawn with, the same one vanilla builds its projection from */
|
||||
double cobblemon$getFov(Camera camera, float partialTick);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -69,7 +69,7 @@ public abstract class ClientRecipeBookMixin {
|
||||
value = "INVOKE",
|
||||
target = "Lcom/google/common/collect/ImmutableMap;copyOf(Ljava/util/Map;)Lcom/google/common/collect/ImmutableMap;"
|
||||
))
|
||||
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci, @Local(ordinal = 1) Map<RecipeBookCategories, List<RecipeCollection>> map2) {
|
||||
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci, @Local(name = "map2") Map<RecipeBookCategories, List<RecipeCollection>> map2) {
|
||||
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,13 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
|
||||
import com.cobblemon.mod.common.duck.GameRendererDuck;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -28,17 +25,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public abstract class GameRendererMixin implements GameRendererDuck {
|
||||
public class GameRendererMixin {
|
||||
@Unique
|
||||
long lastTime = -1;
|
||||
|
||||
@Shadow protected abstract double getFov(Camera camera, float partialTick, boolean useFovSetting);
|
||||
|
||||
@Override
|
||||
public double cobblemon$getFov(Camera camera, float partialTick) {
|
||||
return getFov(camera, partialTick, true);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(value = "TAIL")
|
||||
|
||||
+12
-13
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
|
||||
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.client.model.HumanoidModel;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
@@ -18,7 +19,6 @@ import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.phys.Vec2;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -71,18 +71,17 @@ public class HumanoidModelMixin {
|
||||
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
|
||||
);
|
||||
|
||||
// Vanilla's head angles are world yaw but the body sits on a seat that can be on its side, so measure the real look against the seat
|
||||
if (shouldRotatePlayerHead) {
|
||||
Vec2 riderHead = MountedPlayerRenderer.riderHeadAngles(pokemonEntity, entity, Mth.clamp(ageInTicks - entity.tickCount, 0f, 1f));
|
||||
if (riderHead != null) {
|
||||
netHeadYaw = riderHead.x;
|
||||
headPitch = riderHead.y;
|
||||
if (this.head != null) {
|
||||
this.head.yRot = netHeadYaw * Mth.DEG_TO_RAD;
|
||||
this.head.xRot = headPitch * Mth.DEG_TO_RAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the player is on a rollable ride then use the ride local rotations to rotate the head
|
||||
// This is to allow the player head to face the correct direction when "freelooking"
|
||||
// TODO: find a way for this to work on built jars without borking head rotations.
|
||||
// if (entity instanceof RidePassenger playerRotater &&
|
||||
// vehicle instanceof OrientationControllable controller &&
|
||||
// controller.getOrientationController().isActive()) {
|
||||
// this.head.xRot = playerRotater.cobblemon$getRideXRot();
|
||||
// headPitch = playerRotater.cobblemon$getRideXRot();
|
||||
// this.head.yRot = playerRotater.cobblemon$getRideYRot();
|
||||
// netHeadYaw = playerRotater.cobblemon$getRideYRot();
|
||||
// }
|
||||
|
||||
if (!shouldRotatePlayerHead) {
|
||||
netHeadYaw = 0f;
|
||||
|
||||
-11
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+117
-79
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -20,14 +20,14 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(RecipeBookComponent.class)
|
||||
public class RecipeBookComponentMixin {
|
||||
@Definition(id = "recipeBookCategories", local = @Local(type = RecipeBookCategories.class))
|
||||
@Definition(id = "recipeBookCategories", local = @Local(name = "recipeBookCategories"))
|
||||
@Definition(id = "FURNACE_SEARCH", field = "Lnet/minecraft/client/RecipeBookCategories;FURNACE_SEARCH:Lnet/minecraft/client/RecipeBookCategories;")
|
||||
@Expression("recipeBookCategories != FURNACE_SEARCH")
|
||||
@ModifyExpressionValue(
|
||||
method = "updateTabs",
|
||||
at = @At(value = "MIXINEXTRAS:EXPRESSION")
|
||||
)
|
||||
private boolean notCookingPotSearch(boolean original, @Local RecipeBookCategories recipeBookCategories) {
|
||||
private boolean notCookingPotSearch(boolean original, @Local(name = "recipeBookCategories") RecipeBookCategories recipeBookCategories) {
|
||||
return original && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory();
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ScreenEffectRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(ScreenEffectRenderer.class)
|
||||
public class ScreenEffectRendererMixin {
|
||||
/**
|
||||
* The in block overlay tests the rider's own eye, which sits straight up from the seat even with the ride on a wall
|
||||
* or ceiling. The camera is somewhere else entirely, so only let the overlay through when the camera itself is buried.
|
||||
* Wraps the noPhysics check in front of it since NeoForge swaps out the method that finds the block
|
||||
*/
|
||||
@WrapOperation(method = "renderScreenEffect", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/player/Player;noPhysics:Z"))
|
||||
private static boolean cobblemon$overlayFromRideCamera(Player player, Operation<Boolean> original) {
|
||||
if (!(player.getVehicle() instanceof PokemonEntity)) return original.call(player);
|
||||
BlockPos pos = BlockPos.containing(Minecraft.getInstance().gameRenderer.getMainCamera().getPosition());
|
||||
BlockState state = player.level().getBlockState(pos);
|
||||
boolean cameraBuried = state.getRenderShape() != RenderShape.INVISIBLE && state.isViewBlocking(player.level(), pos);
|
||||
return original.call(player) || !cameraBuried;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -260,9 +260,6 @@ object Cobblemon {
|
||||
RequestManager.onLogoff(it.player)
|
||||
serverPlayerStructs.remove(it.player.uuid)
|
||||
}
|
||||
PlatformEvents.PLAYER_RESPAWN.subscribe {
|
||||
serverPlayerStructs.remove(it.player.uuid)
|
||||
}
|
||||
PlatformEvents.PLAYER_DEATH.subscribe {
|
||||
PCLinkManager.removeLink(it.player.uuid)
|
||||
battleRegistry.getBattleByParticipatingPlayer(it.player)?.stop()
|
||||
|
||||
@@ -845,35 +845,54 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val PEP_UP_FLOWER = this.create("pep_up_flower", FlowerBlock(MobEffects.LEVITATION, 10F, BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak()))
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUT_BUSH = this.create("galarica_nut_bush", NutBushBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().sound(SoundType.SWEET_BERRY_BUSH).pushReaction(PushReaction.DESTROY)))
|
||||
|
||||
@JvmField
|
||||
val POTTED_RED_APRICORN_SAPLING = this.create("potted_red_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(RED_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_YELLOW_APRICORN_SAPLING = this.create("potted_yellow_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(YELLOW_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_GREEN_APRICORN_SAPLING = this.create("potted_green_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(GREEN_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_BLUE_APRICORN_SAPLING = this.create("potted_blue_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLUE_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_PINK_APRICORN_SAPLING = this.create("potted_pink_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(PINK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_BLACK_APRICORN_SAPLING = this.create("potted_black_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLACK_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_WHITE_APRICORN_SAPLING = this.create("potted_white_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(WHITE_APRICORN_SAPLING))
|
||||
@JvmField
|
||||
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val RED_MINT_LEAF_LITTER = this.mintLitter("red_mint_leaf_litter", MapColor.COLOR_RED)
|
||||
@JvmField
|
||||
val POTTED_RED_MINT = this.flowerPot("potted_red_mint", RED_MINT_LEAF_LITTER)
|
||||
val POTTED_RED_MINT = this.pottedPlant("potted_red_mint", RED_MINT_LEAF_LITTER)
|
||||
@JvmField
|
||||
val BLUE_MINT_LEAF_LITTER = this.mintLitter("blue_mint_leaf_litter", MapColor.COLOR_BLUE)
|
||||
@JvmField
|
||||
val POTTED_BLUE_MINT = this.flowerPot("potted_blue_mint", BLUE_MINT_LEAF_LITTER)
|
||||
val POTTED_BLUE_MINT = this.pottedPlant("potted_blue_mint", BLUE_MINT_LEAF_LITTER)
|
||||
@JvmField
|
||||
val CYAN_MINT_LEAF_LITTER = this.mintLitter("cyan_mint_leaf_litter", MapColor.COLOR_CYAN)
|
||||
@JvmField
|
||||
val POTTED_CYAN_MINT = this.flowerPot("potted_cyan_mint", CYAN_MINT_LEAF_LITTER)
|
||||
val POTTED_CYAN_MINT = this.pottedPlant("potted_cyan_mint", CYAN_MINT_LEAF_LITTER)
|
||||
@JvmField
|
||||
val PINK_MINT_LEAF_LITTER = this.mintLitter("pink_mint_leaf_litter", MapColor.COLOR_PINK)
|
||||
@JvmField
|
||||
val POTTED_PINK_MINT = this.flowerPot("potted_pink_mint", PINK_MINT_LEAF_LITTER)
|
||||
val POTTED_PINK_MINT = this.pottedPlant("potted_pink_mint", PINK_MINT_LEAF_LITTER)
|
||||
@JvmField
|
||||
val GREEN_MINT_LEAF_LITTER = this.mintLitter("green_mint_leaf_litter", MapColor.COLOR_GREEN)
|
||||
@JvmField
|
||||
val POTTED_GREEN_MINT = this.flowerPot("potted_green_mint", GREEN_MINT_LEAF_LITTER)
|
||||
val POTTED_GREEN_MINT = this.pottedPlant("potted_green_mint", GREEN_MINT_LEAF_LITTER)
|
||||
@JvmField
|
||||
val WHITE_MINT_LEAF_LITTER = this.mintLitter("white_mint_leaf_litter", MapColor.SNOW)
|
||||
@JvmField
|
||||
val POTTED_WHITE_MINT = this.flowerPot("potted_white_mint", WHITE_MINT_LEAF_LITTER)
|
||||
val POTTED_WHITE_MINT = this.pottedPlant("potted_white_mint", WHITE_MINT_LEAF_LITTER)
|
||||
|
||||
@JvmField
|
||||
val LOBAILA = this.create("lobaila", LobailaFlowerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).ignitedByLava().pushReaction(PushReaction.DESTROY)))
|
||||
@@ -1085,163 +1104,145 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(BlockBehaviour.Properties.of()))
|
||||
|
||||
@JvmField
|
||||
val POTTED_AGUAV_BERRY = this.flowerPot("potted_aguav_berry", AGUAV_BERRY)
|
||||
val POTTED_AGUAV_BERRY = this.create("potted_aguav_berry", BlocksInvoker.createFlowerPotBlock(AGUAV_BERRY))
|
||||
@JvmField
|
||||
val POTTED_APICOT_BERRY = this.flowerPot("potted_apicot_berry", APICOT_BERRY)
|
||||
val POTTED_APICOT_BERRY = this.create("potted_apicot_berry", BlocksInvoker.createFlowerPotBlock(APICOT_BERRY))
|
||||
@JvmField
|
||||
val POTTED_ASPEAR_BERRY = this.flowerPot("potted_aspear_berry", ASPEAR_BERRY)
|
||||
val POTTED_ASPEAR_BERRY = this.create("potted_aspear_berry", BlocksInvoker.createFlowerPotBlock(ASPEAR_BERRY))
|
||||
@JvmField
|
||||
val POTTED_BABIRI_BERRY = this.flowerPot("potted_babiri_berry", BABIRI_BERRY)
|
||||
val POTTED_BABIRI_BERRY = this.create("potted_babiri_berry", BlocksInvoker.createFlowerPotBlock(BABIRI_BERRY))
|
||||
@JvmField
|
||||
val POTTED_BELUE_BERRY = this.flowerPot("potted_belue_berry", BELUE_BERRY)
|
||||
val POTTED_BELUE_BERRY = this.create("potted_belue_berry", BlocksInvoker.createFlowerPotBlock(BELUE_BERRY))
|
||||
@JvmField
|
||||
val POTTED_BLUK_BERRY = this.flowerPot("potted_bluk_berry", BLUK_BERRY)
|
||||
val POTTED_BLUK_BERRY = this.create("potted_bluk_berry", BlocksInvoker.createFlowerPotBlock(BLUK_BERRY))
|
||||
@JvmField
|
||||
val POTTED_CHARTI_BERRY = this.flowerPot("potted_charti_berry", CHARTI_BERRY)
|
||||
val POTTED_CHARTI_BERRY = this.create("potted_charti_berry", BlocksInvoker.createFlowerPotBlock(CHARTI_BERRY))
|
||||
@JvmField
|
||||
val POTTED_CHERI_BERRY = this.flowerPot("potted_cheri_berry", CHERI_BERRY)
|
||||
val POTTED_CHERI_BERRY = this.create("potted_cheri_berry", BlocksInvoker.createFlowerPotBlock(CHERI_BERRY))
|
||||
@JvmField
|
||||
val POTTED_CHESTO_BERRY = this.flowerPot("potted_chesto_berry", CHESTO_BERRY)
|
||||
val POTTED_CHESTO_BERRY = this.create("potted_chesto_berry", BlocksInvoker.createFlowerPotBlock(CHESTO_BERRY))
|
||||
@JvmField
|
||||
val POTTED_CHILAN_BERRY = this.flowerPot("potted_chilan_berry", CHILAN_BERRY)
|
||||
val POTTED_CHILAN_BERRY = this.create("potted_chilan_berry", BlocksInvoker.createFlowerPotBlock(CHILAN_BERRY))
|
||||
@JvmField
|
||||
val POTTED_CHOPLE_BERRY = this.flowerPot("potted_chople_berry", CHOPLE_BERRY)
|
||||
val POTTED_CHOPLE_BERRY = this.create("potted_chople_berry", BlocksInvoker.createFlowerPotBlock(CHOPLE_BERRY))
|
||||
@JvmField
|
||||
val POTTED_COBA_BERRY = this.flowerPot("potted_coba_berry", COBA_BERRY)
|
||||
val POTTED_COBA_BERRY = this.create("potted_coba_berry", BlocksInvoker.createFlowerPotBlock(COBA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_COLBUR_BERRY = this.flowerPot("potted_colbur_berry", COLBUR_BERRY)
|
||||
val POTTED_COLBUR_BERRY = this.create("potted_colbur_berry", BlocksInvoker.createFlowerPotBlock(COLBUR_BERRY))
|
||||
@JvmField
|
||||
val POTTED_CORNN_BERRY = this.flowerPot("potted_cornn_berry", CORNN_BERRY)
|
||||
val POTTED_CORNN_BERRY = this.create("potted_cornn_berry", BlocksInvoker.createFlowerPotBlock(CORNN_BERRY))
|
||||
@JvmField
|
||||
val POTTED_CUSTAP_BERRY = this.flowerPot("potted_custap_berry", CUSTAP_BERRY)
|
||||
val POTTED_CUSTAP_BERRY = this.create("potted_custap_berry", BlocksInvoker.createFlowerPotBlock(CUSTAP_BERRY))
|
||||
@JvmField
|
||||
val POTTED_DURIN_BERRY = this.flowerPot("potted_durin_berry", DURIN_BERRY)
|
||||
val POTTED_DURIN_BERRY = this.create("potted_durin_berry", BlocksInvoker.createFlowerPotBlock(DURIN_BERRY))
|
||||
@JvmField
|
||||
val POTTED_EGGANT_BERRY = this.flowerPot("potted_eggant_berry", EGGANT_BERRY)
|
||||
val POTTED_EGGANT_BERRY = this.create("potted_eggant_berry", BlocksInvoker.createFlowerPotBlock(EGGANT_BERRY))
|
||||
@JvmField
|
||||
val POTTED_ENIGMA_BERRY = this.flowerPot("potted_enigma_berry", ENIGMA_BERRY)
|
||||
val POTTED_ENIGMA_BERRY = this.create("potted_enigma_berry", BlocksInvoker.createFlowerPotBlock(ENIGMA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_FIGY_BERRY = this.flowerPot("potted_figy_berry", FIGY_BERRY)
|
||||
val POTTED_FIGY_BERRY = this.create("potted_figy_berry", BlocksInvoker.createFlowerPotBlock(FIGY_BERRY))
|
||||
@JvmField
|
||||
val POTTED_GANLON_BERRY = this.flowerPot("potted_ganlon_berry", GANLON_BERRY)
|
||||
val POTTED_GANLON_BERRY = this.create("potted_ganlon_berry", BlocksInvoker.createFlowerPotBlock(GANLON_BERRY))
|
||||
@JvmField
|
||||
val POTTED_GREPA_BERRY = this.flowerPot("potted_grepa_berry", GREPA_BERRY)
|
||||
val POTTED_GREPA_BERRY = this.create("potted_grepa_berry", BlocksInvoker.createFlowerPotBlock(GREPA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_HABAN_BERRY = this.flowerPot("potted_haban_berry", HABAN_BERRY)
|
||||
val POTTED_HABAN_BERRY = this.create("potted_haban_berry", BlocksInvoker.createFlowerPotBlock(HABAN_BERRY))
|
||||
@JvmField
|
||||
val POTTED_HONDEW_BERRY = this.flowerPot("potted_hondew_berry", HONDEW_BERRY)
|
||||
val POTTED_HONDEW_BERRY = this.create("potted_hondew_berry", BlocksInvoker.createFlowerPotBlock(HONDEW_BERRY))
|
||||
@JvmField
|
||||
val POTTED_HOPO_BERRY = this.flowerPot("potted_hopo_berry", HOPO_BERRY)
|
||||
val POTTED_HOPO_BERRY = this.create("potted_hopo_berry", BlocksInvoker.createFlowerPotBlock(HOPO_BERRY))
|
||||
@JvmField
|
||||
val POTTED_IAPAPA_BERRY = this.flowerPot("potted_iapapa_berry", IAPAPA_BERRY)
|
||||
val POTTED_IAPAPA_BERRY = this.create("potted_iapapa_berry", BlocksInvoker.createFlowerPotBlock(IAPAPA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_JABOCA_BERRY = this.flowerPot("potted_jaboca_berry", JABOCA_BERRY)
|
||||
val POTTED_JABOCA_BERRY = this.create("potted_jaboca_berry", BlocksInvoker.createFlowerPotBlock(JABOCA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_KASIB_BERRY = this.flowerPot("potted_kasib_berry", KASIB_BERRY)
|
||||
val POTTED_KASIB_BERRY = this.create("potted_kasib_berry", BlocksInvoker.createFlowerPotBlock(KASIB_BERRY))
|
||||
@JvmField
|
||||
val POTTED_KEBIA_BERRY = this.flowerPot("potted_kebia_berry", KEBIA_BERRY)
|
||||
val POTTED_KEBIA_BERRY = this.create("potted_kebia_berry", BlocksInvoker.createFlowerPotBlock(KEBIA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_KEE_BERRY = this.flowerPot("potted_kee_berry", KEE_BERRY)
|
||||
val POTTED_KEE_BERRY = this.create("potted_kee_berry", BlocksInvoker.createFlowerPotBlock(KEE_BERRY))
|
||||
@JvmField
|
||||
val POTTED_KELPSY_BERRY = this.flowerPot("potted_kelpsy_berry", KELPSY_BERRY)
|
||||
val POTTED_KELPSY_BERRY = this.create("potted_kelpsy_berry", BlocksInvoker.createFlowerPotBlock(KELPSY_BERRY))
|
||||
@JvmField
|
||||
val POTTED_LANSAT_BERRY = this.flowerPot("potted_lansat_berry", LANSAT_BERRY)
|
||||
val POTTED_LANSAT_BERRY = this.create("potted_lansat_berry", BlocksInvoker.createFlowerPotBlock(LANSAT_BERRY))
|
||||
@JvmField
|
||||
val POTTED_LEPPA_BERRY = this.flowerPot("potted_leppa_berry", LEPPA_BERRY)
|
||||
val POTTED_LEPPA_BERRY = this.create("potted_leppa_berry", BlocksInvoker.createFlowerPotBlock(LEPPA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_LIECHI_BERRY = this.flowerPot("potted_liechi_berry", LIECHI_BERRY)
|
||||
val POTTED_LIECHI_BERRY = this.create("potted_liechi_berry", BlocksInvoker.createFlowerPotBlock(LIECHI_BERRY))
|
||||
@JvmField
|
||||
val POTTED_LUM_BERRY = this.flowerPot("potted_lum_berry", LUM_BERRY)
|
||||
val POTTED_LUM_BERRY = this.create("potted_lum_berry", BlocksInvoker.createFlowerPotBlock(LUM_BERRY))
|
||||
@JvmField
|
||||
val POTTED_MAGO_BERRY = this.flowerPot("potted_mago_berry", MAGO_BERRY)
|
||||
val POTTED_MAGO_BERRY = this.create("potted_mago_berry", BlocksInvoker.createFlowerPotBlock(MAGO_BERRY))
|
||||
@JvmField
|
||||
val POTTED_MAGOST_BERRY = this.flowerPot("potted_magost_berry", MAGOST_BERRY)
|
||||
val POTTED_MAGOST_BERRY = this.create("potted_magost_berry", BlocksInvoker.createFlowerPotBlock(MAGOST_BERRY))
|
||||
@JvmField
|
||||
val POTTED_MARANGA_BERRY = this.flowerPot("potted_maranga_berry", MARANGA_BERRY)
|
||||
val POTTED_MARANGA_BERRY = this.create("potted_maranga_berry", BlocksInvoker.createFlowerPotBlock(MARANGA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_MICLE_BERRY = this.flowerPot("potted_micle_berry", MICLE_BERRY)
|
||||
val POTTED_MICLE_BERRY = this.create("potted_micle_berry", BlocksInvoker.createFlowerPotBlock(MICLE_BERRY))
|
||||
@JvmField
|
||||
val POTTED_NANAB_BERRY = this.flowerPot("potted_nanab_berry", NANAB_BERRY)
|
||||
val POTTED_NANAB_BERRY = this.create("potted_nanab_berry", BlocksInvoker.createFlowerPotBlock(NANAB_BERRY))
|
||||
@JvmField
|
||||
val POTTED_NOMEL_BERRY = this.flowerPot("potted_nomel_berry", NOMEL_BERRY)
|
||||
val POTTED_NOMEL_BERRY = this.create("potted_nomel_berry", BlocksInvoker.createFlowerPotBlock(NOMEL_BERRY))
|
||||
@JvmField
|
||||
val POTTED_OCCA_BERRY = this.flowerPot("potted_occa_berry", OCCA_BERRY)
|
||||
val POTTED_OCCA_BERRY = this.create("potted_occa_berry", BlocksInvoker.createFlowerPotBlock(OCCA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_ORAN_BERRY = this.flowerPot("potted_oran_berry", ORAN_BERRY)
|
||||
val POTTED_ORAN_BERRY = this.create("potted_oran_berry", BlocksInvoker.createFlowerPotBlock(ORAN_BERRY))
|
||||
@JvmField
|
||||
val POTTED_PAMTRE_BERRY = this.flowerPot("potted_pamtre_berry", PAMTRE_BERRY)
|
||||
val POTTED_PAMTRE_BERRY = this.create("potted_pamtre_berry", BlocksInvoker.createFlowerPotBlock(PAMTRE_BERRY))
|
||||
@JvmField
|
||||
val POTTED_PASSHO_BERRY = this.flowerPot("potted_passho_berry", PASSHO_BERRY)
|
||||
val POTTED_PASSHO_BERRY = this.create("potted_passho_berry", BlocksInvoker.createFlowerPotBlock(PASSHO_BERRY))
|
||||
@JvmField
|
||||
val POTTED_PAYAPA_BERRY = this.flowerPot("potted_payapa_berry", PAYAPA_BERRY)
|
||||
val POTTED_PAYAPA_BERRY = this.create("potted_payapa_berry", BlocksInvoker.createFlowerPotBlock(PAYAPA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_PECHA_BERRY = this.flowerPot("potted_pecha_berry", PECHA_BERRY)
|
||||
val POTTED_PECHA_BERRY = this.create("potted_pecha_berry", BlocksInvoker.createFlowerPotBlock(PECHA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_PERSIM_BERRY = this.flowerPot("potted_persim_berry", PERSIM_BERRY)
|
||||
val POTTED_PERSIM_BERRY = this.create("potted_persim_berry", BlocksInvoker.createFlowerPotBlock(PERSIM_BERRY))
|
||||
@JvmField
|
||||
val POTTED_PETAYA_BERRY = this.flowerPot("potted_petaya_berry", PETAYA_BERRY)
|
||||
val POTTED_PETAYA_BERRY = this.create("potted_petaya_berry", BlocksInvoker.createFlowerPotBlock(PETAYA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_PINAP_BERRY = this.flowerPot("potted_pinap_berry", PINAP_BERRY)
|
||||
val POTTED_PINAP_BERRY = this.create("potted_pinap_berry", BlocksInvoker.createFlowerPotBlock(PINAP_BERRY))
|
||||
@JvmField
|
||||
val POTTED_POMEG_BERRY = this.flowerPot("potted_pomeg_berry", POMEG_BERRY)
|
||||
val POTTED_POMEG_BERRY = this.create("potted_pomeg_berry", BlocksInvoker.createFlowerPotBlock(POMEG_BERRY))
|
||||
@JvmField
|
||||
val POTTED_QUALOT_BERRY = this.flowerPot("potted_qualot_berry", QUALOT_BERRY)
|
||||
val POTTED_QUALOT_BERRY = this.create("potted_qualot_berry", BlocksInvoker.createFlowerPotBlock(QUALOT_BERRY))
|
||||
@JvmField
|
||||
val POTTED_RABUTA_BERRY = this.flowerPot("potted_rabuta_berry", RABUTA_BERRY)
|
||||
val POTTED_RABUTA_BERRY = this.create("potted_rabuta_berry", BlocksInvoker.createFlowerPotBlock(RABUTA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_RAWST_BERRY = this.flowerPot("potted_rawst_berry", RAWST_BERRY)
|
||||
val POTTED_RAWST_BERRY = this.create("potted_rawst_berry", BlocksInvoker.createFlowerPotBlock(RAWST_BERRY))
|
||||
@JvmField
|
||||
val POTTED_RAZZ_BERRY = this.flowerPot("potted_razz_berry", RAZZ_BERRY)
|
||||
val POTTED_RAZZ_BERRY = this.create("potted_razz_berry", BlocksInvoker.createFlowerPotBlock(RAZZ_BERRY))
|
||||
@JvmField
|
||||
val POTTED_RINDO_BERRY = this.flowerPot("potted_rindo_berry", RINDO_BERRY)
|
||||
val POTTED_RINDO_BERRY = this.create("potted_rindo_berry", BlocksInvoker.createFlowerPotBlock(RINDO_BERRY))
|
||||
@JvmField
|
||||
val POTTED_ROSELI_BERRY = this.flowerPot("potted_roseli_berry", ROSELI_BERRY)
|
||||
val POTTED_ROSELI_BERRY = this.create("potted_roseli_berry", BlocksInvoker.createFlowerPotBlock(ROSELI_BERRY))
|
||||
@JvmField
|
||||
val POTTED_ROWAP_BERRY = this.flowerPot("potted_rowap_berry", ROWAP_BERRY)
|
||||
val POTTED_ROWAP_BERRY = this.create("potted_rowap_berry", BlocksInvoker.createFlowerPotBlock(ROWAP_BERRY))
|
||||
@JvmField
|
||||
val POTTED_SALAC_BERRY = this.flowerPot("potted_salac_berry", SALAC_BERRY)
|
||||
val POTTED_SALAC_BERRY = this.create("potted_salac_berry", BlocksInvoker.createFlowerPotBlock(SALAC_BERRY))
|
||||
@JvmField
|
||||
val POTTED_SHUCA_BERRY = this.flowerPot("potted_shuca_berry", SHUCA_BERRY)
|
||||
val POTTED_SHUCA_BERRY = this.create("potted_shuca_berry", BlocksInvoker.createFlowerPotBlock(SHUCA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_SITRUS_BERRY = this.flowerPot("potted_sitrus_berry", SITRUS_BERRY)
|
||||
val POTTED_SITRUS_BERRY = this.create("potted_sitrus_berry", BlocksInvoker.createFlowerPotBlock(SITRUS_BERRY))
|
||||
@JvmField
|
||||
val POTTED_SPELON_BERRY = this.flowerPot("potted_spelon_berry", SPELON_BERRY)
|
||||
val POTTED_SPELON_BERRY = this.create("potted_spelon_berry", BlocksInvoker.createFlowerPotBlock(SPELON_BERRY))
|
||||
@JvmField
|
||||
val POTTED_STARF_BERRY = this.flowerPot("potted_starf_berry", STARF_BERRY)
|
||||
val POTTED_STARF_BERRY = this.create("potted_starf_berry", BlocksInvoker.createFlowerPotBlock(STARF_BERRY))
|
||||
@JvmField
|
||||
val POTTED_TAMATO_BERRY = this.flowerPot("potted_tamato_berry", TAMATO_BERRY)
|
||||
val POTTED_TAMATO_BERRY = this.create("potted_tamato_berry", BlocksInvoker.createFlowerPotBlock(TAMATO_BERRY))
|
||||
@JvmField
|
||||
val POTTED_TANGA_BERRY = this.flowerPot("potted_tanga_berry", TANGA_BERRY)
|
||||
val POTTED_TANGA_BERRY = this.create("potted_tanga_berry", BlocksInvoker.createFlowerPotBlock(TANGA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_TOUGA_BERRY = this.flowerPot("potted_touga_berry", TOUGA_BERRY)
|
||||
val POTTED_TOUGA_BERRY = this.create("potted_touga_berry", BlocksInvoker.createFlowerPotBlock(TOUGA_BERRY))
|
||||
@JvmField
|
||||
val POTTED_WACAN_BERRY = this.flowerPot("potted_wacan_berry", WACAN_BERRY)
|
||||
val POTTED_WACAN_BERRY = this.create("potted_wacan_berry", BlocksInvoker.createFlowerPotBlock(WACAN_BERRY))
|
||||
@JvmField
|
||||
val POTTED_WATMEL_BERRY = this.flowerPot("potted_watmel_berry", WATMEL_BERRY)
|
||||
val POTTED_WATMEL_BERRY = this.create("potted_watmel_berry", BlocksInvoker.createFlowerPotBlock(WATMEL_BERRY))
|
||||
@JvmField
|
||||
val POTTED_WEPEAR_BERRY = this.flowerPot("potted_wepear_berry", WEPEAR_BERRY)
|
||||
val POTTED_WEPEAR_BERRY = this.create("potted_wepear_berry", BlocksInvoker.createFlowerPotBlock(WEPEAR_BERRY))
|
||||
@JvmField
|
||||
val POTTED_WIKI_BERRY = this.flowerPot("potted_wiki_berry", WIKI_BERRY)
|
||||
val POTTED_WIKI_BERRY = this.create("potted_wiki_berry", BlocksInvoker.createFlowerPotBlock(WIKI_BERRY))
|
||||
@JvmField
|
||||
val POTTED_YACHE_BERRY = this.flowerPot("potted_yache_berry", YACHE_BERRY)
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.flowerPot("potted_pep_up_flower", PEP_UP_FLOWER)
|
||||
@JvmField
|
||||
val POTTED_RED_APRICORN_SAPLING = this.flowerPot("potted_red_apricorn_sapling", RED_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_YELLOW_APRICORN_SAPLING = this.flowerPot("potted_yellow_apricorn_sapling", YELLOW_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_GREEN_APRICORN_SAPLING = this.flowerPot("potted_green_apricorn_sapling", GREEN_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_BLUE_APRICORN_SAPLING = this.flowerPot("potted_blue_apricorn_sapling", BLUE_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_PINK_APRICORN_SAPLING = this.flowerPot("potted_pink_apricorn_sapling", PINK_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_BLACK_APRICORN_SAPLING = this.flowerPot("potted_black_apricorn_sapling", BLACK_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_WHITE_APRICORN_SAPLING = this.flowerPot("potted_white_apricorn_sapling", WHITE_APRICORN_SAPLING)
|
||||
@JvmField
|
||||
val POTTED_SACCHARINE_SAPLING = this.flowerPot("potted_saccharine_sapling", SACCHARINE_SAPLING)
|
||||
val POTTED_YACHE_BERRY = this.create("potted_yache_berry", BlocksInvoker.createFlowerPotBlock(YACHE_BERRY))
|
||||
|
||||
init {
|
||||
/**
|
||||
@@ -1345,16 +1346,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
|
||||
fun typeGemClusters() = typeGemClusters.toMap()
|
||||
|
||||
private fun flowerPot(name: String, flower: Block) = this.create(
|
||||
name,
|
||||
FlowerPotBlock(
|
||||
flower,
|
||||
BlockBehaviour.Properties.of()
|
||||
.instabreak()
|
||||
.noOcclusion()
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
)
|
||||
)
|
||||
private fun pottedPlant(name: String, flower: Block) = this.create(name, BlocksInvoker.createFlowerPotBlock(flower))
|
||||
|
||||
private fun mintLitter(name: String, color: MapColor) = this.create(
|
||||
name,
|
||||
|
||||
@@ -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)
|
||||
|
||||
+7
-31
@@ -9,7 +9,6 @@
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
@@ -18,7 +17,6 @@ import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.mainThreadRuntime
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
@@ -36,8 +34,6 @@ import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
class HateEntityTaskConfig : SingleTaskConfig {
|
||||
val entityCondition: Expression = "true".asExpression()
|
||||
val range: ExpressionOrEntityVariable = Either.left("24".asExpression())
|
||||
val chance: Float = Cobblemon.config.predationChance
|
||||
private val coercedChance = chance.coerceIn(0f, 1f)
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
|
||||
override fun createTask(
|
||||
@@ -47,44 +43,24 @@ class HateEntityTaskConfig : SingleTaskConfig {
|
||||
val range = range.resolveFloat(behaviourConfigurationContext.runtime)
|
||||
behaviourConfigurationContext.addMemories(
|
||||
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
|
||||
MemoryModuleType.ANGRY_AT,
|
||||
MemoryModuleType.ATTACK_TARGET
|
||||
MemoryModuleType.ANGRY_AT
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
|
||||
return BehaviorBuilder.create { instance ->
|
||||
instance.group(
|
||||
instance.present(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES),
|
||||
instance.registered(MemoryModuleType.ANGRY_AT),
|
||||
instance.registered(MemoryModuleType.ATTACK_TARGET),
|
||||
).apply(instance) { entities, angryAt, attackTarget ->
|
||||
instance.registered(MemoryModuleType.ANGRY_AT)
|
||||
).apply(instance) { entities, angryAt ->
|
||||
Trigger { world, entity, _ ->
|
||||
// bail if gamerule is disabled
|
||||
if (!world.gameRules.getBoolean(CobblemonGameRules.DO_POKEMON_PREDATION)) return@Trigger false
|
||||
val target = instance.get(entities).findClosest { hated ->
|
||||
mainThreadRuntime.withQueryValue("entity", hated.asMostSpecificMoLangValue())
|
||||
val target = instance.get(entities).findClosest {
|
||||
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
return@findClosest mainThreadRuntime.resolveBoolean(entityCondition)
|
||||
}.orElse(null)
|
||||
|
||||
var shouldTrigger = false
|
||||
if (target != null && target.distanceTo(entity) <= range) {
|
||||
shouldTrigger = when {
|
||||
// set always attacking if activity chance is equal to 1.0
|
||||
coercedChance == 1f -> {
|
||||
angryAt.set(target.uuid)
|
||||
attackTarget.set(target)
|
||||
true
|
||||
}
|
||||
// roll the dice in other way
|
||||
coercedChance != 1f && world.random.nextFloat() < coercedChance -> {
|
||||
angryAt.set(target.uuid)
|
||||
attackTarget.set(target)
|
||||
true
|
||||
}
|
||||
// fallback to peaceful behavior in unattended cases
|
||||
else -> false
|
||||
}
|
||||
angryAt.set(target.uuid)
|
||||
}
|
||||
return@Trigger shouldTrigger
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,10 +239,6 @@ open class PokemonBattle(
|
||||
}
|
||||
|
||||
fun writeShowdownAction(vararg messages: String) {
|
||||
if (messages.any { it.contains("\n") || it.contains("\r") }) {
|
||||
LOGGER.warn("Refusing to forward Showdown action with an embedded line break for battle {}: {}", battleId, messages.joinToString(" | "))
|
||||
return
|
||||
}
|
||||
log(messages.joinToString("\n"))
|
||||
ShowdownService.service.send(battleId, messages.toList().toTypedArray())
|
||||
}
|
||||
|
||||
+2
-8
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.orientation
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.math.QuatSpring
|
||||
import com.cobblemon.mod.common.util.math.geometry.toDegrees
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
@@ -30,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
|
||||
@@ -103,11 +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.
|
||||
val pokemon = entity as? PokemonEntity
|
||||
val halflife = pokemon?.ifRidingAvailableSupply(fallback = null as Float?) { behaviour, settings, state ->
|
||||
behaviour.renderHalflife(settings, state, pokemon)
|
||||
} ?: RENDER_SMOOTH_HALFLIFE
|
||||
simpleSpringDamperQuat(renderSpring, targetQuat, halflife, TICK_DT)
|
||||
simpleSpringDamperQuat(renderSpring, targetQuat, 0.15f, TICK_DT)
|
||||
|
||||
val newRenderOrientation = Matrix3f()
|
||||
renderSpring.quat.get(newRenderOrientation)
|
||||
|
||||
+6
-6
@@ -88,7 +88,7 @@ fun getToolTipHeaderIcon(tradeEvolution: TradeEvolution): CobblemonDexEvolutionR
|
||||
lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(species.asIdentifierDefaultingNamespace())
|
||||
?.let { lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.plainCopy().green()) }
|
||||
?.let { lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.green()) }
|
||||
?: lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
|
||||
}
|
||||
return CobblemonDexEvolutionRequirementDisplay(CobblemonItems.LINK_CABLE, tooltip)
|
||||
@@ -282,7 +282,7 @@ fun buildLevelRequirement(levelRequirement: LevelRequirement): DexEvolutionRequi
|
||||
|
||||
fun buildMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): DexEvolutionRequirementDisplay {
|
||||
val icon = Items.CLOCK
|
||||
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().plainCopy().green()
|
||||
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().green()
|
||||
val tooltip = lang("ui.pokedex.requirement.moon_phase.tooltip", moonPhaseComponent)
|
||||
|
||||
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
|
||||
@@ -290,7 +290,7 @@ fun buildMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): DexEv
|
||||
|
||||
fun buildMoveSetRequirement(moveSetRequirement: MoveSetRequirement): DexEvolutionRequirementDisplay {
|
||||
val icon = CobblemonItems.TECHNICAL_MACHINE
|
||||
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.plainCopy().green())
|
||||
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.green())
|
||||
|
||||
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
|
||||
}
|
||||
@@ -319,7 +319,7 @@ fun buildMoveTypeRequirement(moveTypeRequirement: MoveTypeRequirement): DexEvolu
|
||||
else -> CobblemonItems.NORMAL_GEM
|
||||
}
|
||||
|
||||
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.plainCopy().green())
|
||||
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.green())
|
||||
|
||||
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
|
||||
}
|
||||
@@ -437,7 +437,7 @@ fun buildStructureRequirement(structureRequirement: StructureRequirement, negate
|
||||
|
||||
fun buildTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): DexEvolutionRequirementDisplay? {
|
||||
val icon = Items.CLOCK
|
||||
val timeRangeComponent = timeRangeRequirement.range.displayName()?.plainCopy() ?: return null
|
||||
val timeRangeComponent = timeRangeRequirement.range.displayName() ?: return null
|
||||
val tooltip = lang("ui.pokedex.requirement.time_range.tooltip", timeRangeComponent.green())
|
||||
|
||||
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
|
||||
@@ -445,7 +445,7 @@ fun buildTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): DexEv
|
||||
|
||||
fun buildUseMoveRequirement(useMoveRequirement: UseMoveRequirement): DexEvolutionRequirementDisplay {
|
||||
val icon = CobblemonItems.CHOICE_BAND
|
||||
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.plainCopy().green())
|
||||
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.green())
|
||||
|
||||
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
|
||||
}
|
||||
|
||||
-53
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
+19
-59
@@ -12,21 +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
|
||||
import org.joml.Vector3f
|
||||
|
||||
/**
|
||||
* Represents the behaviour of a Pokemon when being ridden.
|
||||
@@ -47,42 +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
|
||||
|
||||
/** Moves the ride itself this tick instead of the shared velocity and inertia. True if it did */
|
||||
fun travel(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean = false
|
||||
|
||||
/**
|
||||
* Turns the ride for one frame while it rolls. Runs every frame on the client that's driving with the mouse
|
||||
* share the ride took in [consumeLook]. Rotate through [RideSteerInput.controller] or its helpers.
|
||||
*/
|
||||
fun steer(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: RideSteerInput) {}
|
||||
fun 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
|
||||
|
||||
@@ -94,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
|
||||
@@ -110,12 +94,6 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
|
||||
fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? = null
|
||||
|
||||
/**
|
||||
* True if this ride moves along paths the server can't replay as one straight move, like climbing round edges.
|
||||
* The server then takes where the rider says it landed as long as the hitbox is clear there, instead of snapping it back
|
||||
*/
|
||||
fun serverTrustsLanding(settings: Settings, state: State, vehicle: PokemonEntity): Boolean = false
|
||||
|
||||
fun canStopRiding(settings: Settings, state: State, vehicle: PokemonEntity, passenger: Player): Boolean = true
|
||||
|
||||
fun createDefaultState(settings: Settings): State
|
||||
@@ -133,28 +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
|
||||
|
||||
/** Blocks to shift the model and its locators by once [orientation] has turned it, so a ride can keep its model on what it's standing on */
|
||||
fun renderOffset(settings: Settings, state: State, vehicle: PokemonEntity, orientation: Quaternionf): Vector3f = Vector3f()
|
||||
|
||||
/** Seconds the drawn orientation takes to close half the gap to the real one. Null keeps the controller's default */
|
||||
fun renderHalflife(settings: Settings, state: State, vehicle: PokemonEntity): Float? = null
|
||||
|
||||
/** Look limits and carry for riders while the ride rolls. */
|
||||
fun lookSettings(settings: Settings, state: State, vehicle: PokemonEntity): RideLookSettings = RideLookSettings.DEFAULT
|
||||
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.
|
||||
|
||||
-3
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
+101
-310
@@ -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. 0 always rolls
|
||||
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()
|
||||
}
|
||||
-1215
File diff suppressed because it is too large.
Load diff
+34
-12
@@ -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
|
||||
}
|
||||
|
||||
+6
-26
@@ -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
|
||||
}
|
||||
|
||||
+34
-12
@@ -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,
|
||||
|
||||
+8
-15
@@ -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
|
||||
}
|
||||
|
||||
+46
-22
@@ -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,
|
||||
|
||||
+51
-124
@@ -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,12 +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
|
||||
import org.joml.Vector3f
|
||||
|
||||
class CompositeBehaviour : RidingBehaviour<CompositeSettings, CompositeState> {
|
||||
companion object {
|
||||
@@ -175,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,
|
||||
@@ -231,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,
|
||||
@@ -243,49 +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 travel(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.travel(behaviourSettings, behaviourState, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun velocityRunsTwicePerTick(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.velocityRunsTwicePerTick(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun serverTrustsLanding(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.serverTrustsLanding(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
@@ -308,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,
|
||||
@@ -370,70 +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 renderOffset(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
orientation: Quaternionf
|
||||
): Vector3f {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.renderOffset(behaviourSettings, behaviourState, vehicle, orientation)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderHalflife(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): Float? {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.renderHalflife(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun lookSettings(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity
|
||||
): RideLookSettings {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.lookSettings(behaviourSettings, behaviourState, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
override fun consumeLook(
|
||||
settings: CompositeSettings,
|
||||
state: CompositeState,
|
||||
vehicle: PokemonEntity,
|
||||
input: RideLookInput
|
||||
): RideLookTake {
|
||||
return chooseBehaviour(settings, state) { behaviour, behaviourSettings, behaviourState ->
|
||||
behaviour.consumeLook(behaviourSettings, behaviourState, vehicle, input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class CompositeSettings : RidingBehaviourSettings {
|
||||
|
||||
+1
-3
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
-879
@@ -1,879 +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.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.camera.RideCameraContext
|
||||
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.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.util.collectCollisionPrimitives
|
||||
import com.cobblemon.mod.common.api.riding.util.estimateGradient
|
||||
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.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.QuatSpring
|
||||
import com.cobblemon.mod.common.util.math.simpleSpringDamperQuat
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
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 org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.MoverType
|
||||
|
||||
class ClimbBehaviour : RidingBehaviour<ClimbSettings, ClimbState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/climb")
|
||||
// Vanilla gravity, vertical drag and in air drag per tick. LivingEntity.travel
|
||||
private const val VANILLA_GRAVITY = 0.08
|
||||
private const val VANILLA_VERTICAL_DRAG = 0.98
|
||||
private const val VANILLA_AIR_DRAG = 0.91
|
||||
// Vanilla push per tick while in the air. LivingEntity.getFrictionInfluencedSpeed
|
||||
private const val VANILLA_AIR_CONTROL = 0.02
|
||||
private const val VANILLA_TICKS_PER_SECOND = 20.0
|
||||
// Share of top speed that counts as having reached it, for how long accelerationExpr says that takes
|
||||
private const val TOP_SPEED_REACHED_SHARE = 0.95
|
||||
// Stamina has to refill past this after running dry before sprinting works again
|
||||
private const val SPRINT_RESUME_STAMINA = 0.33f
|
||||
// Stamina refills this many times faster than sprinting drains it
|
||||
private const val STAMINA_REFILL_RATE = 4f
|
||||
// FOV while sprinting, the same bump the horse gives
|
||||
private const val SPRINT_FOV_MULTIPLIER = 1.15f
|
||||
// Pull into a wall or ceiling per tick. Floors pull with gravity instead
|
||||
private const val WALL_STICK_FORCE = 0.08
|
||||
// Share of speed along the heading kept each tick with W and S let go. Lower stops quicker
|
||||
private const val SURFACE_GRIP = 0.6
|
||||
// How far off head on walking into a wall can be and still climb onto it. Bigger grabs walls from shallower bumps
|
||||
private const val ENTER_MAX_DEGREES = 45.0
|
||||
private val ENTER_DOT = cos(Math.toRadians(ENTER_MAX_DEGREES))
|
||||
// Blocks the floor steps up like vanilla, per block of hitbox width. Walls and ceilings climb every bump instead
|
||||
private const val STEP_HEIGHT = 1.0
|
||||
// Gap in blocks under the body that still counts as standing on the surface
|
||||
private const val SUPPORT_GAP = 0.05
|
||||
// Going over an outside edge the body drops this far past it before reaching back for the next face
|
||||
private const val EDGE_STEP_BACK = 0.25
|
||||
// Extra reach past this tick's movement when looking for the next face round an edge
|
||||
private const val EDGE_REACH_EXTRA = 0.1
|
||||
// Movement this far short of what was asked counts as blocked
|
||||
private const val BLOCKED_EPSILON = 1.0E-3
|
||||
// Ticks after a jump before surfaces can grab the body again
|
||||
private const val JUMP_DETACH_TICKS = 8
|
||||
// Blocks of leap per point of jumpExpr
|
||||
private const val JUMP_BLOCKS_PER_STAT = 2.0
|
||||
// Share of the body's roll the first person hands lean with
|
||||
private const val HAND_ROLL_SHARE = 0.5f
|
||||
// Seconds the camera spring takes to settle onto a new face
|
||||
private const val CAMERA_HALFLIFE = 0.25f
|
||||
// The drawn body has to be within this many degrees of the face it's on before the camera moves onto that face. Lower holds the camera through more slopes and steps, higher follows face changes sooner
|
||||
private const val CAMERA_FACE_SWITCH_DEGREES = 20.0
|
||||
// Seconds the drawn body takes to close half the gap to the real one. Lower snaps round corners quicker but shows more jitter
|
||||
private const val RENDER_HALFLIFE = 0.05f
|
||||
// Share of the gap to the drawn up the body closes each tick
|
||||
private const val UP_ALIGN_RATE_PER_TICK = 0.5
|
||||
// Fastest the drawn body turns. Lower spreads going round an outside edge over more time, higher follows it closer
|
||||
private const val VISUAL_MAX_TURN_DEGREES_PER_SECOND = 360.0
|
||||
// How far off the surface the drawn tilt reads the field, as a share of the hitbox size. Edges and steps round off by this much so bigger takes one block steps as gentler slopes
|
||||
private const val VISUAL_LIFT_SHARE = 1.0
|
||||
// Field blend width as a share of the hitbox size. Bigger smooths steps and inside corners over a wider stretch
|
||||
private const val VISUAL_BLEND_SHARE = 1.0
|
||||
// smoothMinimum blends over this many times its smoothingRange
|
||||
private const val SMOOTH_MIN_WIDTH_PER_RANGE = 4f
|
||||
// How far out the read grid spreads as a share of the hitbox's half width. Past 1 it reads beyond the body so it starts tipping before it gets there
|
||||
private const val VISUAL_SPREAD_SHARE = 1.5
|
||||
// Samples along each side of the read grid. Smoother with more but it costs this squared
|
||||
private const val VISUAL_GRID = 5
|
||||
// Pull of the face it's on, worth this many samples. Bigger keeps the drawn body nearer the face, smaller lets the field tilt it more
|
||||
private const val VISUAL_FACE_PULL = 0.5
|
||||
// Furthest the drawn body tilts off the surface it's on so it never looks like it's already on the next face
|
||||
private const val VISUAL_MAX_TILT_DEGREES = 60.0
|
||||
private val VISUAL_MIN_UP_DOT = cos(Math.toRadians(VISUAL_MAX_TILT_DEGREES))
|
||||
private val VISUAL_MAX_TILT_SIN = sin(Math.toRadians(VISUAL_MAX_TILT_DEGREES))
|
||||
private const val MIN_LENGTH_SQ = 1.0E-6
|
||||
private val WORLD_UP = Vec3(0.0, 1.0, 0.0)
|
||||
private val AXES = listOf(Vec3(1.0, 0.0, 0.0), Vec3(0.0, 1.0, 0.0), Vec3(0.0, 0.0, 1.0))
|
||||
}
|
||||
|
||||
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 state.rideVelocity.get().lengthSqr() > MIN_LENGTH_SQ
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
// Water hands over to whatever swims
|
||||
return !vehicle.isInWater && !vehicle.isUnderWater
|
||||
}
|
||||
|
||||
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
|
||||
) {
|
||||
if (vehicle.level().isClientSide) {
|
||||
inAirCheck(state, vehicle)
|
||||
if (driver.isLocalPlayer) updateSprint(settings, state, vehicle, driver)
|
||||
tickStamina(settings, state, vehicle)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Sprint needs forward held, the sprint key and stamina. Once stamina runs dry it has to refill a bit first
|
||||
private fun updateSprint(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity, driver: Player) {
|
||||
val wantsSprint = vehicle.runtime.resolveBoolean(settings.canSprint) && driver.zza > 0f && Minecraft.getInstance().options.keySprint.isDown
|
||||
val stamina = state.stamina.get()
|
||||
if (stamina <= 0f) {
|
||||
state.sprintToggleable.set(false)
|
||||
} else if (!state.sprintToggleable.get() && stamina > SPRINT_RESUME_STAMINA) {
|
||||
state.sprintToggleable.set(true)
|
||||
}
|
||||
state.sprinting.set(wantsSprint && state.sprintToggleable.get() && stamina > 0f)
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
// staminaExpr is seconds of sprinting from full
|
||||
val drain = (1.0 / (vehicle.runtime.resolveDouble(settings.staminaExpr) * VANILLA_TICKS_PER_SECOND)).toFloat()
|
||||
val stamina = state.stamina.get()
|
||||
state.stamina.set((if (state.sprinting.get()) stamina - drain else stamina + drain * STAMINA_REFILL_RATE).coerceIn(0f, 1f))
|
||||
}
|
||||
|
||||
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 )
|
||||
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
// travel does the moving, this only reports it
|
||||
return vehicle.deltaMovement
|
||||
}
|
||||
|
||||
/** Top speed in blocks per tick, multiplied up while sprinting */
|
||||
private fun topSpeed(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity): Double {
|
||||
val speed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
return if (state.sprinting.get()) speed * vehicle.runtime.resolveDouble(settings.sprintSpeedFactor).coerceAtLeast(1.0) else speed
|
||||
}
|
||||
|
||||
/**
|
||||
* Share of speed kept each tick while W or S is held, so the gap to top speed closes to [TOP_SPEED_REACHED_SHARE]
|
||||
* in the seconds accelerationExpr gives. Sprint gets there in the same time since it's a share of the gap
|
||||
*/
|
||||
private fun pushGrip(settings: ClimbSettings, vehicle: PokemonEntity): Double {
|
||||
val seconds = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
return (1.0 - TOP_SPEED_REACHED_SHARE).pow(1.0 / (seconds * VANILLA_TICKS_PER_SECOND))
|
||||
}
|
||||
|
||||
override fun steer(
|
||||
settings: ClimbSettings,
|
||||
state: ClimbState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: RideSteerInput
|
||||
) {
|
||||
// A and D turn the body round the surface it's on. The mouse only looks
|
||||
val heading = state.heading.get()
|
||||
val turnDegrees = driver.xxa * vehicle.runtime.resolveDouble(settings.handlingExpr) * input.deltaTime
|
||||
if (turnDegrees != 0.0 && heading.lengthSqr() > MIN_LENGTH_SQ) {
|
||||
// Positive round the normal turns left, the same way A pushes
|
||||
state.heading.set(rotateAround(heading, state.attachmentNormal.get(), Math.toRadians(turnDegrees)))
|
||||
}
|
||||
orientBody(state, input.controller, input.deltaTime)
|
||||
}
|
||||
|
||||
/** Points the body along the heading with its up eased toward the drawn surface normal */
|
||||
private fun orientBody(state: ClimbState, controller: OrientationController, deltaTime: Double) {
|
||||
val target = state.visualUp.get()
|
||||
val current = state.renderUp.get().takeIf { it.lengthSqr() > MIN_LENGTH_SQ } ?: target
|
||||
val blend = 1.0 - (1.0 - UP_ALIGN_RATE_PER_TICK).pow(deltaTime * VANILLA_TICKS_PER_SECOND)
|
||||
val up = turnToward(current, target, blend, Math.toRadians(VISUAL_MAX_TURN_DEGREES_PER_SECOND) * deltaTime)
|
||||
state.renderUp.set(up)
|
||||
val forward = headingOn(state, up) ?: return
|
||||
val upF = up.toVector3f()
|
||||
val forwardF = forward.toVector3f()
|
||||
val right = Vector3f(forwardF).cross(upF)
|
||||
controller.updateOrientation { Matrix3f(right, upF, Vector3f(forwardF).negate()) }
|
||||
}
|
||||
|
||||
/** [from] turned [share] of the way to [to] but never more than [maxRadians]. Copes with them pointing opposite ways */
|
||||
private fun turnToward(from: Vec3, to: Vec3, share: Double, maxRadians: Double): Vec3 {
|
||||
val fromF = from.toVector3f().normalize()
|
||||
val toF = to.toVector3f().normalize()
|
||||
val angle = acos(fromF.dot(toF).coerceIn(-1f, 1f).toDouble())
|
||||
val step = if (angle > MIN_LENGTH_SQ) min(share, maxRadians / angle) else share
|
||||
val turn = Quaternionf().rotationTo(fromF, toF)
|
||||
return Vec3(Quaternionf().slerp(turn, step.toFloat()).transform(fromF))
|
||||
}
|
||||
|
||||
/** [vector] turned [radians] round [axis] by the right hand rule */
|
||||
private fun rotateAround(vector: Vec3, axis: Vec3, radians: Double): Vec3 {
|
||||
val c = cos(radians)
|
||||
val s = sin(radians)
|
||||
return vector.scale(c).add(axis.cross(vector).scale(s)).add(axis.scale(axis.dot(vector) * (1.0 - c)))
|
||||
}
|
||||
|
||||
// Going round an edge is several moves in one tick so the server's straight line replay lands somewhere else
|
||||
override fun serverTrustsLanding(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity) = true
|
||||
|
||||
// Vanilla step up on the floor. Every other face steps over bumps in travel
|
||||
override fun maxUpStep(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity): Float? {
|
||||
return if (isFloor(state.attachmentNormal.get())) (STEP_HEIGHT * max(1.0, vehicle.bbWidth.toDouble())).toFloat() else 0f
|
||||
}
|
||||
|
||||
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 if (state.sprinting.get()) SPRINT_FOV_MULTIPLIER else 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()
|
||||
|
||||
/**
|
||||
* Sticks to one face at a time. W and S push along the body's heading and the face only changes when
|
||||
* walking head on into another one or walking off an outside edge. The floor steps up like vanilla, walls
|
||||
* and ceilings climb over bumps face by face. Nothing near the body pulls on it except the face it's on
|
||||
*/
|
||||
override fun travel(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
if (vehicle !is OrientationControllable) return false
|
||||
val controller = vehicle.orientationController
|
||||
val crouching = driver.isShiftKeyDown
|
||||
|
||||
if (state.detachTicks.get() > 0) state.detachTicks.set(state.detachTicks.get() - 1)
|
||||
val canAttach = state.detachTicks.get() == 0
|
||||
|
||||
val n = state.attachmentNormal.get()
|
||||
val onFloor = isFloor(n)
|
||||
val heading = flatten(state.heading.get(), n) ?: flatten(controller.forwardVector.toVec3d(), n) ?: perpendicularTo(n)
|
||||
state.heading.set(heading)
|
||||
// Walking normally falls off edges. Crouching or being on a wall or ceiling holds on round them
|
||||
val holdsOnEdges = !onFloor || crouching
|
||||
val supported = state.supported.get()
|
||||
// What it's standing on before it moves, so an edge it walks off can be found from it
|
||||
val support = if (supported) supportBoxes(vehicle, n) else emptyList()
|
||||
|
||||
val velocity = vehicle.deltaMovement
|
||||
// Settles at top speed, v = push / (1 - grip). Letting go stops on the surface grip instead
|
||||
val grip = if (driver.zza != 0f) pushGrip(settings, vehicle) else SURFACE_GRIP
|
||||
val along = heading.dot(velocity) * grip + driver.zza * topSpeed(settings, state, vehicle) * (1.0 - grip)
|
||||
var normal = n.dot(velocity) * VANILLA_VERTICAL_DRAG - if (onFloor) VANILLA_GRAVITY else WALL_STICK_FORCE
|
||||
|
||||
// Leap away from the surface
|
||||
val jumpPressed = driver.jumping && !state.jumpHeld.get()
|
||||
state.jumpHeld.set(driver.jumping)
|
||||
var jumped = false
|
||||
if (jumpPressed && supported && settings.canJump.let { vehicle.runtime.resolveBoolean(it) }) {
|
||||
normal = sqrt(2.0 * VANILLA_GRAVITY * vehicle.runtime.resolveDouble(settings.jumpExpr) * JUMP_BLOCKS_PER_STAT)
|
||||
state.detachTicks.set(JUMP_DETACH_TICKS)
|
||||
jumped = true
|
||||
}
|
||||
|
||||
// On a surface the velocity is only ever along the heading and the normal, so there's nothing sideways to drift with. In the air it's vanilla
|
||||
val motion = if (supported) {
|
||||
heading.scale(along).add(n.scale(normal))
|
||||
} else {
|
||||
Vec3(velocity.x * VANILLA_AIR_DRAG, velocity.y * VANILLA_VERTICAL_DRAG - VANILLA_GRAVITY, velocity.z * VANILLA_AIR_DRAG)
|
||||
.add(heading.scale(driver.zza * VANILLA_AIR_CONTROL))
|
||||
}
|
||||
|
||||
val before = vehicle.position()
|
||||
vehicle.deltaMovement = motion
|
||||
vehicle.move(MoverType.SELF, motion)
|
||||
val moved = vehicle.position().subtract(before)
|
||||
|
||||
if (supported) {
|
||||
// Keep the speed unless something stopped it, and don't keep pushing into the surface once it's on it
|
||||
val movedAlong = moved.dot(heading)
|
||||
val alongAfter = if (abs(movedAlong) + BLOCKED_EPSILON < abs(along)) movedAlong else along
|
||||
val normalAfter = if (normal < 0.0 && touching(vehicle, n, SUPPORT_GAP)) 0.0 else normal
|
||||
vehicle.deltaMovement = heading.scale(alongAfter).add(n.scale(normalAfter))
|
||||
}
|
||||
|
||||
val moveDir = if (abs(along) > MIN_LENGTH_SQ) heading.scale(sign(along)) else heading
|
||||
var supportedNow = touching(vehicle, n, SUPPORT_GAP)
|
||||
|
||||
// Walked head on into something too tall to step over so climb onto it. Shallower bumps just slide along it
|
||||
val enter = if (canAttach) blockingFace(motion, moved, n, moveDir) else null
|
||||
if (enter != null) {
|
||||
changeFace(state, vehicle, n, enter, along)
|
||||
supportedNow = true
|
||||
} else if (!supportedNow && supported && !jumped && holdsOnEdges && canAttach) {
|
||||
// Walked off an outside edge. Crouching on the floor always goes over too, that's what it's for
|
||||
val next = wrapOutsideEdge(vehicle, n, support, moveDir, motion.length() + EDGE_REACH_EXTRA)
|
||||
if (next != null) {
|
||||
changeFace(state, vehicle, n, next, along)
|
||||
supportedNow = true
|
||||
}
|
||||
}
|
||||
// Nothing to hold onto any more so fall like normal
|
||||
if (!supportedNow && !isFloor(state.attachmentNormal.get())) {
|
||||
state.heading.set(flatten(heading, WORLD_UP) ?: flatten(n, WORLD_UP) ?: perpendicularTo(WORLD_UP))
|
||||
state.attachmentNormal.set(WORLD_UP)
|
||||
}
|
||||
|
||||
state.supported.set(supportedNow)
|
||||
val face = state.attachmentNormal.get()
|
||||
state.visualUp.set(if (supportedNow) visualNormal(vehicle, face, state.heading.get(), state.visualUp.get()) else WORLD_UP)
|
||||
vehicle.setOnGround(supportedNow)
|
||||
if (supportedNow) vehicle.resetFallDistance()
|
||||
state.rideVelocity.set(vehicle.deltaMovement)
|
||||
return true
|
||||
}
|
||||
|
||||
/** The face that stopped the move along the surface if it was walked into close enough to head on, pointing back out of it */
|
||||
private fun blockingFace(motion: Vec3, moved: Vec3, n: Vec3, moveDir: Vec3): Vec3? {
|
||||
var best: Vec3? = null
|
||||
var bestDot = -ENTER_DOT
|
||||
for (axis in AXES) {
|
||||
if (abs(axis.dot(n)) > 0.5) continue
|
||||
val asked = axis.dot(motion)
|
||||
if (abs(asked) < BLOCKED_EPSILON || abs(axis.dot(moved) - asked) < BLOCKED_EPSILON) continue
|
||||
val face = axis.scale(-sign(asked))
|
||||
val dot = moveDir.dot(face)
|
||||
if (dot < bestDot) {
|
||||
bestDot = dot
|
||||
best = face
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
/**
|
||||
* Walked off an outside edge. Which edges it went over comes from where the hitbox ended up next to the blocks it
|
||||
* was standing on at the start of the tick, so it's the real edge whichever way it was heading. Drops just past it
|
||||
* and reaches straight back across each edge in turn, most along the way it was walking first, then across all of
|
||||
* them at once for a corner where it's clear of the block on both sides. This tick took it at most its own movement
|
||||
* past an edge so reaching back that far finds the face round it. Gives that face's normal, or puts everything back
|
||||
* and gives null if there isn't one
|
||||
*/
|
||||
private fun wrapOutsideEdge(vehicle: PokemonEntity, n: Vec3, support: List<AABB>, moveDir: Vec3, reach: Double): Vec3? {
|
||||
val savedPos = vehicle.position()
|
||||
val savedMotion = vehicle.deltaMovement
|
||||
val savedOnGround = vehicle.onGround()
|
||||
val savedHorizontal = vehicle.horizontalCollision
|
||||
val savedVertical = vehicle.verticalCollision
|
||||
|
||||
val box = vehicle.boundingBox
|
||||
val crossed = mutableListOf<Vec3>()
|
||||
for (block in support) for (axis in AXES) {
|
||||
if (abs(axis.dot(n)) > 0.5) continue
|
||||
val side = directionAxis(axis)
|
||||
if (box.min(side) >= block.max(side) - BLOCKED_EPSILON) crossed.add(axis)
|
||||
if (box.max(side) <= block.min(side) + BLOCKED_EPSILON) crossed.add(axis.scale(-1.0))
|
||||
}
|
||||
val faces = crossed.distinct().sortedByDescending { it.dot(moveDir) }
|
||||
val tries = faces.map { listOf(it) } + if (faces.size > 1) listOf(faces) else emptyList()
|
||||
for (group in tries) {
|
||||
vehicle.setPos(savedPos)
|
||||
slide(vehicle, n.scale(-EDGE_STEP_BACK))
|
||||
// Each axis gets the full reach so a corner reaches back as far on both sides
|
||||
slide(vehicle, group.fold(Vec3.ZERO) { back, face -> back.subtract(face.scale(reach)) })
|
||||
val landed = group.filter { touching(vehicle, it, SUPPORT_GAP) }.maxByOrNull { it.dot(moveDir) }
|
||||
if (landed != null) return landed
|
||||
}
|
||||
|
||||
vehicle.setPos(savedPos)
|
||||
vehicle.deltaMovement = savedMotion
|
||||
vehicle.setOnGround(savedOnGround)
|
||||
vehicle.horizontalCollision = savedHorizontal
|
||||
vehicle.verticalCollision = savedVertical
|
||||
return null
|
||||
}
|
||||
|
||||
/** Collision boxes of whatever the body is standing on, against the side facing away from [n] */
|
||||
private fun supportBoxes(vehicle: PokemonEntity, n: Vec3): List<AABB> {
|
||||
val slab = slabBeyond(vehicle.boundingBox, n, SUPPORT_GAP)
|
||||
return vehicle.level().getBlockCollisions(vehicle, slab).flatMap { it.toAabbs() }.filter { it.intersects(slab) }
|
||||
}
|
||||
|
||||
private fun directionAxis(axis: Vec3): Direction.Axis = when {
|
||||
abs(axis.x) > 0.5 -> Direction.Axis.X
|
||||
abs(axis.y) > 0.5 -> Direction.Axis.Y
|
||||
else -> Direction.Axis.Z
|
||||
}
|
||||
/**
|
||||
* Moves the body by [delta] stopping at blocks and nothing else. No step up, which would climb straight back over
|
||||
* the edge it's reaching round, and none of move's fall or ground updates since this is only feeling for a face
|
||||
*/
|
||||
private fun slide(vehicle: PokemonEntity, delta: Vec3) {
|
||||
val box = vehicle.boundingBox
|
||||
val level = vehicle.level()
|
||||
val allowed = Entity.collideBoundingBox(vehicle, delta, box, level, level.getEntityCollisions(vehicle, box.expandTowards(delta)))
|
||||
vehicle.setPos(vehicle.position().add(allowed))
|
||||
}
|
||||
|
||||
/** Moves onto the face [to] from [from]. The heading tips over the edge between them and keeps its speed */
|
||||
private fun changeFace(state: ClimbState, vehicle: PokemonEntity, from: Vec3, to: Vec3, along: Double) {
|
||||
val turn = Quaternionf().rotationTo(from.toVector3f(), to.toVector3f())
|
||||
val heading = flatten(Vec3(turn.transform(state.heading.get().toVector3f())), to) ?: perpendicularTo(to)
|
||||
state.heading.set(heading)
|
||||
state.attachmentNormal.set(to)
|
||||
vehicle.deltaMovement = heading.scale(along)
|
||||
}
|
||||
|
||||
/** True if there's something solid within [depth] of the hitbox on the side facing away from [normal] */
|
||||
private fun touching(vehicle: PokemonEntity, normal: Vec3, depth: Double): Boolean {
|
||||
return !vehicle.level().noCollision(vehicle, slabBeyond(vehicle.boundingBox, normal, depth))
|
||||
}
|
||||
|
||||
/** The [depth] thick slab against [box] on the side facing away from [normal] */
|
||||
private fun slabBeyond(box: AABB, normal: Vec3, depth: Double): AABB = when {
|
||||
normal.x > 0.5 -> AABB(box.minX - depth, box.minY, box.minZ, box.minX, box.maxY, box.maxZ)
|
||||
normal.x < -0.5 -> AABB(box.maxX, box.minY, box.minZ, box.maxX + depth, box.maxY, box.maxZ)
|
||||
normal.y > 0.5 -> AABB(box.minX, box.minY - depth, box.minZ, box.maxX, box.minY, box.maxZ)
|
||||
normal.y < -0.5 -> AABB(box.minX, box.maxY, box.minZ, box.maxX, box.maxY + depth, box.maxZ)
|
||||
normal.z > 0.5 -> AABB(box.minX, box.minY, box.minZ - depth, box.maxX, box.maxY, box.minZ)
|
||||
else -> AABB(box.minX, box.minY, box.maxZ, box.maxX, box.maxY, box.maxZ + depth)
|
||||
}
|
||||
|
||||
/**
|
||||
* Surface normal the body is drawn with. Reads the field on a grid under the body lifted off the surface, the way
|
||||
* the hoverboard reads it from its hover height. Up there every edge and step is rounded off by the lift, so a
|
||||
* one block step reads as a slope instead of a wall. The grid hangs off last tick's drawn up so it turns smoothly
|
||||
* through face changes, and only the part of each gradient along the heading counts so walls beside the body never
|
||||
* roll it. The face it's on pulls weakly underneath. Lift, spread and blend grow with the hitbox. Never tilts more
|
||||
* than [VISUAL_MAX_TILT_DEGREES] off the face it's on
|
||||
*/
|
||||
private fun visualNormal(vehicle: PokemonEntity, n: Vec3, heading: Vec3, previous: Vec3): Vec3 {
|
||||
val box = vehicle.boundingBox
|
||||
// Mean of the hitbox's three sides
|
||||
val size = (box.xsize + box.ysize + box.zsize) / 3.0
|
||||
val lift = size * VISUAL_LIFT_SHARE
|
||||
val blend = size * VISUAL_BLEND_SHARE
|
||||
val up = previous.normalize()
|
||||
val across = heading.cross(n).normalize()
|
||||
val along = up.cross(across).normalize()
|
||||
// Middle of the underside, lifted off the surface
|
||||
val middle = box.center.add(up.scale(lift - halfWidthAlong(box, up)))
|
||||
val halfAlong = halfWidthAlong(box, along) * VISUAL_SPREAD_SHARE
|
||||
val halfAcross = halfWidthAlong(box, across) * VISUAL_SPREAD_SHARE
|
||||
val primitives = collectCollisionPrimitives(vehicle, box.inflate(lift + blend + max(halfAlong, halfAcross)))
|
||||
if (primitives.isEmpty()) return n
|
||||
|
||||
val tuning = DistanceFieldTuning(smoothingRange = blend.toFloat() / SMOOTH_MIN_WIDTH_PER_RANGE)
|
||||
var sum = n.scale(VISUAL_FACE_PULL)
|
||||
for (i in 0 until VISUAL_GRID) for (j in 0 until VISUAL_GRID) {
|
||||
val point = middle.add(along.scale(gridStep(i) * halfAlong)).add(across.scale(gridStep(j) * halfAcross))
|
||||
val gradient = estimateGradient(primitives, point, tuning)
|
||||
sum = sum.add(gradient.subtract(across.scale(gradient.dot(across))))
|
||||
}
|
||||
if (sum.lengthSqr() < MIN_LENGTH_SQ) return n
|
||||
val normal = sum.normalize()
|
||||
if (normal.dot(n) >= VISUAL_MIN_UP_DOT) return normal
|
||||
val side = flatten(normal, n) ?: return n
|
||||
return n.scale(VISUAL_MIN_UP_DOT).add(side.scale(VISUAL_MAX_TILT_SIN))
|
||||
}
|
||||
|
||||
/** Half the hitbox's width measured along [axis], for any direction */
|
||||
private fun halfWidthAlong(box: AABB, axis: Vec3): Double = (abs(axis.x) * box.xsize + abs(axis.y) * box.ysize + abs(axis.z) * box.zsize) * 0.5
|
||||
|
||||
/** Where grid sample [index] sits across the hitbox, -1 to 1 */
|
||||
private fun gridStep(index: Int): Double = index * 2.0 / (VISUAL_GRID - 1) - 1.0
|
||||
|
||||
private fun isFloor(n: Vec3) = n.y > 0.5
|
||||
|
||||
/** Any direction along the face with normal [n] */
|
||||
private fun perpendicularTo(n: Vec3): Vec3 = n.cross(if (abs(n.y) < 0.5) WORLD_UP else Vec3(1.0, 0.0, 0.0)).normalize()
|
||||
|
||||
/** [vector] with the part along [axis] taken out, or null if nothing's left */
|
||||
private fun flatten(vector: Vec3, axis: Vec3): Vec3? {
|
||||
val flat = vector.subtract(axis.scale(axis.dot(vector)))
|
||||
return if (flat.lengthSqr() > MIN_LENGTH_SQ) flat.normalize() else null
|
||||
}
|
||||
|
||||
// The mouse only ever moves the look. A and D steer
|
||||
override fun consumeLook(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity, input: RideLookInput) = RideLookTake.NONE
|
||||
|
||||
// The look turns with the body, both when A and D steer and when it goes round onto another face
|
||||
override fun lookSettings(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity) =
|
||||
RideLookSettings(maxYaw = vehicle.runtime.resolveFloat(settings.lookYawLimit))
|
||||
|
||||
/**
|
||||
* Camera stands on one of the 6 cube sides and heads the way the body does. It only moves to the face the body
|
||||
* is really on, and only once the drawn body has turned to within [CAMERA_FACE_SWITCH_DEGREES] of it. Steps and
|
||||
* slopes change the face back and forth but the drawn body only leans partway, so the camera stays put
|
||||
*/
|
||||
override fun cameraBase(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity, context: RideCameraContext): Quaternionf {
|
||||
val camera = context.data as? ClimbCamera ?: ClimbCamera().also { context.data = it }
|
||||
val face = state.attachmentNormal.get()
|
||||
val surface = (state.renderUp.get().takeIf { it.lengthSqr() > MIN_LENGTH_SQ } ?: face).normalize()
|
||||
val previous = camera.face
|
||||
if (previous == null || (previous != face && angleBetween(surface, face) <= CAMERA_FACE_SWITCH_DEGREES)) {
|
||||
camera.face = face
|
||||
}
|
||||
val up = camera.face!!
|
||||
val forward = headingOn(state, up) ?: return if (camera.started) Quaternionf(camera.spring.quat) else context.defaultBase()
|
||||
val target = RideCameraContext.frameOf(forward.toVector3f(), up.toVector3f())
|
||||
if (!camera.started) {
|
||||
camera.spring.quat.set(target)
|
||||
camera.started = true
|
||||
} else if (context.frameSeconds > 0f) {
|
||||
simpleSpringDamperQuat(camera.spring, target, CAMERA_HALFLIFE, context.frameSeconds)
|
||||
}
|
||||
return Quaternionf(camera.spring.quat)
|
||||
}
|
||||
|
||||
private fun angleBetween(a: Vec3, b: Vec3): Double = Math.toDegrees(acos(a.dot(b).coerceIn(-1.0, 1.0)))
|
||||
|
||||
/**
|
||||
* The body's heading seen from a surface with normal [up] instead of the face it's really on. It's tipped over the
|
||||
* edge between the two the shortest way, so going diagonally up a riser still reads as going diagonally along the
|
||||
* ceiling it came from. Flattening it straight onto [up] would leave only the sideways part. Null if nothing's left
|
||||
*/
|
||||
private fun headingOn(state: ClimbState, up: Vec3): Vec3? {
|
||||
val face = state.attachmentNormal.get()
|
||||
val heading = state.heading.get()
|
||||
val tipped = Vec3(Quaternionf().rotationTo(face.toVector3f().normalize(), up.toVector3f().normalize()).transform(heading.toVector3f()))
|
||||
return flatten(tipped, up)
|
||||
}
|
||||
|
||||
override fun firstPersonHandRoll(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity, context: RideCameraContext): Float {
|
||||
return context.rideRollAroundView() * HAND_ROLL_SHARE
|
||||
}
|
||||
|
||||
override fun renderHalflife(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity): Float = RENDER_HALFLIFE
|
||||
|
||||
// The model turns round the hitbox middle so on a wall its feet sink in or float off. Slide it along its up until the feet sit where the hitbox touches
|
||||
override fun renderOffset(settings: ClimbSettings, state: ClimbState, vehicle: PokemonEntity, orientation: Quaternionf): Vector3f {
|
||||
val up = orientation.transform(Vector3f(UP))
|
||||
// How far the hitbox reaches from its middle the way the feet point
|
||||
val support = (abs(up.x) + abs(up.z)) * vehicle.bbWidth * 0.5f + abs(up.y) * vehicle.bbHeight * 0.5f
|
||||
return up.mul(vehicle.bbHeight * 0.5f - support)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// Degrees either side the rider can look while standing still before the body turns with them
|
||||
var lookYawLimit = "105.0f".asExpression()
|
||||
private set
|
||||
|
||||
// Top climbing speed in blocks per tick
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 0.25, 0.1)".asExpression()
|
||||
private set
|
||||
|
||||
// Seconds from standing still to top speed
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', 0.25, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Seconds of sprinting from full stamina
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Top speed while sprinting as a multiple of normal top speed
|
||||
var sprintSpeedFactor: Expression = "1.5".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.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)
|
||||
buffer.writeExpression(lookYawLimit)
|
||||
buffer.writeExpression(sprintSpeedFactor)
|
||||
}
|
||||
|
||||
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()
|
||||
lookYawLimit = buffer.readExpression()
|
||||
sprintSpeedFactor = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class ClimbState : RidingBehaviourState() {
|
||||
var sprinting = ridingState(false, Side.CLIENT)
|
||||
// False after stamina runs dry until it refills enough to sprint again
|
||||
var sprintToggleable = ridingState(false, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
|
||||
// Climbing, all client only. The face the body is on, always one of the 6 axes
|
||||
var attachmentNormal = ridingState(Vec3(0.0, 1.0, 0.0), Side.CLIENT)
|
||||
var detachTicks = ridingState(0, Side.CLIENT)
|
||||
var jumpHeld = ridingState(false, Side.CLIENT)
|
||||
// Way W pushes, always along the face
|
||||
var heading = ridingState(Vec3.ZERO, Side.CLIENT)
|
||||
// Whether the body was on its face at the end of last tick
|
||||
var supported = ridingState(false, Side.CLIENT)
|
||||
// Surface normal the body is drawn with, and how far the drawn up has eased toward it
|
||||
var visualUp = ridingState(Vec3(0.0, 1.0, 0.0), Side.CLIENT)
|
||||
var renderUp = ridingState(Vec3.ZERO, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(sprinting.get())
|
||||
buffer.writeBoolean(inAir.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
sprinting.set(buffer.readBoolean(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
sprinting.set(false, forced = true)
|
||||
sprintToggleable.set(false, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
attachmentNormal.set(Vec3(0.0, 1.0, 0.0), forced = true)
|
||||
detachTicks.set(0, forced = true)
|
||||
jumpHeld.set(false, forced = true)
|
||||
heading.set(Vec3.ZERO, forced = true)
|
||||
supported.set(false, forced = true)
|
||||
visualUp.set(Vec3(0.0, 1.0, 0.0), forced = true)
|
||||
renderUp.set(Vec3.ZERO, 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.sprintToggleable.set(this.sprintToggleable.get(), forced = true)
|
||||
it.inAir.set(this.inAir.get(), forced = true)
|
||||
it.attachmentNormal.set(this.attachmentNormal.get(), forced = true)
|
||||
it.detachTicks.set(this.detachTicks.get(), forced = true)
|
||||
it.jumpHeld.set(this.jumpHeld.get(), forced = true)
|
||||
it.heading.set(this.heading.get(), forced = true)
|
||||
it.supported.set(this.supported.get(), forced = true)
|
||||
it.visualUp.set(this.visualUp.get(), forced = true)
|
||||
it.renderUp.set(this.renderUp.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)
|
||||
}
|
||||
}
|
||||
|
||||
/** Climb camera smoothing kept in the camera context between frames */
|
||||
class ClimbCamera {
|
||||
var started = false
|
||||
// Cube side the camera stands on
|
||||
var face: Vec3? = null
|
||||
val spring = QuatSpring()
|
||||
}
|
||||
+40
-12
@@ -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,
|
||||
|
||||
+39
-20
@@ -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
|
||||
}
|
||||
|
||||
+39
-20
@@ -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
|
||||
}
|
||||
|
||||
+34
-12
@@ -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
|
||||
}
|
||||
|
||||
+34
-12
@@ -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
|
||||
}
|
||||
|
||||
+13
-22
@@ -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
|
||||
}
|
||||
|
||||
+23
-21
@@ -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(
|
||||
|
||||
-95
@@ -1,95 +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.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.atan2
|
||||
|
||||
/**
|
||||
* What a ride gets to build its camera base from each frame.
|
||||
* One lives per ride on the client so [data] can keep a ride's own camera state between frames.
|
||||
*/
|
||||
class RideCameraContext {
|
||||
companion object {
|
||||
// Ride up this close to the view direction has no readable roll
|
||||
private const val MIN_FLAT_LENGTH_SQ = 1.0E-6f
|
||||
|
||||
/** Rotation whose forward is [forward] and up is [up]. Both unit length and perpendicular */
|
||||
@JvmStatic
|
||||
fun frameOf(forward: Vector3f, up: Vector3f): Quaternionf {
|
||||
val right = Vector3f(forward).cross(up)
|
||||
return Quaternionf().setFromNormalized(Matrix3f(right, up, Vector3f(forward).negate()))
|
||||
}
|
||||
}
|
||||
|
||||
/** Ride orientation exactly as the driver sets it */
|
||||
val rawOrientation = Quaternionf()
|
||||
/** Spring smoothed ride orientation the model and seats are drawn with */
|
||||
val renderOrientation = Quaternionf()
|
||||
/** True when the local player is the one steering */
|
||||
var isDriving = false
|
||||
private set
|
||||
/** Base the camera used last frame, null on the first frame of a ride */
|
||||
var previousBase: Quaternionf? = null
|
||||
private set
|
||||
/** Seconds since the last frame, 0 while paused */
|
||||
var frameSeconds = 0f
|
||||
private set
|
||||
var partialTicks = 0f
|
||||
private set
|
||||
/** Free slot for a ride to keep its own camera state in. Cleared when the ride changes */
|
||||
var data: Any? = null
|
||||
/** Final camera view this frame, base with the look on top */
|
||||
val view = Quaternionf()
|
||||
|
||||
/** Drivers hang straight off the ride. Passengers get the smoothed one since raw packets make it jerky */
|
||||
fun defaultBase(): Quaternionf = Quaternionf(if (isDriving) rawOrientation else renderOrientation)
|
||||
|
||||
internal fun update(raw: Quaternionf, render: Quaternionf, isDriving: Boolean, frameSeconds: Float, partialTicks: Float) {
|
||||
rawOrientation.set(raw)
|
||||
renderOrientation.set(render)
|
||||
this.isDriving = isDriving
|
||||
this.frameSeconds = frameSeconds
|
||||
this.partialTicks = partialTicks
|
||||
}
|
||||
|
||||
/** Degrees the ride's model is rolled from the camera around the view direction */
|
||||
fun rideRollAroundView(): Float {
|
||||
val forward = FORWARDS.rotate(view, Vector3f())
|
||||
val cameraUp = UP.rotate(view, Vector3f())
|
||||
val rideUp = UP.rotate(renderOrientation, Vector3f())
|
||||
rideUp.sub(Vector3f(forward).mul(rideUp.dot(forward)))
|
||||
if (rideUp.lengthSquared() < MIN_FLAT_LENGTH_SQ) return 0f
|
||||
return atan2(Vector3f(cameraUp).cross(rideUp).dot(forward), cameraUp.dot(rideUp)) * Mth.RAD_TO_DEG
|
||||
}
|
||||
|
||||
internal fun updateView(view: Quaternionf) {
|
||||
this.view.set(view)
|
||||
}
|
||||
|
||||
internal fun finishFrame(base: Quaternionf) {
|
||||
previousBase = (previousBase ?: Quaternionf()).set(base)
|
||||
}
|
||||
|
||||
internal fun reset() {
|
||||
view.identity()
|
||||
rawOrientation.identity()
|
||||
renderOrientation.identity()
|
||||
isDriving = false
|
||||
previousBase = null
|
||||
frameSeconds = 0f
|
||||
partialTicks = 0f
|
||||
data = null
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
-23
@@ -9,12 +9,8 @@
|
||||
package com.cobblemon.mod.common.api.riding.util
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.shapes.BooleanOp
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
@@ -64,22 +60,3 @@ fun collectPrimitivesFromBlocks(
|
||||
|
||||
return primitives
|
||||
}
|
||||
|
||||
/**
|
||||
* Real collision boxes in [searchBox] as [PrimitiveShape.BOX] primitives, so slabs, stairs and fences
|
||||
* keep their actual shape. Merged first so a flat wall comes out as one box with no seams to dip the field
|
||||
*/
|
||||
fun collectCollisionPrimitives(entity: Entity, searchBox: AABB): List<FieldPrimitive> {
|
||||
var merged: VoxelShape = Shapes.empty()
|
||||
for (shape in entity.level().getBlockCollisions(entity, searchBox)) {
|
||||
merged = Shapes.joinUnoptimized(merged, shape, BooleanOp.OR)
|
||||
}
|
||||
return merged.optimize().toAabbs().map {
|
||||
val center = it.center
|
||||
FieldPrimitive(
|
||||
shape = PrimitiveShape.BOX,
|
||||
centerX = center.x.toFloat(), centerY = center.y.toFloat(), centerZ = center.z.toFloat(),
|
||||
halfWidth = (it.xsize * 0.5).toFloat(), halfHeight = (it.ysize * 0.5).toFloat(), halfDepth = (it.zsize * 0.5).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -42,8 +42,8 @@ abstract class AreaTypeSpawningCondition<T : AreaSpawnablePosition> : SpawningCo
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
super.copyFrom(other, merger)
|
||||
if (other is AreaTypeSpawningCondition) {
|
||||
minHeight = merger.mergeSingle(minHeight, other.minHeight)
|
||||
maxHeight = merger.mergeSingle(maxHeight, other.maxHeight)
|
||||
merger.mergeSingle(minHeight, other.minHeight)
|
||||
merger.mergeSingle(maxHeight, other.maxHeight)
|
||||
neededNearbyBlocks = merger.merge(neededNearbyBlocks, other.neededNearbyBlocks)?.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawnablePosition> :
|
||||
super.copyFrom(other, merger)
|
||||
if (other is SubmergedTypeSpawningCondition) {
|
||||
minDepth = merger.mergeSingle(minDepth, other.minDepth)
|
||||
maxDepth = merger.mergeSingle(maxDepth, other.maxDepth)
|
||||
maxDepth = merger.mergeSingle(minDepth, other.minDepth)
|
||||
fluidIsSource = merger.mergeSingle(fluidIsSource, other.fluidIsSource)
|
||||
fluid = merger.mergeSingle(fluid, other.fluid)
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawnablePosition> : Area
|
||||
super.copyFrom(other, merger)
|
||||
if (other is SurfaceTypeSpawningCondition) {
|
||||
minDepth = merger.mergeSingle(minDepth, other.minDepth)
|
||||
maxDepth = merger.mergeSingle(maxDepth, other.maxDepth)
|
||||
maxDepth = merger.mergeSingle(minDepth, other.minDepth)
|
||||
fluid = merger.mergeSingle(fluid, other.fluid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,9 +117,6 @@ abstract class ShowdownActionResponse(val type: ShowdownActionResponseType) {
|
||||
|
||||
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null, var gimmickID: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
|
||||
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
|
||||
// Ensure that the gimmick ID the player is sending is valid
|
||||
gimmickID?.let { if (it !in VALID_GIMMICK_IDS) return false }
|
||||
|
||||
if (forceSwitch || showdownMoveSet == null) {
|
||||
return false
|
||||
}
|
||||
@@ -171,10 +168,6 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
|
||||
gimmickID = buffer.readNullable { buffer.readString() }
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val VALID_GIMMICK_IDS = ShowdownMoveset.Gimmick.entries.map { it.id }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
data class HealItemActionResponse(var item: String) : ShowdownActionResponse(ShowdownActionResponseType.FORCE_PASS) {
|
||||
@@ -190,8 +183,7 @@ data class HealItemActionResponse(var item: String) : ShowdownActionResponse(Sho
|
||||
}
|
||||
|
||||
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
|
||||
// Previously checked for !forceSwitch, excepts in the case of a forceSwitch, this couldn't legally be sent anyway.
|
||||
return false
|
||||
return !forceSwitch
|
||||
}
|
||||
|
||||
override fun toShowdownString(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?): String {
|
||||
|
||||
+2
-11
@@ -37,6 +37,7 @@ import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
||||
/**
|
||||
* Format: |move|POKEMON|MOVE|TARGET
|
||||
@@ -76,7 +77,7 @@ class MoveInstruction(
|
||||
val pokemonName = userPokemon.getName()
|
||||
ShowdownInterpreter.lastCauser[battle.battleId] = message
|
||||
// For Spread targets the message data only gives the pnx strings. So we can't know what pokemon are actually targeted until the previous messages have been interpreted
|
||||
val spreadTargetPokemon = spreadTargets.map { battle.activePokemon.firstOrNull { poke -> poke.getPNX() == it }?.battlePokemon }
|
||||
val spreadTargetPokemon = spreadTargets.map { battle.activePokemon.firstOrNull() { poke -> poke.getPNX() == it }?.battlePokemon }
|
||||
|
||||
val targetPokemonEntity = targetPokemon?.entity
|
||||
if (targetPokemonEntity != null) {
|
||||
@@ -136,20 +137,10 @@ class MoveInstruction(
|
||||
|
||||
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
|
||||
val missedTargets = subsequentInstructions.filterIsInstance<MissInstruction>().mapNotNull { it.target }
|
||||
val faintedTargets = subsequentInstructions.filterIsInstance<FaintInstruction>().map { it.faintingPokemon }
|
||||
val failed = subsequentInstructions.any { it is FailInstruction }
|
||||
val failedTargets = subsequentInstructions.filterIsInstance<FailInstruction>().mapNotNull { it.target }
|
||||
val hitCountInstruction = subsequentInstructions.filterIsInstance<HitCountInstruction>().firstOrNull()
|
||||
|
||||
runtime.environment.query.addFunction("target_fainted") { params ->
|
||||
if (params.params.size == 0) {
|
||||
return@addFunction DoubleValue(faintedTargets.isNotEmpty())
|
||||
} else {
|
||||
val entityUUID = params.getString(0)
|
||||
return@addFunction DoubleValue(faintedTargets.any { it.entity?.stringUUID == entityUUID })
|
||||
}
|
||||
}
|
||||
|
||||
runtime.environment.query.addFunction("missed") { params ->
|
||||
if (params.params.size == 0) {
|
||||
return@addFunction DoubleValue(missedTargets.isNotEmpty())
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -85,7 +85,6 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
|
||||
if (HidePartyBinding.shouldHide) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -559,4 +558,4 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,6 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
|
||||
showHorizontalMouse = false
|
||||
}
|
||||
VehicleBehaviour.KEY -> {}
|
||||
BoardBehaviour.KEY -> {}
|
||||
}
|
||||
|
||||
val centerX = minecraft.window.guiScaledWidth / 2
|
||||
|
||||
+17
-1
@@ -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 }
|
||||
|
||||
+62
-210
@@ -14,14 +14,12 @@ 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
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository.getPoser
|
||||
import com.cobblemon.mod.common.duck.CameraDuck
|
||||
import com.cobblemon.mod.common.duck.GameRendererDuck
|
||||
import com.cobblemon.mod.common.duck.RidePassenger
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
@@ -32,46 +30,23 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3d
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.math.tan
|
||||
|
||||
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
|
||||
// Camera.getMaxZoom checks this far either side on each axis, so it reads a wall as hit if it starts closer than this
|
||||
private const val VANILLA_CAMERA_PROBE = 0.1
|
||||
// Distance in front of the camera vanilla starts drawing. GameRenderer.PROJECTION_Z_NEAR
|
||||
private const val VANILLA_NEAR_PLANE = 0.05
|
||||
// Longest step of the camera sweep. Lower follows the straight line more closely round corners but costs more
|
||||
private const val CAMERA_SWEEP_STEP = 0.25
|
||||
|
||||
private var 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,
|
||||
@@ -104,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)
|
||||
|
||||
@@ -150,58 +125,24 @@ object MountedCameraRenderer {
|
||||
pivot.add(entityPos.toVector3f())
|
||||
} */
|
||||
|
||||
val offsetDistance = offset.length()
|
||||
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)
|
||||
val eyeMaxZoom: Double = getMaxZoom(eyeOffsetDistance, eyeOffsetDirection, eyePos, instance)
|
||||
|
||||
val playerRotater = driver as RidePassenger
|
||||
playerRotater.`cobblemon$setRideEyePos`(eyeOffsetDirection.scale(eyeMaxZoom).add(eyePos))
|
||||
// The seat can swing into a wall round corners so clipping from it isn't safe. Come out from the hitbox instead
|
||||
return sweepFromBody(vehicle, entityPos, offset.add(pos), nearPlaneReach(instance))
|
||||
}
|
||||
|
||||
/**
|
||||
* How far the corners of the near plane are from the camera. Walls closer than this get drawn through
|
||||
* at the edges of the screen, and it grows with the FOV and how wide the window is
|
||||
*/
|
||||
private fun nearPlaneReach(instance: Camera): Double {
|
||||
val minecraft = Minecraft.getInstance()
|
||||
val fov = (minecraft.gameRenderer as GameRendererDuck).`cobblemon$getFov`(instance, instance.partialTickTime)
|
||||
val window = minecraft.window
|
||||
val aspect = window.width.toDouble() / max(1, window.height)
|
||||
val halfHeight = VANILLA_NEAR_PLANE * tan(Math.toRadians(fov) * 0.5)
|
||||
val halfWidth = halfHeight * aspect
|
||||
return sqrt(VANILLA_NEAR_PLANE * VANILLA_NEAR_PLANE + halfHeight * halfHeight + halfWidth * halfWidth)
|
||||
}
|
||||
|
||||
/**
|
||||
* Slides a small box from the middle of the ride's hitbox out to [target] and returns where it ends up.
|
||||
* The hitbox never sits in blocks so this never starts inside a wall. The box is big enough that the near
|
||||
* plane can't reach a wall from its middle and vanilla's zoom clipping can't start inside one
|
||||
*/
|
||||
private fun sweepFromBody(vehicle: Entity, lerpedPos: Vec3, target: Vec3, nearPlaneReach: Double): Vec3 {
|
||||
val level = vehicle.level()
|
||||
val halfSize = max(VANILLA_CAMERA_PROBE, nearPlaneReach)
|
||||
var start = lerpedPos.add(0.0, vehicle.bbHeight * 0.5, 0.0)
|
||||
// Lerping between ticks can cut across a corner the ride went round, so fall back to where it really is
|
||||
if (level.getBlockCollisions(null, AABB(start, start).inflate(halfSize)).any()) start = vehicle.boundingBox.center
|
||||
var box = AABB(start, start).inflate(halfSize)
|
||||
val path = target.subtract(start)
|
||||
// Small steps so it slides along the straight line instead of going one axis at a time
|
||||
val steps = max(1, ceil(path.length() / CAMERA_SWEEP_STEP).toInt())
|
||||
val step = path.scale(1.0 / steps)
|
||||
repeat(steps) {
|
||||
box = box.move(Entity.collideBoundingBox(null, step, box, level, emptyList()))
|
||||
}
|
||||
return box.center
|
||||
return offsetDirection.scale(maxZoom).add(pos)
|
||||
}
|
||||
|
||||
// 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()
|
||||
@@ -211,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
|
||||
@@ -221,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
|
||||
}
|
||||
}
|
||||
@@ -243,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(),
|
||||
@@ -304,114 +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 lookSettings = ride?.let { pokemon ->
|
||||
pokemon.ifRidingAvailableSupply(RideLookSettings.DEFAULT) { behaviour, settings, rideState ->
|
||||
behaviour.lookSettings(settings, rideState, pokemon)
|
||||
}
|
||||
} ?: RideLookSettings.DEFAULT
|
||||
val previousBase = context.previousBase
|
||||
// Full carry is exactly what the look angles already do so skip the work
|
||||
if (lookSettings.carry < RideLookSettings.FULL_CARRY && previousBase != null) {
|
||||
val oldYaw = playerRotater.`cobblemon$getRideYRot`()
|
||||
val oldPitch = playerRotater.`cobblemon$getRideXRot`()
|
||||
carryLook(playerRotater, previousBase, newRotation, lookSettings.carry)
|
||||
// The base turning drags the look along at its limits but never pushes it further out than it was
|
||||
playerRotater.`cobblemon$setRideYRot`(clampOutward(playerRotater.`cobblemon$getRideYRot`(), oldYaw, lookSettings.maxYaw))
|
||||
playerRotater.`cobblemon$setRideXRot`(clampOutward(playerRotater.`cobblemon$getRideXRot`(), oldPitch, lookSettings.maxPitch))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
/** Keeps [angle] within [limit] either side, or within [previous] if that was already further out */
|
||||
private fun clampOutward(angle: Float, previous: Float, limit: Float): Float {
|
||||
val bound = max(limit, abs(previous))
|
||||
return angle.coerceIn(-bound, bound)
|
||||
}
|
||||
|
||||
/** Look as a rotation relative to the base. */
|
||||
private fun lookRotation(rotater: RidePassenger): Quaternionf = Quaternionf().rotationYXZ(
|
||||
-Mth.DEG_TO_RAD * rotater.`cobblemon$getRideYRot`(),
|
||||
-Mth.DEG_TO_RAD * rotater.`cobblemon$getRideXRot`(),
|
||||
0.0f
|
||||
)
|
||||
|
||||
/** Look angles that point along [local], a direction in the base's frame. Inverse of [lookRotation]. */
|
||||
private fun setLookFromLocal(rotater: RidePassenger, local: Vector3f) {
|
||||
rotater.`cobblemon$setRideXRot`(-asin(local.y.coerceIn(-1f, 1f)) * Mth.RAD_TO_DEG)
|
||||
rotater.`cobblemon$setRideYRot`(atan2(local.x, -local.z) * Mth.RAD_TO_DEG)
|
||||
}
|
||||
|
||||
/**
|
||||
* Points [player] along [view] so picking, bows and other clients follow the camera.
|
||||
* Yaw stays continuous so partial tick lerps never sweep through a 360 wrap.
|
||||
*/
|
||||
private fun lookAlong(player: Entity, view: Quaternionf) {
|
||||
val forward = FORWARDS.rotate(view, Vector3f())
|
||||
val pitch = pitchOf(forward)
|
||||
val yaw = player.yRot + Mth.wrapDegrees(yawOf(forward, player.yRot) - player.yRot)
|
||||
// Shift the old values by the same amount like vanilla turn does so the lerped view vector doesn't trail a tick behind
|
||||
player.xRotO = Mth.clamp(player.xRotO + pitch - player.xRot, -VANILLA_MAX_PITCH, VANILLA_MAX_PITCH)
|
||||
player.yRotO += yaw - player.yRot
|
||||
player.xRot = pitch
|
||||
player.yRot = yaw
|
||||
}
|
||||
|
||||
/** Vanilla style pitch in degrees for a forward vector. */
|
||||
private fun pitchOf(forward: Vector3f): Float = -asin(forward.y.coerceIn(-1f, 1f)) * Mth.RAD_TO_DEG
|
||||
|
||||
/** Vanilla style yaw in degrees for a forward vector, or [fallback] when looking straight up or down. */
|
||||
private fun yawOf(forward: Vector3f, fallback: Float): Float {
|
||||
if (forward.x * forward.x + forward.z * forward.z < MIN_HORIZONTAL_LENGTH_SQ) return fallback
|
||||
return atan2(-forward.x, forward.z) * Mth.RAD_TO_DEG
|
||||
// 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(
|
||||
@@ -421,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()
|
||||
|
||||
@@ -437,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
|
||||
@@ -473,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")) {
|
||||
@@ -498,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) {
|
||||
@@ -532,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`())
|
||||
}
|
||||
}
|
||||
-55
@@ -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()
|
||||
}
|
||||
}
|
||||
+1
-6
@@ -770,13 +770,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
val controller = entity.orientationController
|
||||
val transformationMatrix = Matrix4f()
|
||||
val center = Vector3f(0f, entity.bbHeight/2, 0f)
|
||||
val orientation = controller.getRenderOrientation(state.getPartialTicks())
|
||||
// Same shift the renderer gives the model so seats stay on it
|
||||
transformationMatrix.translate(entity.ifRidingAvailableSupply(fallback = Vector3f()) { behaviour, settings, rideState ->
|
||||
behaviour.renderOffset(settings, rideState, entity, orientation)
|
||||
})
|
||||
transformationMatrix.translate(center)
|
||||
transformationMatrix.rotate(orientation)
|
||||
transformationMatrix.rotate(controller.getRenderOrientation(state.getPartialTicks()))
|
||||
transformationMatrix.translate(center.negate())
|
||||
matrixStack.mulPose(transformationMatrix)
|
||||
} else {
|
||||
|
||||
+2
-2
@@ -34,7 +34,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animatio
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.QuirkData
|
||||
import com.cobblemon.mod.common.client.sound.instances.UIEntitySoundInstance
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -45,6 +44,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.multiplayer.ClientLevel
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.commands.arguments.item.ItemParser
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.entity.Entity
|
||||
@@ -289,7 +289,7 @@ abstract class PosableState : Schedulable {
|
||||
entity.level().playLocalSound(entity, soundEvent, entity.soundSource, volume, pitch)
|
||||
}
|
||||
} else {
|
||||
Minecraft.getInstance().soundManager.play(UIEntitySoundInstance(soundEvent, volume, pitch))
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, volume, pitch))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.client.sound.instances.AlphaCrySoundInstance
|
||||
import com.cobblemon.mod.common.client.sound.instances.UIEntitySoundInstance
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.effectiveName
|
||||
import com.cobblemon.mod.common.util.genericRuntime
|
||||
@@ -32,6 +31,7 @@ import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.client.multiplayer.ClientLevel
|
||||
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.world.entity.Entity
|
||||
@@ -121,7 +121,7 @@ class BedrockSoundKeyframe(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Minecraft.getInstance().soundManager.play(UIEntitySoundInstance(soundEvent))
|
||||
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1F, 1F))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@ class FarfetchdGalarianModel(root: ModelPart) : PokemonPosableModel(root), Heade
|
||||
override var portraitScale = 2.6F
|
||||
override var portraitTranslation = Vec3(-0.2, -1.0, 0.0)
|
||||
|
||||
override var profileScale = 0.93F
|
||||
override var profileTranslation = Vec3(-0.1, 0.36, 0.0)
|
||||
override var profileScale = 1.1F
|
||||
override var profileTranslation = Vec3(-0.1, 0.1, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
+2
-2
@@ -28,8 +28,8 @@ class FarfetchdModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame,
|
||||
override var portraitScale = 2.6F
|
||||
override var portraitTranslation = Vec3(-0.2, -1.0, 0.0)
|
||||
|
||||
override var profileScale = 0.93F
|
||||
override var profileTranslation = Vec3(-0.1, 0.36, 0.0)
|
||||
override var profileScale = 1.1F
|
||||
override var profileTranslation = Vec3(-0.1, 0.1, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
+2
-2
@@ -23,8 +23,8 @@ class KabutoModel(root: ModelPart) : PokemonPosableModel(root) {
|
||||
override var portraitScale = 3.3F
|
||||
override var portraitTranslation = Vec3(0.0, -3.3, 0.0)
|
||||
|
||||
override var profileScale = 1.25F
|
||||
override var profileTranslation = Vec3(0.0, -0.17, 0.0)
|
||||
override var profileScale = 1.6F
|
||||
override var profileTranslation = Vec3(0.0, -0.6, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ class HoothootModel (root: ModelPart) : PokemonPosableModel(root) {
|
||||
|
||||
override var portraitScale = 2.2F
|
||||
override var portraitTranslation = Vec3(-0.25, -0.9, 0.0)
|
||||
override var profileScale = 0.82F
|
||||
override var profileTranslation = Vec3(0.0, 0.53, 0.0)
|
||||
override var profileScale = 1.0F
|
||||
override var profileTranslation = Vec3(0.0, 0.3, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@ class WooperModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override var portraitScale = 2.0F
|
||||
override var portraitTranslation = Vec3(-0.15, -0.65, 0.0)
|
||||
|
||||
override var profileScale = 0.75F
|
||||
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
|
||||
override var profileScale = 0.9F
|
||||
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
|
||||
|
||||
lateinit var shoulderLeft: Pose
|
||||
lateinit var shoulderRight: Pose
|
||||
|
||||
+2
-2
@@ -29,8 +29,8 @@ class WooperPaldeanModel(root: ModelPart) : PokemonPosableModel(root), HeadedFra
|
||||
override var portraitScale = 2.0F
|
||||
override var portraitTranslation = Vec3(-0.15, -0.65, 0.0)
|
||||
|
||||
override var profileScale = 0.75F
|
||||
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
|
||||
override var profileScale = 0.9F
|
||||
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
|
||||
|
||||
lateinit var shoulderLeft: Pose
|
||||
lateinit var shoulderRight: Pose
|
||||
|
||||
-3
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
@@ -29,8 +28,6 @@ class MasquerainModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame
|
||||
lateinit var walk: Pose
|
||||
lateinit var standing: Pose
|
||||
override fun registerPoses() {
|
||||
animations["cry"] = "q.bedrock_stateful('masquerain', 'cry')".asExpressionLike()
|
||||
|
||||
val blink = quirk { bedrockStateful("masquerain", "blink") }
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
|
||||
-3
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.isInWater
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
@@ -37,8 +36,6 @@ class SurskitModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
val wateroffset = -8
|
||||
|
||||
override fun registerPoses() {
|
||||
animations["cry"] = "q.bedrock_stateful('surskit', 'cry')".asExpressionLike()
|
||||
|
||||
val blink = quirk { bedrockStateful("surskit", "blink") }
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
|
||||
-3
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
@@ -37,8 +36,6 @@ class CombeeModel (root: ModelPart) : PokemonPosableModel(root) {
|
||||
val shoulderZOffset = 8
|
||||
|
||||
override fun registerPoses() {
|
||||
animations["cry"] = "q.bedrock_stateful('combee', 'cry')".asExpressionLike()
|
||||
|
||||
val blink1 = quirk { bedrockStateful("combee", "blink_right") }
|
||||
val blink2 = quirk { bedrockStateful("combee", "blink_gender") }
|
||||
val blink3 = quirk { bedrockStateful("combee", "blink_left") }
|
||||
|
||||
-3
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
@@ -31,8 +30,6 @@ class VespiquenModel (root: ModelPart) : PokemonPosableModel(root) {
|
||||
lateinit var standing: Pose
|
||||
|
||||
override fun registerPoses() {
|
||||
animations["cry"] = "q.bedrock_stateful('vespiquen', 'cry')".asExpressionLike()
|
||||
|
||||
val blink1 = quirk { bedrockStateful("vespiquen", "blink") }
|
||||
val wingsleep = quirk { bedrockStateful("vespiquen", "sleep_flap") }
|
||||
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class SerperiorModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("serperior")
|
||||
override val head = getPart("head_ai")
|
||||
|
||||
override var portraitScale = 2.5F
|
||||
override var portraitTranslation = Vec3(-0.3, 1.25, 0.0)
|
||||
|
||||
override var profileScale = 0.6F
|
||||
override var profileTranslation = Vec3(0.0, 0.8, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var sleep: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("serperior", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("serperior", "blink") }
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(bedrock("serperior", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("serperior", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("serperior", "ground_walk")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ServineModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("servine")
|
||||
override val head = getPart("head_ai")
|
||||
override val rightArm = getPart("arm_right")
|
||||
override val leftArm = getPart("arm_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
|
||||
override var portraitScale = 2.6F
|
||||
override var portraitTranslation = Vec3(-0.15, 0.75, 0.0)
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
|
||||
|
||||
lateinit var sleep: Pose
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("servine", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("servine", "blink") }
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(bedrock("servine", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("servine", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walking",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("servine", "ground_walk")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class SnivyModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("snivy")
|
||||
override val head = getPart("head_ai")
|
||||
override val rightArm = getPart("arm_right")
|
||||
override val leftArm = getPart("arm_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
|
||||
override var portraitScale = 2.0F
|
||||
override var portraitTranslation = Vec3(-0.15, 0.32, 0.0)
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
|
||||
|
||||
lateinit var sleep: Pose
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("snivy", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("snivy", "blink") }
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(bedrock("snivy", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("snivy", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walking",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
transformTicks = 10,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("snivy", "ground_walk")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@ class CarbinkModel (root: ModelPart) : PokemonPosableModel(root) {
|
||||
override var portraitScale = 1.8F
|
||||
override var portraitTranslation = Vec3(0.0, 0.2, 0.0)
|
||||
|
||||
override var profileScale = 0.68F
|
||||
override var profileScale = 0.85F
|
||||
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen6
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class GourgeistModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("gourgeist")
|
||||
override val head = getPart("head")
|
||||
|
||||
override var portraitScale = 4.0F
|
||||
override var portraitTranslation = Vec3(-0.1, 0.6, 0.0)
|
||||
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var hovering: Pose
|
||||
lateinit var fly: Pose
|
||||
lateinit var sleep: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("gourgeist", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("gourgeist", "blink") }
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.UI_POSES + PoseType.STAND,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("gourgeist", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseType = PoseType.WALK,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("gourgeist", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
hovering = registerPose(
|
||||
poseName = "hovering",
|
||||
poseType = PoseType.HOVER,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("gourgeist", "air_idle")
|
||||
)
|
||||
)
|
||||
|
||||
fly = registerPose(
|
||||
poseName = "fly",
|
||||
poseType = PoseType.FLY,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("gourgeist", "air_fly")
|
||||
)
|
||||
)
|
||||
|
||||
sleep = registerPose(
|
||||
poseName = "sleep",
|
||||
poseType = PoseType.SLEEP,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("gourgeist", "sleep")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen6
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PumpkabooModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("pumpkaboo")
|
||||
override val head = getPart("head")
|
||||
|
||||
override var portraitScale = 3.5F
|
||||
override var portraitTranslation = Vec3(0.0, -1.5, 0.0)
|
||||
|
||||
override var profileScale = 1.0F
|
||||
override var profileTranslation = Vec3(0.0, 0.25, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
lateinit var battleidle: CobblemonPose
|
||||
lateinit var sleep: CobblemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("pumpkaboo", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("pumpkaboo", "blink") }
|
||||
val quirk = quirk { bedrockStateful("pumpkaboo", "quirk_idle") }
|
||||
|
||||
sleep = registerPose(
|
||||
poseName = "sleep",
|
||||
poseType = PoseType.SLEEP,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("pumpkaboo", "sleep")
|
||||
)
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
quirks = arrayOf(blink, quirk),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("pumpkaboo", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("pumpkaboo", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
battleidle = registerPose(
|
||||
poseName = "battleidle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("pumpkaboo", "battle_idle")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -26,8 +26,8 @@ class ScatterbugModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
|
||||
override var portraitScale = 2.6F
|
||||
override var portraitTranslation = Vec3(-0.2, -1.9, 0.0)
|
||||
|
||||
override var profileScale = 0.98F
|
||||
override var profileTranslation = Vec3(0.0, 0.28, 0.0)
|
||||
override var profileScale = 1.2F
|
||||
override var profileTranslation = Vec3(0.0, 0.05, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
|
||||
+2
-2
@@ -36,8 +36,8 @@ class CharcadetModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame,
|
||||
override var portraitScale = 1.7F
|
||||
override var portraitTranslation = Vec3(-0.15, 0.08, 0.0)
|
||||
|
||||
override var profileScale = 0.58F
|
||||
override var profileTranslation = Vec3(0.0, 0.91, 0.0)
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walking: CobblemonPose
|
||||
|
||||
+2
-2
@@ -35,8 +35,8 @@ class QuaquavalModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame,
|
||||
override var portraitScale = 2.3F
|
||||
override var portraitTranslation = Vec3(-0.42, 3.7, 0.0)
|
||||
|
||||
override var profileScale = 0.36F
|
||||
override var profileTranslation = Vec3(0.0, 1.19, 0.0)
|
||||
override var profileScale = 0.32F
|
||||
override var profileTranslation = Vec3(0.0, 1.24, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var standing2: CobblemonPose
|
||||
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen9
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.parabolaFunction
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class SquawkabillyModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BiWingedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("squawkabilly")
|
||||
override val head = getPart("head")
|
||||
override val leftWing = getPart("wing_left")
|
||||
override val rightWing = getPart("wing_right")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
private val tail = getPart("tail")
|
||||
|
||||
override var portraitScale = 2.0F
|
||||
override var portraitTranslation = Vec3(-0.2, -0.2, 0.0)
|
||||
|
||||
override var profileScale = 0.85F
|
||||
override var profileTranslation = Vec3(0.0, 0.51, 0.0)
|
||||
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var hovering: Pose
|
||||
lateinit var flying: Pose
|
||||
lateinit var shoulderLeft: Pose
|
||||
lateinit var shoulderRight: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("squawkabilly", "cry") }
|
||||
|
||||
val shoulderOffsetX = -1.0
|
||||
val shoulderOffsetY = 0
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("squawkabilly", "blink") }
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
quirks = arrayOf(blink),
|
||||
poseTypes = PoseType.UI_POSES + PoseType.STAND,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("squawkabilly", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
quirks = arrayOf(blink),
|
||||
poseType = PoseType.WALK,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("squawkabilly", "ground_idle"),
|
||||
rootPart.translation(
|
||||
function = parabolaFunction(
|
||||
peak = -4F,
|
||||
period = 0.4F
|
||||
),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Y_AXIS
|
||||
),
|
||||
head.translation(
|
||||
function = sineFunction(
|
||||
amplitude = (-20F).toRadians(),
|
||||
period = 1F,
|
||||
verticalShift = (-10F).toRadians()
|
||||
),
|
||||
axis = ModelPartTransformation.X_AXIS,
|
||||
timeVariable = { state, _, _ -> state.animationSeconds }
|
||||
),
|
||||
leftLeg.rotation(
|
||||
function = parabolaFunction(
|
||||
tightness = -20F,
|
||||
phaseShift = 0F,
|
||||
verticalShift = (30F).toRadians()
|
||||
),
|
||||
axis = ModelPartTransformation.X_AXIS,
|
||||
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
|
||||
),
|
||||
rightLeg.rotation(
|
||||
function = parabolaFunction(
|
||||
tightness = -20F,
|
||||
phaseShift = 0F,
|
||||
verticalShift = (30F).toRadians()
|
||||
),
|
||||
axis = ModelPartTransformation.X_AXIS,
|
||||
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
|
||||
),
|
||||
tail.rotation(
|
||||
function = sineFunction(
|
||||
amplitude = (-5F).toRadians(),
|
||||
period = 1F
|
||||
),
|
||||
axis = ModelPartTransformation.X_AXIS,
|
||||
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
|
||||
),
|
||||
wingFlap(
|
||||
flapFunction = sineFunction(
|
||||
amplitude = (-5F).toRadians(),
|
||||
period = 0.4F,
|
||||
phaseShift = 0.00F,
|
||||
verticalShift = (-20F).toRadians()
|
||||
),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Z_AXIS
|
||||
),
|
||||
rightWing.translation(
|
||||
function = parabolaFunction(
|
||||
tightness = -10F,
|
||||
phaseShift = 30F,
|
||||
verticalShift = (25F).toRadians()
|
||||
),
|
||||
axis = ModelPartTransformation.Y_AXIS,
|
||||
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
|
||||
),
|
||||
leftWing.translation(
|
||||
function = parabolaFunction(
|
||||
tightness = -10F,
|
||||
phaseShift = 30F,
|
||||
verticalShift = (25F).toRadians()
|
||||
),
|
||||
axis = ModelPartTransformation.Y_AXIS,
|
||||
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
hovering = registerPose(
|
||||
poseName = "hovering",
|
||||
quirks = arrayOf(blink),
|
||||
poseTypes = setOf(PoseType.FLOAT, PoseType.HOVER),
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.9F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Z_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
flying = registerPose(
|
||||
poseName = "flying",
|
||||
quirks = arrayOf(blink),
|
||||
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
WingFlapIdleAnimation(this,
|
||||
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.9F),
|
||||
timeVariable = { state, _, _ -> state.animationSeconds },
|
||||
axis = ModelPartTransformation.Z_AXIS
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
shoulderLeft = registerPose(
|
||||
poseType = PoseType.SHOULDER_LEFT,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("squawkabilly", "ground_idle")
|
||||
),
|
||||
transformedParts = arrayOf(
|
||||
rootPart.createTransformation().addPosition(shoulderOffsetX, shoulderOffsetY, 0.0)
|
||||
)
|
||||
)
|
||||
|
||||
shoulderRight = registerPose(
|
||||
poseType = PoseType.SHOULDER_RIGHT,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("squawkabilly", "ground_idle")
|
||||
),
|
||||
transformedParts = arrayOf(
|
||||
rootPart.createTransformation().addPosition(-shoulderOffsetX, shoulderOffsetY, 0.0)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isNotPosedIn(sleep)) bedrockStateful("squawkabilly", "faint") else null
|
||||
}
|
||||
+6
@@ -322,6 +322,8 @@ object VaryingModelRepository {
|
||||
inbuilt("loudred", :: LoudredModel)
|
||||
inbuilt("exploud", :: ExploudModel)
|
||||
inbuilt("cryogonal", :: CryogonalModel)
|
||||
inbuilt("pumpkaboo", :: PumpkabooModel)
|
||||
inbuilt("gourgeist", :: GourgeistModel)
|
||||
inbuilt("eiscue", :: EiscueModel)
|
||||
inbuilt("wooloo", :: WoolooModel)
|
||||
inbuilt("dubwool", :: DubwoolModel)
|
||||
@@ -338,6 +340,9 @@ object VaryingModelRepository {
|
||||
inbuilt("thwackey", ::ThwackeyModel)
|
||||
inbuilt("rillaboom", ::RillaboomModel)
|
||||
inbuilt("samurott", ::SamurottModel)
|
||||
inbuilt("snivy", ::SnivyModel)
|
||||
inbuilt("servine", ::ServineModel)
|
||||
inbuilt("serperior", ::SerperiorModel)
|
||||
inbuilt("slugma", ::SlugmaModel)
|
||||
inbuilt("magcargo", ::MagcargoModel)
|
||||
inbuilt("slugma_shiny", ::SlugmaShinyModel)
|
||||
@@ -397,6 +402,7 @@ object VaryingModelRepository {
|
||||
inbuilt("bunnelby", ::BunnelbyModel)
|
||||
inbuilt("arrokuda", ::ArrokudaModel)
|
||||
inbuilt("barraskewda", ::BarraskewdaModel)
|
||||
inbuilt("squawkabilly", ::SquawkabillyModel)
|
||||
inbuilt("ponyta_galarian", ::PonytaGalarianModel)
|
||||
inbuilt("rapidash_galarian", ::RapidashGalarianModel)
|
||||
inbuilt("volbeat", ::VolbeatModel)
|
||||
|
||||
-32
@@ -19,16 +19,9 @@ import net.minecraft.client.model.AnimationUtils
|
||||
import net.minecraft.client.model.HumanoidModel
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.client.player.AbstractClientPlayer
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.HumanoidArm
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.hypot
|
||||
|
||||
/**
|
||||
* @author landonjw
|
||||
@@ -37,9 +30,6 @@ object MountedPlayerRenderer {
|
||||
@JvmField
|
||||
var shouldApplyRootAnimation = false
|
||||
|
||||
// Furthest a passenger's head turns from its body either way. From LivingEntityRenderer.render
|
||||
private const val VANILLA_PASSENGER_HEAD_YAW_LIMIT = 85f
|
||||
|
||||
@JvmField
|
||||
val relevantPartsByName = mutableMapOf(
|
||||
"body" to ModelPart(emptyList(), emptyMap()),
|
||||
@@ -150,28 +140,6 @@ object MountedPlayerRenderer {
|
||||
model.hat.copyFrom(model.head)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rider head yaw and pitch in degrees like vanilla hands them to setupAnim, but measured against the seat
|
||||
* the body is drawn on instead of world yaw. Null if the rider has no seat locator
|
||||
*/
|
||||
@JvmStatic
|
||||
fun riderHeadAngles(pokemon: PokemonEntity, rider: LivingEntity, partialTicks: Float): Vec2? {
|
||||
val delegate = pokemon.delegate as? PokemonClientDelegate ?: return null
|
||||
val locator = delegate.getSeatLocator(rider)?.let { delegate.locatorStates[it] } ?: return null
|
||||
// MountedLivingEntityRenderer cancels the body yaw so the seat's rotation is the whole body frame
|
||||
val seat = locator.matrix.getNormalizedRotation(Quaternionf())
|
||||
val forward = seat.transform(Vector3f(0f, 0f, -1f))
|
||||
val up = seat.transform(Vector3f(0f, 1f, 0f))
|
||||
// Way a positive head yaw turns
|
||||
val side = Vector3f(forward).cross(up)
|
||||
val view = rider.getViewVector(partialTicks).toVector3f()
|
||||
val ahead = view.dot(forward)
|
||||
val across = view.dot(side)
|
||||
val yaw = atan2(across, ahead) * Mth.RAD_TO_DEG
|
||||
val pitch = atan2(-view.dot(up), hypot(across, ahead)) * Mth.RAD_TO_DEG
|
||||
return Vec2(yaw.coerceIn(-VANILLA_PASSENGER_HEAD_YAW_LIMIT, VANILLA_PASSENGER_HEAD_YAW_LIMIT), pitch)
|
||||
}
|
||||
|
||||
const val RIDING_SITTING_OFFSET = -10.0
|
||||
fun animateRoot(stack: PoseStack) {
|
||||
val root = relevantPartsByName["body"] ?: return
|
||||
|
||||
+1
-6
@@ -199,16 +199,11 @@ class PokemonRenderer(
|
||||
val matrix = poseMatrix.last().pose()
|
||||
val yaw = Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot)
|
||||
val center = Vector3f(0f, entity.bbHeight/2, 0f)
|
||||
val orientation = controller.getRenderOrientation(partialTicks)
|
||||
val transformationMatrix = Matrix4f()
|
||||
// Lets the ride slide the model onto what it's standing on
|
||||
transformationMatrix.translate(entity.ifRidingAvailableSupply(fallback = Vector3f()) { behaviour, settings, state ->
|
||||
behaviour.renderOffset(settings, state, entity, orientation)
|
||||
})
|
||||
//Move origin to center of Pokemon
|
||||
transformationMatrix.translate(center)
|
||||
|
||||
transformationMatrix.rotate(orientation)
|
||||
transformationMatrix.rotate(controller.getRenderOrientation(partialTicks))
|
||||
//Move origin to base of the entity
|
||||
transformationMatrix.translate(center.negate(Vector3f()))
|
||||
|
||||
|
||||
-22
@@ -1,22 +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.sound.instances
|
||||
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.resources.sounds.SoundInstance
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import net.minecraft.sounds.SoundSource
|
||||
|
||||
/**
|
||||
* Plays a sound for a Pokémon rendered in a GUI, where there is no entity to take the sound source from.
|
||||
* Unlike [SimpleSoundInstance.forUI] this uses [SoundSource.NEUTRAL] so the Friendly Creatures slider applies.
|
||||
*/
|
||||
class UIEntitySoundInstance(sound: SoundEvent, volume: Float = 1F, pitch: Float = 1F) :
|
||||
SimpleSoundInstance(sound.location, SoundSource.NEUTRAL, volume, pitch, SoundInstance.createUnseededRandom(),
|
||||
false, 0, SoundInstance.Attenuation.NONE, 0.0, 0.0, 0.0, true)
|
||||
@@ -164,9 +164,6 @@ class CobblemonConfig {
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "experience_multiplier", SERVER)
|
||||
var experienceMultiplier = 2F
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "predation_chance", SERVER)
|
||||
var predationChance = 0.05F
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "pokemon_per_chunk", SERVER)
|
||||
var pokemonPerChunk = 1F
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -188,8 +189,6 @@ open class PokemonEntity(
|
||||
type: EntityType<out PokemonEntity> = CobblemonEntities.POKEMON,
|
||||
) : ShoulderRidingEntity(type, world), PosableEntity, Shearable, Schedulable, Rideable, ScannableEntity, MoLangScriptingEntity, OmniPathingEntity {
|
||||
companion object {
|
||||
// Squared blocks the server's replay of a rider's move can end up from where the rider landed before it counts as a different spot
|
||||
private const val LANDING_MISMATCH_SQ = 1.0E-7
|
||||
@JvmStatic val SPECIES = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.STRING)
|
||||
@JvmStatic val NICKNAME = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.COMPONENT)
|
||||
@JvmStatic val NICKNAME_VISIBLE = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.BOOLEAN)
|
||||
@@ -1295,7 +1294,16 @@ open class PokemonEntity(
|
||||
}
|
||||
|
||||
private fun showInteractionWheel(player: ServerPlayer, itemStack: ItemStack) {
|
||||
val canRide = isRideableFor(player)
|
||||
val canRide = ifRidingAvailableSupply(false) { behaviour, settings, state ->
|
||||
if (platform != PlatformType.NONE) return@ifRidingAvailableSupply false
|
||||
if (tethering != null) return@ifRidingAvailableSupply false;
|
||||
if (seats.isEmpty()) return@ifRidingAvailableSupply false;
|
||||
if ((owner as? ServerPlayer)?.isInBattle() == true) return@ifRidingAvailableSupply false;
|
||||
if (this.owner != player && this.passengers.isEmpty()) return@ifRidingAvailableSupply false;
|
||||
if(pokemon.effectiveScale < Cobblemon.config.minimumRidingScale) return@ifRidingAvailableSupply false;
|
||||
if(beamMode == 3) return@ifRidingAvailableSupply false;
|
||||
return@ifRidingAvailableSupply behaviour.isActive(settings, state, this);
|
||||
}
|
||||
if (pokemon.getOwnerPlayer() == player) {
|
||||
val cosmeticItemDefinition = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(
|
||||
player.level().registryAccess(),
|
||||
@@ -1317,18 +1325,6 @@ open class PokemonEntity(
|
||||
}
|
||||
}
|
||||
|
||||
fun isRideableFor(player: ServerPlayer): Boolean = ifRidingAvailableSupply(false) canRide@{ behaviour, settings, state ->
|
||||
if (!player.canInteractWithEntity(this, player.entityInteractionRange())) return@canRide false
|
||||
if (platform != PlatformType.NONE) return@canRide false
|
||||
if (tethering != null) return@canRide false
|
||||
if (seats.isEmpty()) return@canRide false
|
||||
if ((owner as? ServerPlayer)?.isInBattle() == true) return@canRide false
|
||||
if (this.owner != player && this.passengers.isEmpty()) return@canRide false
|
||||
if (pokemon.effectiveScale < Cobblemon.config.minimumRidingScale) return@canRide false
|
||||
if (beamMode == 3) return@canRide false
|
||||
return@canRide behaviour.isActive(settings, state, this)
|
||||
}
|
||||
|
||||
override fun getDimensions(pose: Pose): EntityDimensions {
|
||||
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.effectiveScale)
|
||||
var result = this.exposedForm.hitbox.scale(scale)
|
||||
@@ -1610,8 +1606,9 @@ open class PokemonEntity(
|
||||
|| stack.`is`(CobblemonItemTags.WHITELISTED_ITEMS_TO_HOLD)
|
||||
|
||||
fun tryRidingPokemon(player: ServerPlayer): Boolean {
|
||||
if (!isRideableFor(player)) return false
|
||||
|
||||
if (pokemon.effectiveScale < Cobblemon.config.minimumRidingScale) {
|
||||
return false
|
||||
}
|
||||
val event = RidePokemonEvent.Pre(player, this)
|
||||
CobblemonEvents.RIDE_EVENT_PRE.post(event)
|
||||
if (!event.isCanceled) {
|
||||
@@ -1867,9 +1864,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
|
||||
}
|
||||
|
||||
@@ -1891,15 +1907,7 @@ open class PokemonEntity(
|
||||
this.fallDistance = 1.0F
|
||||
}
|
||||
}
|
||||
// The server replays the rider's move for the tick as one straight line, which can't follow a path round an edge
|
||||
val landing = if (type == MoverType.PLAYER && !level().isClientSide) position().add(pos) else null
|
||||
super.move(type, pos)
|
||||
if (landing != null && position().distanceToSqr(landing) > LANDING_MISMATCH_SQ &&
|
||||
ifRidingAvailableSupply(fallback = false) { behaviour, settings, state -> behaviour.serverTrustsLanding(settings, state, this) } &&
|
||||
level().noCollision(this, boundingBox.move(landing.subtract(position())))
|
||||
) {
|
||||
setPos(landing)
|
||||
}
|
||||
}
|
||||
|
||||
override fun travel(movementInput: Vec3) {
|
||||
@@ -1910,46 +1918,44 @@ open class PokemonEntity(
|
||||
if (!this.hasRider() || this.controllingPassenger == null) {
|
||||
super.travel(movementInput)
|
||||
} else {
|
||||
// Some rides move themselves, like climbers going over every surface
|
||||
val travelled = ifRidingAvailableSupply(fallback = false) { behaviour, settings, state ->
|
||||
behaviour.travel(settings, state, this, this.controllingPassenger as Player)
|
||||
val inp = ifRidingAvailableSupply(fallback = Vec3.ZERO) { behaviour, settings, state ->
|
||||
behaviour.velocity(settings, state, this, this.controllingPassenger as Player, deltaMovement)
|
||||
}
|
||||
if (!travelled) {
|
||||
// movementInput is the world space velocity getRiddenInput already got from the ride this tick
|
||||
val v = ifRidingAvailableSupply(fallback = Vec3.ZERO) { behaviour, settings, state ->
|
||||
if (behaviour.velocityRunsTwicePerTick(settings, state, this)) {
|
||||
behaviour.velocity(settings, state, this, this.controllingPassenger as Player, deltaMovement)
|
||||
} else {
|
||||
movementInput
|
||||
}
|
||||
}
|
||||
|
||||
val diff = v.subtract(this.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 inertia = ifRidingAvailableSupply(fallback = 0.5) { behaviour, settings, state ->
|
||||
behaviour.inertia(settings, state,this)
|
||||
}
|
||||
val diff = v.subtract(this.deltaMovement)
|
||||
|
||||
this.deltaMovement = this.deltaMovement.add( diff.scale(inertia) )
|
||||
val triedMovement = this.deltaMovement
|
||||
val inertia = ifRidingAvailableSupply(fallback = 0.5) { behaviour, settings, state ->
|
||||
behaviour.inertia(settings, state,this)
|
||||
}
|
||||
|
||||
this.move(MoverType.SELF, this.deltaMovement)
|
||||
this.deltaMovement = this.deltaMovement.add( diff.scale(inertia) )
|
||||
val triedMovement = this.deltaMovement
|
||||
|
||||
if (this.horizontalCollision && this.isControlledByLocalInstance) {
|
||||
ifRidingAvailable { behaviour, settings, state ->
|
||||
// Tried minus performed = vector pointing at where we *couldn't* go
|
||||
val delta = triedMovement.subtract(this.deltaMovement)
|
||||
if (behaviour.damageOnCollision(settings, state, this, delta)) {
|
||||
DamageOnCollisionPacket(delta).sendToServer()
|
||||
this.move(MoverType.SELF, this.deltaMovement)
|
||||
|
||||
for (passenger in this.passengers) {
|
||||
passenger.deltaMovement = this.deltaMovement
|
||||
}
|
||||
DismountPokemonPacket().sendToServer()
|
||||
if (this.horizontalCollision && this.isControlledByLocalInstance) {
|
||||
ifRidingAvailable { behaviour, settings, state ->
|
||||
// Tried minus performed = vector pointing at where we *couldn't* go
|
||||
val delta = triedMovement.subtract(this.deltaMovement)
|
||||
if (behaviour.damageOnCollision(settings, state, this, delta)) {
|
||||
DamageOnCollisionPacket(delta).sendToServer()
|
||||
|
||||
// Reset ride velocity
|
||||
state.rideVelocity.set(state.rideVelocity.get().multiply(0.0, 1.0, 0.0))
|
||||
for (passenger in this.passengers) {
|
||||
passenger.deltaMovement = this.deltaMovement
|
||||
}
|
||||
DismountPokemonPacket().sendToServer()
|
||||
|
||||
// Reset ride velocity
|
||||
state.rideVelocity.set(state.rideVelocity.get().multiply(0.0, 1.0, 0.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2382,11 +2388,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-5
@@ -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)
|
||||
|
||||
-24
@@ -8,12 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.net.serverhandling.battle
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.HealItemActionResponse
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleQueueRequestPacket
|
||||
@@ -28,27 +25,6 @@ object BattleSelectActionsHandler : ServerNetworkPacketHandler<BattleSelectActio
|
||||
if (!actor.mustChoose) {
|
||||
return
|
||||
}
|
||||
|
||||
// Battle command injection attempt detection
|
||||
packet.showdownActionResponses.asSequence()
|
||||
.flatMap {
|
||||
when (it) {
|
||||
is MoveActionResponse -> sequenceOf(it.moveName, it.targetPnx, it.gimmickID)
|
||||
is HealItemActionResponse -> sequenceOf(it.item)
|
||||
else -> emptySequence()
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
.firstOrNull { field -> field.any(Char::isISOControl) }
|
||||
?.let {
|
||||
// Escapes control chars as \uXXXX to prevent forging logs.
|
||||
val printable = it.replace(Regex("\\p{Cc}")) { "\\u%04x".format(it.value[0].code) }
|
||||
Cobblemon.LOGGER.warn("Blocked battle command injection from ${player.name.string}: $printable")
|
||||
actor.sendUpdate(BattleQueueRequestPacket(actor.request!!))
|
||||
actor.sendUpdate(BattleMakeChoicePacket())
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
actor.setActionResponses(packet.showdownActionResponses)
|
||||
} catch (e: IllegalActionChoiceException) {
|
||||
|
||||
@@ -55,8 +55,6 @@ object PlatformEvents {
|
||||
@JvmField
|
||||
val CLIENT_PLAYER_LOGOUT = EventObservable<ClientPlayerEvent.Logout>()
|
||||
@JvmField
|
||||
val PLAYER_RESPAWN = EventObservable<ServerPlayerEvent.Respawn>()
|
||||
@JvmField
|
||||
val PLAYER_DEATH = CancelableObservable<ServerPlayerEvent.Death>()
|
||||
@JvmField
|
||||
val RIGHT_CLICK_BLOCK = CancelableObservable<ServerPlayerEvent.RightClickBlock>()
|
||||
|
||||
@@ -50,11 +50,6 @@ interface ServerPlayerEvent {
|
||||
*/
|
||||
data class Logout(override val player: ServerPlayer) : ServerPlayerEvent
|
||||
|
||||
/**
|
||||
* Fired when the [player] respawns (either by coming back from the end or dying).
|
||||
*/
|
||||
data class Respawn(override val player: ServerPlayer) : ServerPlayerEvent
|
||||
|
||||
/**
|
||||
* Fired when the [player] dies.
|
||||
* If canceled the death will be prevented but healing is required in order to not be stuck in a loop.
|
||||
|
||||
+3
-4
@@ -135,18 +135,17 @@ internal object PropertiesCompletionProvider : DataRegistry {
|
||||
private fun addDefaults() {
|
||||
this.inject(setOf("level", "lvl", "l"), setOf("1", "${Cobblemon.config.maxPokemonLevel}") )
|
||||
this.inject(setOf("shiny", "s"), setOf("yes", "no"))
|
||||
this.inject(setOf("alpha", "is_alpha"), setOf("yes", "no"))
|
||||
this.inject(setOf("gender"), Gender.entries.map { it.name.lowercase() })
|
||||
this.inject(setOf("friendship"), setOf("0", Cobblemon.config.maxPokemonFriendship.toString()))
|
||||
this.inject(setOf("fullness"), setOf("0", "255"))
|
||||
this.inject(setOf("pokeball"), PokeBalls.all().map { it.name.simplify() })
|
||||
this.inject(setOf("nature"), Natures.all().map { it.name.simplify() })
|
||||
this.inject(setOf("ability"), Abilities.all().map { if (it.name.asIdentifierDefaultingNamespace().namespace == Cobblemon.MODID) it.name.asIdentifierDefaultingNamespace().path else it.name })
|
||||
this.inject(setOf("dmax_level", "dmax"), setOf("0", Cobblemon.config.maxDynamaxLevel.toString()))
|
||||
this.inject(setOf("gmax_factor", "gmax"), setOf("yes", "no"))
|
||||
this.inject(setOf("dmax"), setOf("0", Cobblemon.config.maxDynamaxLevel.toString()))
|
||||
this.inject(setOf("gmax"), setOf("yes", "no"))
|
||||
this.inject(setOf("type", "elemental_type"), ElementalTypes.all().map { it.name })
|
||||
this.inject(setOf("tera_type", "tera"), TeraTypes.map { it.id.simplify() })
|
||||
this.inject(setOf("tradeable", "tradable"), setOf("yes", "no"))
|
||||
this.inject(setOf("tradeable"), setOf("yes", "no"))
|
||||
this.inject(setOf("originaltrainer", "ot"), setOf(""))
|
||||
this.inject(setOf("originaltrainertype", "ottype"), setOf("None", "Player", "NPC"))
|
||||
this.inject(setOf("nickname", "nick"), setOf(""))
|
||||
|
||||
+6
-3
@@ -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,
|
||||
|
||||
/*
|
||||
|
||||
+1
-2
@@ -26,6 +26,5 @@ object CobblemonGameRules {
|
||||
val SHINY_STARTERS: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("doShinyStarters", GameRules.Category.MISC, BooleanRuleInvoker.`cobblemon$create`(false))
|
||||
@JvmField
|
||||
val HEALERS_HEAL_PC: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("healersHealPC", GameRules.Category.MISC, BooleanRuleInvoker.`cobblemon$create`(false))
|
||||
@JvmField
|
||||
val DO_POKEMON_PREDATION: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("doPokemonPredation", GameRules.Category.MOBS, BooleanRuleInvoker.`cobblemon$create`(true))
|
||||
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:watershuriken_actor",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/moves/watershuriken"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.rsize": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"trail": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_actortrail",
|
||||
"type": "particle"
|
||||
}
|
||||
},
|
||||
"hit": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_target",
|
||||
"type": "particle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_initialization": {
|
||||
"creation_expression": "v.target_deltaz=5;"
|
||||
},
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 1
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.25
|
||||
},
|
||||
"minecraft:emitter_shape_disc": {
|
||||
"offset": [0, 0.02, 0.1],
|
||||
"radius": 0.5,
|
||||
"direction": "outwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 0.35
|
||||
},
|
||||
"minecraft:particle_lifetime_events": {
|
||||
"timeline": {
|
||||
"0.00": "trail",
|
||||
"0.06": "trail",
|
||||
"0.12": "trail",
|
||||
"0.18": "trail",
|
||||
"0.24": "trail",
|
||||
"0.30": "trail",
|
||||
"0.34": "hit"
|
||||
}
|
||||
},
|
||||
"minecraft:particle_initial_spin": {
|
||||
"rotation": "math.random_integer(0,3)*90"
|
||||
},
|
||||
"minecraft:particle_motion_parametric": {
|
||||
"relative_position": ["-v.target_deltax*(v.particle_age / v.particle_lifetime)", "(v.target_deltay*(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"],
|
||||
"facing_camera_mode": "lookat_direction",
|
||||
"direction": {
|
||||
"mode": "custom",
|
||||
"custom_direction": [0, 0, -1]
|
||||
},
|
||||
"uv": {
|
||||
"texture_width": 60,
|
||||
"texture_height": 15,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [15, 15],
|
||||
"step_UV": [15, 0],
|
||||
"frames_per_second": 24,
|
||||
"max_frame": 4,
|
||||
"loop": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:watershuriken_actoralt",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/moves/watershuriken"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.rsize": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"trail": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_actortrail",
|
||||
"type": "particle"
|
||||
}
|
||||
},
|
||||
"hit": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_target",
|
||||
"type": "particle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_initialization": {
|
||||
"creation_expression": "v.target_deltaz=5;"
|
||||
},
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 7,
|
||||
"max_particles": 3
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.4
|
||||
},
|
||||
"minecraft:emitter_shape_disc": {
|
||||
"offset": [0, 0.02, 0.1],
|
||||
"radius": 0.5,
|
||||
"direction": "outwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 0.35
|
||||
},
|
||||
"minecraft:particle_lifetime_events": {
|
||||
"timeline": {
|
||||
"0.00": "trail",
|
||||
"0.06": "trail",
|
||||
"0.12": "trail",
|
||||
"0.18": "trail",
|
||||
"0.24": "trail",
|
||||
"0.30": "trail",
|
||||
"0.34": "hit"
|
||||
}
|
||||
},
|
||||
"minecraft:particle_initial_spin": {
|
||||
"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)"]
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["0.4*v.rsize", "0.4*v.rsize"],
|
||||
"facing_camera_mode": "lookat_direction",
|
||||
"direction": {
|
||||
"mode": "custom",
|
||||
"custom_direction": [0, 0, -1]
|
||||
},
|
||||
"uv": {
|
||||
"texture_width": 60,
|
||||
"texture_height": 15,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [15, 15],
|
||||
"step_UV": [15, 0],
|
||||
"frames_per_second": 24,
|
||||
"max_frame": 4,
|
||||
"loop": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:watershuriken_actorhor",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/moves/watershuriken"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.rsize": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"trail": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_actortrail",
|
||||
"type": "particle"
|
||||
}
|
||||
},
|
||||
"hit": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_target",
|
||||
"type": "particle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_initialization": {
|
||||
"creation_expression": "v.target_deltaz=5;"
|
||||
},
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 1
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.25
|
||||
},
|
||||
"minecraft:emitter_shape_disc": {
|
||||
"offset": [0, 0.02, 0.1],
|
||||
"radius": 0.5,
|
||||
"direction": "outwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 0.35
|
||||
},
|
||||
"minecraft:particle_lifetime_events": {
|
||||
"timeline": {
|
||||
"0.00": "trail",
|
||||
"0.06": "trail",
|
||||
"0.12": "trail",
|
||||
"0.18": "trail",
|
||||
"0.24": "trail",
|
||||
"0.30": "trail",
|
||||
"0.34": "hit"
|
||||
}
|
||||
},
|
||||
"minecraft:particle_initial_spin": {
|
||||
"rotation": "math.random_integer(0,3)*90"
|
||||
},
|
||||
"minecraft:particle_motion_parametric": {
|
||||
"relative_position": ["-v.target_deltax*(v.particle_age / v.particle_lifetime)", "(v.target_deltay*(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"],
|
||||
"facing_camera_mode": "emitter_transform_xz",
|
||||
"uv": {
|
||||
"texture_width": 60,
|
||||
"texture_height": 15,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [15, 15],
|
||||
"step_UV": [15, 0],
|
||||
"frames_per_second": 24,
|
||||
"max_frame": 4,
|
||||
"loop": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:watershuriken_actortrail",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_alpha",
|
||||
"texture": "textures/particles/generic/impact/impact_water"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.scale": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [1, 0]
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 1
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 1
|
||||
},
|
||||
"minecraft:emitter_shape_point": {
|
||||
"direction": ["v.particle_random_1-0.5", "v.particle_random_2-0.25", -3]
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": "math.random(0.3, 0.6)"
|
||||
},
|
||||
"minecraft:particle_initial_spin": {
|
||||
"rotation": "math.round(v.particle_random_4*3)*90"
|
||||
},
|
||||
"minecraft:particle_initial_speed": 8,
|
||||
"minecraft:particle_motion_dynamic": {
|
||||
"linear_acceleration": [0, -5, 0],
|
||||
"linear_drag_coefficient": 8
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["0.22 * variable.scale", "0.22 * variable.scale"],
|
||||
"facing_camera_mode": "rotate_xyz",
|
||||
"uv": {
|
||||
"texture_width": 8,
|
||||
"texture_height": 56,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 24],
|
||||
"size_UV": [8, 8],
|
||||
"step_UV": [0, 8],
|
||||
"frames_per_second": 16,
|
||||
"max_frame": 4
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_motion_collision": {
|
||||
"collision_radius": 0.02
|
||||
},
|
||||
"minecraft:particle_appearance_tinting": {
|
||||
"color": [0.61176, 0.87451, 1, 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:watershuriken_actorvert",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_blend",
|
||||
"texture": "textures/particles/moves/watershuriken"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.rsize": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"trail": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_actortrail",
|
||||
"type": "particle"
|
||||
}
|
||||
},
|
||||
"hit": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_target",
|
||||
"type": "particle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_initialization": {
|
||||
"creation_expression": "v.target_deltaz=5;"
|
||||
},
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 1
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.25
|
||||
},
|
||||
"minecraft:emitter_shape_disc": {
|
||||
"offset": [0, 0.02, 0.1],
|
||||
"radius": 0.5,
|
||||
"direction": "outwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 0.35
|
||||
},
|
||||
"minecraft:particle_lifetime_events": {
|
||||
"timeline": {
|
||||
"0.00": "trail",
|
||||
"0.06": "trail",
|
||||
"0.12": "trail",
|
||||
"0.18": "trail",
|
||||
"0.24": "trail",
|
||||
"0.30": "trail",
|
||||
"0.34": "hit"
|
||||
}
|
||||
},
|
||||
"minecraft:particle_initial_spin": {
|
||||
"rotation": "math.random_integer(0,3)*90"
|
||||
},
|
||||
"minecraft:particle_motion_parametric": {
|
||||
"relative_position": ["-v.target_deltax*(v.particle_age / v.particle_lifetime)", "(v.target_deltay*(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"],
|
||||
"facing_camera_mode": "emitter_transform_yz",
|
||||
"uv": {
|
||||
"texture_width": 60,
|
||||
"texture_height": 15,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [15, 15],
|
||||
"step_UV": [15, 0],
|
||||
"frames_per_second": 24,
|
||||
"max_frame": 4,
|
||||
"loop": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "cobblemon:watershuriken_target",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_alpha",
|
||||
"texture": "textures/particles/generic/impact/impact_water"
|
||||
}
|
||||
},
|
||||
"curves": {
|
||||
"variable.scale": {
|
||||
"type": "linear",
|
||||
"input": "v.particle_age",
|
||||
"horizontal_range": "v.particle_lifetime",
|
||||
"nodes": [1, 0]
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"event": {
|
||||
"particle_effect": {
|
||||
"effect": "cobblemon:watershuriken_targetspark",
|
||||
"type": "particle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 240,
|
||||
"max_particles": 14
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 0.1
|
||||
},
|
||||
"minecraft:emitter_lifetime_events": {
|
||||
"creation_event": "event"
|
||||
},
|
||||
"minecraft:emitter_shape_point": {},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": "math.random(0.3, 0.6)"
|
||||
},
|
||||
"minecraft:particle_initial_spin": {
|
||||
"rotation": "math.round(v.particle_random_4*3)*90"
|
||||
},
|
||||
"minecraft:particle_initial_speed": "math.random(9,17)",
|
||||
"minecraft:particle_motion_dynamic": {
|
||||
"linear_drag_coefficient": 5
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": ["0.3 * variable.scale", "0.3 * variable.scale"],
|
||||
"facing_camera_mode": "rotate_xyz",
|
||||
"uv": {
|
||||
"texture_width": 8,
|
||||
"texture_height": 56,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 16],
|
||||
"size_UV": [8, 8],
|
||||
"step_UV": [0, 8],
|
||||
"frames_per_second": 16,
|
||||
"max_frame": 5
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_appearance_tinting": {
|
||||
"color": [0.61176, 0.87451, 1, 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 713 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user