Compare commits

..
126 changed files with 1183 additions and 7721 deletions
-3
View File
@@ -1,3 +0,0 @@
### Additions
- Added the ability to ride Pokémon by shift-right clicking and selecting the ride option.
+1 -1
View File
@@ -1,6 +1,6 @@
# Cobblemon
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and Forge.
An open-source Pokémon mod for Minecraft Java Edition, written in Kotlin for Fabric and NeoForge.
## Getting started
-81
View File
@@ -1,81 +0,0 @@
{
"lastSavedVersion": "1.7.0",
"maxPokemonLevel": 100,
"maxPokemonFriendship": 255,
"announceDropItems": true,
"defaultDropItemMethod": "on-entity",
"ambientPokemonCryTicks": 1080,
"defaultBoxCount": 30,
"pokemonSaveIntervalSeconds": 30,
"storageFormat": "nbt",
"preventCompletePartyDeposit": false,
"mongoDBConnectionString": "mongodb://localhost:27017",
"mongoDBDatabaseName": "cobblemon",
"maxVerticalCorrectionBlocks": 64,
"minimumLevelRangeMax": 10,
"enableSpawning": true,
"minimumDistanceBetweenEntities": 8.0,
"maxNearbyBlocksHorizontalRange": 4,
"maxNearbyBlocksVerticalRange": 2,
"maxVerticalSpace": 8,
"worldSliceDiameter": 8,
"worldSliceHeight": 16,
"ticksBetweenSpawnAttempts": 20.0,
"minimumSliceDistanceFromPlayer": 16.0,
"maximumSliceDistanceFromPlayer": 64.0,
"exportSpawnConfig": false,
"savePokemonToWorld": true,
"exportStarterConfig": false,
"autoUpdateShowdown": true,
"defaultFleeDistance": 32.0,
"allowExperienceFromPvP": true,
"experienceShareMultiplier": 0.5,
"luckyEggMultiplier": 1.5,
"allowSpectating": true,
"experienceMultiplier": 2.0,
"pokemonPerChunk": 1.0,
"passiveStatuses": {
"cobblemon:poison": "180-300",
"cobblemon:poisonbadly": "180-300",
"cobblemon:paralysis": "180-300",
"cobblemon:frozen": "180-300",
"cobblemon:sleep": "180-300",
"cobblemon:burn": "180-300"
},
"infiniteHealerCharge": false,
"maxHealerCharge": 6.0,
"secondsToChargeHealingMachine": 900.0,
"defaultFaintTimer": 300,
"faintAwakenHealthPercent": 0.2,
"healPercent": 0.05,
"healTimer": 60,
"baseApricornTreeGenerationChance": 0.1,
"displayEntityLevelLabel": true,
"displayEntityNameLabel": true,
"displayNameForUnknownPokemon": false,
"displayEntityLabelsWhenCrouchingOnly": false,
"shinyRate": 8192.0,
"shinyNoticeParticlesDistance": 24.0,
"captureCalculator": "cobblemon",
"playerDamagePokemon": true,
"appleLeftoversChance": 0.025,
"maxRootsInArea": 5,
"bigRootPropagationChance": 0.1,
"energyRootChance": 0.25,
"maxDynamaxLevel": 10,
"teraTypeRate": 20.0,
"defaultPasturedPokemonLimit": 16,
"pastureBlockUpdateTicks": 40,
"pastureMaxWanderDistance": 64,
"pastureMaxPerChunk": 4.0,
"maxInsertedFossilItems": 2,
"walkingInBattleAnimations": false,
"battleWildMaxDistance": 12.0,
"tradeMaxDistance": 12.0,
"battlePvPMaxDistance": 32.0,
"battleSpectateMaxDistance": 64.0,
"maxPokedexScanningDetectionRange": 10.0,
"hideUnimplementedPokemonInThePokedex": false,
"summaryPokemonFollowCursor": true,
"enableDebugKeys": false
}
@@ -1,141 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import java.util.function.Function;
public interface Rollable {
// From Minecraft's Camera
Vector3f FORWARDS = new Vector3f(0.0F, 0.0F, -1.0F);
Vector3f UP = new Vector3f(0.0F, 1.0F, 0.0F);
Vector3f LEFT = new Vector3f(-1.0F, 0.0F, 0.0F);
@Nullable Matrix3f getOrientation();
Rollable updateOrientation(Function<Matrix3f, Matrix3f> update);
/**
* @return The forward vector (0, 0, -1) after the rotation matrix is applied.
*/
default Vector3f getForwardVector() {
if (getOrientation() == null) return new Vector3f(FORWARDS);
return getOrientation().transform(FORWARDS, new Vector3f());
}
/**
* @return The left vector (-1, 0, 0) after the rotation matrix is applied.
*/
default Vector3f getLeftVector() {
if (getOrientation() == null) return new Vector3f(LEFT);
return getOrientation().transform(LEFT, new Vector3f());
}
/**
* @return The up vector (0, 1, 0) after the rotation matrix is applied.
*/
default Vector3f getUpVector() {
if (getOrientation() == null) return new Vector3f(UP);
return getOrientation().transform(UP, new Vector3f());
}
/**
*
* @param yaw change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateYaw(float yaw) {
return updateOrientation(prev -> prev.rotateY((float) Math.toRadians(-yaw)));
}
/**
*
* @param pitch change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotatePitch(float pitch) {
return updateOrientation(prev -> prev.rotateX((float) Math.toRadians(-pitch)));
}
/**
*
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateRoll(float roll) {
return updateOrientation(prev -> prev.rotateZ((float) Math.toRadians(-roll)));
}
/**
* Order matters when rotating an object. If you need a different order, individually call rotateYaw, rotatePitch, and rotateRoll
*
* @param yaw change in degrees
* @param pitch change in degrees
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotate(float yaw, float pitch, float roll) {
return rotateYaw(yaw).rotatePitch(pitch).rotateRoll(roll);
}
/**
* @return The pitch angle in degrees as a float.
* Value is between [-180, 180] with 0 being South, 90 being West, 180/-180 being North, and -90 being East
*/
default float getYaw() {
return (float) Mth.wrapDegrees(-Math.toDegrees(FORWARDS.angleSigned(getForwardVector(), UP)) + 180);
}
/**
* @return The yaw angle in degrees as a float.
* Value is between [-90, 90] with 0 being straight forward, 90 being straight down, and -90 being straight up
*/
default float getPitch() {
return (float) Mth.wrapDegrees(-Math.toDegrees(Math.asin(getForwardVector().y)));
}
/**
* @return The roll angle in degrees as a float.
* Value is between [-180, 180] with 0 being no roll (normal), 90 being 90 degrees CW, 180/-180 being 180 degrees CW/CCW, and -90 being 90 degrees CCW
*/
default float getRoll() {
//Cobblemon.LOGGER.info("{}", (float) Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
return (float) Mth.wrapDegrees(-Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
}
/**
* Get the angular velocity in degrees per second as a Vec3
*/
Vec3 angRollVel( double deltaTime );
public Vec3 rotationOnMouseXY(double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime );
boolean shouldRoll();
boolean useAngVelSmoothing();
void clearRotation();
}
@@ -1,97 +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;
import com.cobblemon.mod.common.Rollable;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Shadow @Final private static float DEFAULT_CAMERA_DISTANCE;
@Shadow private Entity entity;
@Shadow @Final private Quaternionf rotation;
@Shadow @Final private Vector3f forwards;
@Shadow @Final private Vector3f up;
@Shadow @Final private Vector3f left;
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow public abstract float getXRot();
@Shadow public abstract float getYRot();
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@Unique boolean disableRollableCameraDebug = false;
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void open_camera$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof Rollable rollable) || disableRollableCameraDebug) return;
if (!rollable.shouldRoll() && rollable.getOrientation() != null) {
if(this.returnTimer < 1) {
//Rotation is taken from entity since we no longer handle mouse ourselves
//Stops a period of time when you can't input anything.
rollable.getOrientation().set(
new Matrix3f()
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
);
if(rollAngleStart == 0){
this.returnTimer = 1;
rollable.clearRotation();
return;
}
rollable.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
this.returnTimer = 1;
rollable.clearRotation();
}
return;
}
if (rollable.getOrientation() == null) return;
applyRotation();
this.returnTimer = 0;
this.rollAngleStart = rollable.getRoll();
ci.cancel();
}
@Unique
private void applyRotation(){
if (!(this.entity instanceof Rollable rollable) || rollable.getOrientation() == null) return;
var newRotation = rollable.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (this.minecraft.options.getCameraType().isMirrored()) {
newRotation.rotateY((float)Math.toRadians(180));
}
this.rotation.set(newRotation);
this.xRot = rollable.getPitch();
this.yRot = rollable.getYaw();
this.forwards.set(rollable.getForwardVector());
this.up.set(rollable.getUpVector());
this.left.set(rollable.getLeftVector());
}
}
@@ -1,66 +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;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
@Mixin(HumanoidModel.class)
public class HumanoidModelMixin {
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
@Shadow @Final
public ModelPart head;
@Shadow @Final
public ModelPart rightArm;
@Shadow @Final
public ModelPart leftArm;
@Shadow @Final
public ModelPart rightLeg;
@Shadow @Final
public ModelPart leftLeg;
@Inject(method = "setupAnim*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V", shift = At.Shift.BEFORE))
public void cobblemon$animateRiders(
LivingEntity entity,
float limbSwing,
float limbSwingAmount,
float ageInTicks,
float netHeadYaw,
float headPitch,
CallbackInfo ci
) {
if (relevantPartsByName.isEmpty()) {
relevantPartsByName.put("arm_left", leftArm);
relevantPartsByName.put("arm_right", rightArm);
relevantPartsByName.put("leg_left", leftLeg);
relevantPartsByName.put("leg_right", rightLeg);
relevantPartsByName.put("head", head);
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity && entity instanceof AbstractClientPlayer) {
MountedPlayerRenderer.INSTANCE.animate((PokemonEntity) vehicle, (AbstractClientPlayer) entity, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -8,21 +8,16 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
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.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import org.joml.Quaternionf;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemInHandRenderer.class)
@@ -32,15 +27,5 @@ public class ItemInHandRendererMixin {
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
ci.cancel();
}
else if (abstractClientPlayer instanceof Rollable rollable && rollable.shouldRoll()) {
}
}
@Redirect(method = "renderHandsWithItems", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V", ordinal = 1))
private void cobblemon$renderHandswithItems(PoseStack instance, Quaternionf quaternion) {
if (!(Minecraft.getInstance().player instanceof Rollable rollable && rollable.shouldRoll())) {
instance.mulPose(quaternion);
}
}
}
@@ -12,15 +12,9 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
@@ -34,70 +28,4 @@ public abstract class LivingEntityMixin {
}
}
/*
@Inject(method = "travelRidden", at = @At(value = "HEAD"))
private void cobblemon$altTravelRidden(Player player, Vec3 travelVector)
{
this.setSpeed(this.getRiddenSpeed(player));
this.travel(vec3);
}
/*
// Shadow the original method so we can call it.
@Shadow
protected abstract boolean isControlledByLocalInstance();
/**
* Redirect the call to isControlledByLocalInstance() within travelRidden.
* When travelRidden calls isControlledByLocalInstance(), it will get our modified result.
*/
/*
@Redirect(
method = "travelRidden(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/phys/Vec3;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/LivingEntity;isControlledByLocalInstance()Z"
)
)
private boolean redirectIsControlledByLocalInstance(LivingEntity instance) {
// Return true if either the original method returns true or our custom condition is true.
return this.isControlledByLocalInstance() || this.b;
}
@Overwrite
private void travelRidden(Player player, Vec3 travelVector) {
Vec3 vec3 = this.getRiddenInput(player, travelVector);
this.tickRidden(player, vec3);
if (this.isControlledByLocalInstance()) {
this.setSpeed(this.getRiddenSpeed(player));
this.travel(vec3);
} else {
this.calculateEntityAnimation(false);
this.setDeltaMovement(Vec3.ZERO);
this.tryCheckInsideBlocks();
}
}
@ModifyArg(
method = "travelRidden",
at = @At(
value = "INVOKE",
target = "setDeltaMovement(Lnet/minecraft/world/phys/Vec3;)V"
),
index = 0 // the first (and only) argument
)
private Vec3 modifyDeltaMovementArgument(Vec3 original) {
// Optionally, check if original is Vec3.ZERO, to only replace that call:
if (original.equals(Vec3.ZERO)) {
// Return your custom vector instead of Vec3.ZERO
return original;
}
// Otherwise, return the original value
return original;
}
*/
}
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import net.minecraft.client.player.LocalPlayer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerMixin {
@Shadow
private float jumpRidingScale;
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
public void modifyJumpRidingScale(CallbackInfoReturnable<Float> cir) {
LocalPlayer player = (LocalPlayer) (Object) this;
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
//ride.getRiding().canJump()
if( !ride.getRiding().canJump(ride, player))
{
cir.setReturnValue(0.5f);
}
}
// Check if the player is riding your custom entity (for example, a Pokemon)
/*
if (player.isRiding() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity rideable = (PokemonEntity) player.getVehicle();
// Get your custom fuel ratio (a value from 0.0 to 1.0)
float fuelRatio = rideable.getRidingFuelRatio();
cir.setReturnValue(fuelRatio);
}
*/
}
}
@@ -8,38 +8,30 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.Blaze3D;
import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler;
import net.minecraft.client.player.LocalPlayer;
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;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MouseHandler.class)
public class MouseHandlerMixin {
@Shadow private double accumulatedScrollY;
@Shadow @Final private Minecraft minecraft;
@Shadow
private double accumulatedScrollY;
@Unique SmoothDouble xMouseSmoother = new SmoothDouble();
@Unique SmoothDouble yMouseSmoother = new SmoothDouble();
@Unique SmoothDouble pitchSmoother = new SmoothDouble();
@Unique SmoothDouble rollSmoother = new SmoothDouble();
@Unique SmoothDouble yawSmoother = new SmoothDouble();
@Shadow @Final private Minecraft minecraft;
@Shadow @Final private SmoothDouble smoothTurnY;
@@ -49,8 +41,6 @@ public class MouseHandlerMixin {
@Shadow private double accumulatedDY;
@Shadow private double accumulatedScrollX;
@Inject(
method = "onScroll",
at = @At(
@@ -102,12 +92,8 @@ public class MouseHandlerMixin {
target = "Lnet/minecraft/client/player/LocalPlayer;turn(DD)V"
)
)
public boolean cobblemon$modifyRotation(LocalPlayer player,
double cursorDeltaX,
double cursorDeltaY,
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
public boolean cobblemon$modifyRotation(LocalPlayer player, double cursorDeltaX, double cursorDeltaY, @Local(argsOnly = true) double d) {
// I know this technically doesn't need to be a WrapWithCondition, but it'll make things easier later for riding.
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -119,106 +105,7 @@ public class MouseHandlerMixin {
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity));
return false;
}
if (!(player instanceof Rollable rollable)) return true;
if (!rollable.shouldRoll()) {
xMouseSmoother.reset();
yMouseSmoother.reset();
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return true;
}
//maybe have a rollable x mouse and rollable y mouse that take the xmousesmoother
//and y mouse smoother ( which are right now named pichsmoother and roll smoother)
// values and feed it to those rollable functions which will be called by the
//specific ride controller where rollable.rotate can then be called and used
//controller specific.
//return a vec3 of the yaw pitch and roll that are wanted.
//multiply by sensitivity before passing it in
//var xMouse = xMouseSmoother.getNewDeltaValue(cursorDeltaX * 0.05f, d);
//var yMouse = yMouseSmoother.getNewDeltaValue(cursorDeltaY * 0.05f, d);
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
var ridingSensitivity = Math.pow(defaultSensitivity, 3);
Vec3 angVecMouse = rollable.rotationOnMouseXY
( cursorDeltaY,
cursorDeltaX ,
yMouseSmoother,
xMouseSmoother,
ridingSensitivity,
movementTime );
//make this configurable maybe?
//var mRoll = rollSmoother.getNewDeltaValue(angVecMouse.z * 0.15f, d);
rollable.rotate((float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z);
//var yMouse = yMouseSmoother.getNewDeltaValue(angVecMouse.y * 0.15f, d);
//var xMouse = xMouseSmoother.getNewDeltaValue(cursorDeltaX * 0.15f, d);
//get the current angular velocity requested to be applied
//by the ride controller
var angRot = rollable.angRollVel(movementTime);
if( rollable.useAngVelSmoothing() )
{
//get the current angular velocity requested to be applied
//by the ride controller
//Multiply against deltatime to get a consistent turn
//independent of framerate
if( angRot != null ) {
//var yaw = angRot.x * d;
//Add the yaw to the mouse here... this all needs changed. It should
//just map which 0 1 or 2 of the three it is going to modulate with
//what
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
rollable.rotate((float) yaw, (float) pitch, (float) roll);
}
}
else
{
//Multiply against deltatime to get a consistent turn
//independent of framerate
if( angRot != null ) {
//var yaw = angRot.x * d;
//Add the yaw to the mouse here... this all needs changed. It should
//just map which 0 1 or 2 of the three it is going to modulate with
//what
rollable.rotate((float) (angRot.x * 10 * d),
(float) (angRot.y * 10 * d),
(float) (angRot.z * 10 * d));
}
}
return false;
}
@Inject(method = "handleAccumulatedMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;isMouseGrabbed()Z", ordinal = 0))
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci, @Local(ordinal = 1) double e) {
if (minecraft.player == null) return;
if (!(minecraft.player instanceof Rollable rollable)) return;
if (!rollable.shouldRoll()) return;
if (minecraft.isPaused()) return;
var pitch = pitchSmoother.getNewDeltaValue(0, e);
var roll = rollSmoother.getNewDeltaValue(0, e);
rollable.rotate(0.0F, (float)pitch, (float)roll);
return true;
}
}
@@ -10,15 +10,12 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonItems;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.api.events.CobblemonEvents;
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.api.storage.party.PartyStore;
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
import com.cobblemon.mod.common.pokemon.FormData;
@@ -29,16 +26,11 @@ import com.cobblemon.mod.common.util.CompoundTagExtensionsKt;
import com.cobblemon.mod.common.util.CompoundTagUtilities;
import com.cobblemon.mod.common.util.DataKeys;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@@ -46,18 +38,14 @@ import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -65,11 +53,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements Rollable, ScannableEntity {
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity {
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@@ -87,7 +74,6 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Shadow public abstract void displayClientMessage(Component message, boolean overlay);
@Unique private Matrix3f orientation = null;
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
super(p_20966_, p_20967_);
@@ -239,137 +225,4 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
public LivingEntity resolveEntityScan() {
return this;
}
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (shouldRoll()) {
this.absMoveTo(x, y, z);
this.setYRot(yaw % 360.0f);
this.setXRot(pitch % 360.0f);
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
}
else {
this.absMoveTo(x, y, z);
this.absRotateTo(yaw, pitch);
}
}
@Inject(method = "tick", at = @At("HEAD"))
public void tick(CallbackInfo ci) {
if (this.shouldRoll()) {
// Proof of concept for yaw movement
if (this.getMainHandItem().is(Items.STICK)) {
this.rotateYaw(2);
}
else if (this.getMainHandItem().is(Items.BLAZE_ROD)) {
this.rotateYaw(-2);
}
}
}
@Override
public Matrix3f getOrientation() {
return orientation;
}
@Override
public Rollable updateOrientation(Function<Matrix3f, Matrix3f> update) {
//Disabled to allow non mouse inputs to modify roll
if (!shouldRoll()) return this;
if (this.orientation == null) {
this.orientation = new Matrix3f();
this.rotate(getYRot() - 180, getXRot(), 0);
}
this.orientation = update.apply(this.orientation);
setYRot(this.getYaw());
setXRot(this.getPitch());
return this;
}
@Override
public boolean shouldRoll() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return false;
if (player.isHolding(Items.DIAMOND)) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Override
public Vec3 rotationOnMouseXY(double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime )
{
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return Vec3.ZERO;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return Vec3.ZERO;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
//this is ridiculous. There likely needs to be some sort of mouseInfo class.
return pokemonEntity.getRiding().rotationOnMouseXY
(
pokemonEntity,
player,
yMouse,
xMouse,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime );
}
@Override
public boolean useAngVelSmoothing() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return true;
if (player.isHolding(Items.DIAMOND)) return false;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return true;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
return pokemonEntity.getRiding().useAngVelSmoothing(pokemonEntity);
}
@Override
public Vec3 angRollVel( double deltaTime ) {
var ret = new Vec3(0.0, 0.0, 0.0);
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return ret;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return ret;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return ret;
return pokemonEntity.getRiding().angRollVel( pokemonEntity, player, deltaTime );
}
@Override
public void clearRotation() {
this.orientation = null;
}
@WrapOperation(
method = "rideTick()V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;wantsToStopRiding()Z")
)
public boolean delegateDismountToController(Player instance, Operation<Boolean> original) {
if (this.getVehicle() instanceof Rideable vehicle) {
return vehicle.getRiding().dismountOnShift() && original.call(instance);
}
return original.call(instance);
}
}
}
@@ -9,14 +9,10 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.render.ClientPlayerIcon;
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -24,21 +20,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PlayerRenderer.class)
public class PlayerRendererMixin {
@Inject(method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "TAIL"))
public void cobblemon$clientPlayerIcon(LivingEntity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
ClientPlayerIcon.Companion.onRenderPlayer((Player) entity);
}
@Inject(
method = "setupRotations(Lnet/minecraft/client/player/AbstractClientPlayer;Lcom/mojang/blaze3d/vertex/PoseStack;FFFF)V",
at = @At("HEAD")
)
private void cobblemon$modifyRoll(AbstractClientPlayer player, PoseStack poseStack, float a, float b, float partialTicks, float i, CallbackInfo ci) {
if (player.isPassenger()) {
var vehicle = player.getVehicle();
if (vehicle instanceof PokemonEntity entity) {
MountedPlayerRenderer.INSTANCE.render(player, entity, poseStack, a, b, partialTicks, i);
}
}
@Inject(method = "render", at = @At(value = "TAIL"))
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
ClientPlayerIcon.Companion.onRenderPlayer(player);
}
}
@@ -1,43 +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.CobblemonNetwork;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket;
import net.minecraft.client.player.LocalPlayer;
import org.joml.Matrix3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LocalPlayer.class)
public class LocalPlayerMixin {
@Unique Matrix3f cobblemon$lastOrientation;
@Inject(method = "sendPosition", at = @At("TAIL"))
private void cobblemon_updateRotationMatrix(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
}
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
private void cobblemon_updateRotationMatrixPassenger(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
}
}
@@ -1,36 +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.api.riding.Rideable;
import com.cobblemon.mod.common.api.riding.RidingManager;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PlayerModel.class)
public class PlayerModelMixin {
@Inject(method = "setupAnim(Lnet/minecraft/world/entity/LivingEntity;FFFFF)V", at = @At(value = "HEAD"))
private void cobblemon$setHeadRotation(LivingEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, CallbackInfo ci){
if (!(entity instanceof Player)) return;
Entity vehicle = entity.getVehicle();
if (!(vehicle instanceof PokemonEntity)) return;
RidingManager ridingManager = ((Rideable) vehicle).getRiding();
if (ridingManager.shouldRotatePlayerHead((PokemonEntity) vehicle)) return;
netHeadYaw = 0f;
headPitch = 0f;
}
}
@@ -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.mixin.entity;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Entity.class)
public abstract class EntityMixin {
@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)
public void cobblemon$verifyActuallyTouchingWater(CallbackInfo ci) {
if(this.getVehicle() instanceof PokemonEntity) {
ci.cancel();
}
}
}
@@ -1,45 +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.renderer;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
/**
* @author landonjw
*/
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin {
// @Redirect(
// method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V",
// at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;hasVehicle()Z"),
// slice = @Slice(
// from = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerpAngleDegrees(FFF)F"),
// to = @At(value = "TAIL")
// )
// )
// private boolean dodgeRenderer(LivingEntity instance) {
// return false;
// }
/*@WrapOperation(
method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;wrapDegrees(F)F")
)
private float replaceWrapValue(float angle, Operation<Float> original){
if(origin){
return 0f;
} else {
return original.call(angle);
}
}*/
}
@@ -1,62 +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.riding;
import net.minecraft.client.player.LocalPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(LocalPlayer.class)
public class JetMixin {
@Shadow private int jumpRidingTicks;
@Shadow private float jumpRidingScale;
private float jetStrength;
// @Redirect(method = "tickMovement", at = @At(target = "Lnet/minecraft/entity/JumpingMount;getJumpCooldown()I", value = "INVOKE"))
// public int cobblemon$tryHandlingJet(JumpingMount mount) {
// if(mount instanceof PokemonEntity pokemon) {
//// if(pokemon.getDataTracker().get(PokemonEntity.MOVING)) {
//// ++this.field_3938;
//// if (this.field_3938 < 11) {
//// this.jetStrength = (float) this.field_3938 * 0.1F;
//// } else {
// this.mountJumpStrength = 0.8F + 2.0F / (float) (this.field_3938 - 9) * 0.1F;
//// }
//// } else {
//// this.field_3938 = 0;
//// this.jetStrength = 0;
//// }
//
//// return -1;
// }
//
// return 0;
// }
@Inject(
method = "aiStep",
at = @At(
target = "Lnet/minecraft/client/player/LocalPlayer;jumpRidingScale:F",
value = "FIELD",
ordinal = 4,
shift = At.Shift.AFTER
),
locals = LocalCapture.CAPTURE_FAILSOFT
)
public void cobblemon$applyCachedJumpStrength(CallbackInfo ci) {
this.jumpRidingScale = this.jetStrength;
}
}
@@ -12,14 +12,6 @@ object CobblemonBuildDetails {
const val BRANCH = "{{ branch | default("Not Specified") }}"
fun smallCommitHash(): String {
if(this.GIT_COMMIT.equals("Not Specified")) {
return "Unknown"
}
return this.GIT_COMMIT.substring(0, 7)
}
fun version(): String {
return this.VERSION + if(this.SNAPSHOT) "+${this.smallCommitHash()}" else ""
}
}
@@ -36,6 +36,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.of(::PCBlockEntity, CobblemonBlocks.PC).build(null))
@JvmField
val STADIUM: BlockEntityType<StadiumBlockEntity> = this.create("stadium_terminal", BlockEntityType.Builder.of(::StadiumBlockEntity, CobblemonBlocks.STADIUM).build(null))
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@@ -354,6 +354,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }))
@JvmField
val STADIUM = create("stadium_terminal", StadiumBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F)))
@JvmField
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava()))
@@ -272,6 +272,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val PC = blockItem("pc", CobblemonBlocks.PC)
@JvmField
val STADIUM = blockItem("stadium_terminal", CobblemonBlocks.STADIUM)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@@ -47,14 +47,7 @@ import com.cobblemon.mod.common.client.net.storage.party.SetPartyPokemonHandler
import com.cobblemon.mod.common.client.net.storage.party.SetPartyReferenceHandler
import com.cobblemon.mod.common.client.net.storage.pc.*
import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
import com.cobblemon.mod.common.client.net.trade.TradeAcceptanceChangedHandler
import com.cobblemon.mod.common.client.net.trade.TradeCancelledHandler
import com.cobblemon.mod.common.client.net.trade.TradeCompletedHandler
import com.cobblemon.mod.common.client.net.trade.TradeOfferExpiredHandler
import com.cobblemon.mod.common.client.net.trade.TradeOfferNotificationHandler
import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.client.net.trade.*
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
@@ -94,41 +87,17 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePart
import com.cobblemon.mod.common.net.messages.client.storage.party.MoveClientPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.RenamePCBoxPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeCompletedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
import com.cobblemon.mod.common.net.messages.client.trade.*
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamResponsePacket
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.net.messages.server.*
import com.cobblemon.mod.common.net.messages.server.battle.*
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
@@ -139,7 +108,6 @@ import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPoke
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.server.pasture.PasturePokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPacket
@@ -153,22 +121,19 @@ import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPa
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptancePacket
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.PerformTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
import com.cobblemon.mod.common.net.serverhandling.ChallengeHandler
import com.cobblemon.mod.common.net.serverhandling.ChallengeResponseHandler
import com.cobblemon.mod.common.net.serverhandling.RequestInteractionsHandler
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonToPartyPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePartyPokemonToPCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.PCBoxWallpapersPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.ReleasePCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestChangePCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.RequestRenamePCBoxPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SortPCBoxPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.SwapPCPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.UnlinkPlayerFromPCPacket
import com.cobblemon.mod.common.net.messages.server.trade.*
import com.cobblemon.mod.common.net.serverhandling.*
import com.cobblemon.mod.common.net.serverhandling.battle.*
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
@@ -180,7 +145,6 @@ import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandle
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
import com.cobblemon.mod.common.net.serverhandling.npc.SaveNPCHandler
import com.cobblemon.mod.common.net.serverhandling.orientation.OrientationPacketHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.PasturePokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpastureAllPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandler
@@ -198,12 +162,7 @@ import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemo
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
import com.cobblemon.mod.common.net.serverhandling.trade.*
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -216,11 +175,13 @@ import net.minecraft.server.level.ServerPlayer
* @since November 27th, 2021
*/
object CobblemonNetwork {
//private val logger = LogUtils.getLogger()
fun ServerPlayer.sendPacket(packet: NetworkPacket<*>) {
sendPacketToPlayer(this, packet)
}
fun sendToServer(packet: NetworkPacket<*>) {
//logger.info("[C->S] ${packet.type()}")
Cobblemon.implementation.networkManager.sendToServer(packet)
}
fun sendToAllPlayers(packet: NetworkPacket<*>) = sendPacketToPlayers(server()!!.playerList.players, packet)
@@ -494,13 +455,11 @@ object CobblemonNetwork {
// NPC packets
list.add(PacketRegisterInfo(SaveNPCPacket.ID, SaveNPCPacket::decode, SaveNPCHandler))
// Orientation packet(s)
list.add(PacketRegisterInfo(C2SUpdateOrientationPacket.ID, C2SUpdateOrientationPacket::decode, OrientationPacketHandler))
return list
}
fun sendPacketToPlayer(player: ServerPlayer, packet: NetworkPacket<*>) {
//logger.info("[S->C] ${packet.type()}")
Cobblemon.implementation.networkManager.sendPacketToPlayer(player, packet)
}
}
}
@@ -15,10 +15,12 @@ package com.cobblemon.mod.common.api.berry
* @author Licious
* @since November 28th, 2022
*/
enum class Flavor(val colour: Int) {
SPICY(0x00FF00),
DRY(0xFF0000),
SWEET(0x0000FF),
BITTER(0xFFFF00),
SOUR(0xFF00FF);
enum class Flavor {
SPICY,
DRY,
SWEET,
BITTER,
SOUR
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.api.entity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.Entity.MoveFunction
import net.minecraft.world.entity.player.Player
interface PokemonSideDelegate : EntitySideDelegate<PokemonEntity> {
@@ -52,7 +52,6 @@ import com.cobblemon.mod.common.api.reactive.EventObservable
import com.cobblemon.mod.common.api.reactive.Observable.Companion.filter
import com.cobblemon.mod.common.api.reactive.Observable.Companion.map
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.events.SelectDriverEvent
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.server.level.ServerPlayer
@@ -254,12 +253,4 @@ object CobblemonEvents {
val HATCH_EGG_PRE = CancelableObservable<HatchEggEvent.Pre>()
@JvmField
val HATCH_EGG_POST = EventObservable<HatchEggEvent.Post>()
// -------------------------------------------------------------------------------------
//
// Riding
//
// -------------------------------------------------------------------------------------
@JvmField
val SELECT_DRIVER = EventObservable<SelectDriverEvent>()
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.pokemon
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.AbilityPool
import com.cobblemon.mod.common.api.abilities.AbilityTemplate
@@ -33,7 +32,6 @@ import com.cobblemon.mod.common.api.pokemon.moves.Learnset
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
@@ -47,7 +45,6 @@ import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementA
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.HashBasedTable
import com.google.gson.Gson
@@ -56,7 +53,6 @@ import com.google.gson.reflect.TypeToken
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
@@ -97,7 +93,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(DropEntry::class.java, DropEntryAdapter)
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(Component::class.java, TextAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
@@ -105,8 +100,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(RideController::class.java, RideControllerAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
@@ -1,25 +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
import net.minecraft.world.entity.PlayerRideableJumping
/**
* Represents an entity that supports riding.
*
* @since 1.5.0
*/
interface Rideable : PlayerRideableJumping {
/**
* Denotes the manager responsible for handling any instance of riding taking place on an entity
*/
val riding: RidingManager
}
@@ -1,197 +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
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import kotlin.math.sin
data class RidingManager(val entity: PokemonEntity) {
var lastSpeed = 0F
var states: MutableMap<ResourceLocation, RidingState> = mutableMapOf()
var deltaRotation = Vec2.ZERO
val runtime: MoLangRuntime by lazy {
MoLangRuntime()
.setup()
.withQueryValue("entity", entity.struct)
.also {
it.environment.query.addFunction("passenger_count") { DoubleValue(entity.passengers.size.toDouble()) }
}
}
fun <T : RidingState> getState(id: ResourceLocation, constructor: (PokemonEntity) -> T): T {
val storedState = states[id]
if (storedState == null) {
val newState = constructor(entity)
states[id] = newState
return newState
}
return storedState as T
}
fun getController(entity: PokemonEntity): RideController? {
return entity.pokemon.riding.controller?.takeIf { it.condition.invoke(entity) }
}
/**
* Responsible for handling riding conditions and transitions amongst controllers. This will tick
* whenever the entity receives a tickControlled interaction.
*/
fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val controller = getController(entity) ?: return
val pose = controller.pose(entity)
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
//val speedPlayer = driver.deltaMovement.horizontalDistance()
//driver.displayClientMessage(Component.literal("Speed player: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$speedPlayer b/t")), true)
//val speedEntity = entity.deltaMovement.length() //* 20 * 60 * 60) / ( 1000 )
//println(speedEntity)
//driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("${String.format("%.2f", speedEntity)} km/h")), true)
}
fun speed(entity: PokemonEntity, driver: Player): Float {
val controller = getController(entity) ?: return 0.05F
this.lastSpeed = controller.speed(entity, driver)
return this.lastSpeed
}
fun controlledRotation(entity: PokemonEntity, driver: Player): Vec2 {
val controller = getController(entity) ?: return entity.rotationVector
val previousRotation = entity.rotationVector
val rotation = controller.rotation(entity, driver)
this.deltaRotation = Vec2(rotation.x - previousRotation.x, rotation.y - previousRotation.y)
return rotation
}
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
val controller = getController(entity) ?: return
return controller.clampPassengerRotation(entity, driver)
}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
val controller = getController(entity) ?: return
return controller.updatePassengerRotation(entity, driver)
}
fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val controller = getController(entity) ?: return Vec3.ZERO
return controller.velocity(entity, driver, input)
}
fun fuelBar(entity: PokemonEntity, driver: Player): Float {
val controller = getController(entity) ?: return 0.0f
return controller.fuelBar(entity, driver)
}
fun canJump(entity: PokemonEntity, driver: Player): Boolean {
val controller = getController(entity) ?: return false
return controller.canJump(entity, driver)
}
fun jumpVelocity(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
val controller = getController(entity) ?: return Vec3.ZERO
return controller.jumpForce(entity, driver, jumpStrength)
}
/**
* Used to supply the dela for the lerp() between velocity
* vectors. Called inertia for now as the way it feels when
* affecting the ride is somewhat akin to inertia.
*/
fun inertia(entity: PokemonEntity): Double {
val controller = getController(entity) ?: return 0.5
return controller.inertia(entity)
}
//Can be accomplished through the velocity controls
fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
val controller = getController(entity) ?: return null
return controller.gravity(entity, regularGravity)
}
//Maybe should be changed to be a function that conveys:
// "turn on use our camera system function"
fun shouldRoll(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.shouldRoll(entity)
}
fun shouldRotatePlayerHead(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.shouldRotatePlayerHead()
}
fun shouldRotatePokemonHead(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.shouldRotatePokemonHead()
}
//this is called just as often as
fun rotationOnMouseXY
( entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
val controller = getController(entity) ?: return Vec3.ZERO
return controller.rotationOnMouseXY( entity,
driver,
yMouse,
xMouse,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime )
}
/**
* Determines whether mouse input will affect the rotation of
* the rollable object. Need to remove this as it's not used anymore
*/
//I think this might better serve as a "use smoothing on angular velocity"useAngVelSmoothing
fun useAngVelSmoothing(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return true
return controller.useAngVelSmoothing(entity)
}
fun turnOffOnGround(entity: PokemonEntity): Boolean {
val controller = getController(entity) ?: return false
return controller.turnOffOnGround(entity)
}
fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val controller = getController(entity) ?: return Vec3(0.0, 0.0, 0.0)
return controller.angRollVel(entity, driver, deltaTime)
}
fun dismountOnShift(): Boolean {
val controller = getController(entity) ?: return false
return controller.dismountOnShift(entity)
}
}
@@ -1,70 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinitions
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
class RidingProperties(
val stats: Map<RidingStat, RidingStatDefinitions> = mapOf(),
val seats: List<Seat> = listOf(),
val conditions: List<Expression> = listOf(),
val controller: RideController? = null
) {
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RidingProperties {
val stats: Map<RidingStat, RidingStatDefinitions> = buffer.readMap(
{ buffer.readEnum<RidingStat>(RidingStat::class.java) },
{ RidingStatDefinitions.decode(buffer) }
)
val seats: List<Seat> = buffer.readList { _ -> Seat.decode(buffer) }
val conditions = buffer.readList { buffer.readString().asExpression() }
val controller = buffer.readNullable { _ ->
val key = buffer.readIdentifier()
val controller = RideControllerAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
controller.decode(buffer)
return@readNullable controller
}
return RidingProperties(stats = stats, seats = seats, conditions = conditions, controller = controller)
}
}
val canRide: Boolean
get() = seats.isNotEmpty() && controller != null
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeMap(
stats,
{ _, stat -> buffer.writeEnum(stat) },
{ _, stat -> stat.encode(buffer) }
)
buffer.writeCollection(seats) { _, seat -> seat.encode(buffer) }
buffer.writeCollection(conditions) { _, condition -> buffer.writeString(condition.getString()) }
buffer.writeNullable(controller) { _, controller -> controller.encode(buffer) }
}
fun calculate(stat: RidingStat, style: RidingStyle, boosts: Int): Float {
val definitions = stats[stat] ?: return 0F
return definitions.calculate(style, boosts)
}
fun hasStat(stat: RidingStat, style: RidingStyle): Boolean {
return stats[stat]?.ranges?.containsKey(style) == true
}
}
@@ -1,18 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding
/**
* Honestly having a base interface is probably unnecessary I'm just considering whether there's going to be some
* kind of shared state later.
*
* @author Hiroku
* @since October 6th, 2024
*/
interface RidingState
@@ -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.api.riding
/**
* A general medium for riding. Mainly used for categorizing which stats to pull from and for
* presenting a simplified view of riding to users.
*
* @author Hiroku
* @since February 17th, 2025
*/
enum class RidingStyle {
LAND,
LIQUID,
AIR
}
@@ -1,91 +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
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.controller.RidingAnimation
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.phys.Vec3
/**
* Seat Properties are responsible for the base information that would then be used to construct a Seat on an entity.
*/
data class Seat(
val locator: String = "seat_1",
val offset: Vec3 = Vec3.ZERO,
val poseOffsets: MutableList<SeatPoseOffset> = mutableListOf(),
val poseAnimations: MutableList<SeatPoseAnimations> = mutableListOf()
) : Encodable {
fun getOffset(poseType: PoseType) : Vec3 {
return poseOffsets.firstOrNull { poseType in it.poseTypes }?.offset ?: offset
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(locator)
buffer.writeDouble(offset.x)
buffer.writeDouble(offset.y)
buffer.writeDouble(offset.z)
buffer.writeCollection(poseOffsets) { _, offset -> offset.encode(buffer) }
buffer.writeCollection(poseAnimations) { _, animations -> animations.encode(buffer) }
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : Seat {
return Seat(
buffer.readString(),
Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()),
buffer.readList { SeatPoseOffset.decode(buffer) },
buffer.readList { SeatPoseAnimations.decode(buffer) }
)
}
}
}
class SeatPoseOffset {
val poseTypes = mutableSetOf<PoseType>()
var offset = Vec3.ZERO
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(poseTypes) { _, poseType -> buffer.writeString(poseType.name) }
buffer.writeDouble(offset.x)
buffer.writeDouble(offset.y)
buffer.writeDouble(offset.z)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : SeatPoseOffset {
val offset = SeatPoseOffset()
offset.poseTypes.addAll(buffer.readList { buffer.readString().let { PoseType.valueOf(it) } })
offset.offset = Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble())
return offset
}
}
}
class SeatPoseAnimations {
val poseTypes = mutableSetOf<PoseType>()
var animations = mutableListOf<RidingAnimation>()
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeCollection(poseTypes) { _, poseType -> buffer.writeString(poseType.name) }
buffer.writeCollection(animations) { _, animation -> animation.encode(buffer) }
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : SeatPoseAnimations {
val animations = SeatPoseAnimations()
animations.poseTypes.addAll(buffer.readList { buffer.readString().let { PoseType.valueOf(it) } })
animations.animations = buffer.readList { RidingAnimation.decode(buffer) }
return animations
}
}
}
@@ -1,146 +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.controller
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingState
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.writeIdentifier
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
/**
* A riding controller is the internal control mechanism for determining how a pokemon
* handles during riding. Controllers come into play once the pokemon being ridden has
* established the path it's capable of. For instance, a land based controller could have
* the pokemon experience drifting after movement, or generic movement. It's up to the
* controller to decide how the pokemon behaves.
*
* @since x.x.x
*/
interface RideController : Encodable, Decodable {
/** A reference key used to denote the individual controller */
val key: ResourceLocation
/**
* Specifies the provider of poses which helps control updating the current pose on the pokemon
* being ridden. Controllers are expected to define the pose types responsible for posing the entity
* through all expected conditions. It should define a default pose, that will be used in all cases
* where a pose does not meet the current conditions for the current mount. Typically, this will be
* an idle pose.
*/
val poseProvider: PoseProvider
/**
* Represents a condition that must be met for this controller to be active. For instance, this can be used
* to evaluate things such as current velocity or block state of the entity.
*/
val condition: (PokemonEntity) -> Boolean
/**
* Specifies the pose the ridden mount should be positioned in. This queries the pose provider using
* the entity as a means of determining the conditions the entity is currently under. This allows for
* dynamic pose updates.
*/
fun pose(entity: PokemonEntity) : PoseType {
return this.poseProvider.select(entity)
}
/**
* Calculates the current speed of the mount.
*/
fun speed(entity: PokemonEntity, driver: Player) : Float
/**
* Sets the rotation of the mount. This is typically based on the controlling driver and is manipulated as
* necessary.
*/
fun rotation(entity: PokemonEntity, driver: LivingEntity) : Vec2
/**
* Sets the change current change in rotation for that tick in three
* dimensions
*/
fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
return Vec3(0.0, 0.0, 0.0)
}
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
driver.yRot = entity.yRot
}
/**
* Manipulates the movement input as necessary by the controller. This is primarily used to apply limits
* to the types of movements. For instance, we can apply limits to any sort of sideways movement input, so that
* it would otherwise be slower than normal forward movement.
*/
fun velocity(entity: PokemonEntity, driver: Player, input: Vec3) : Vec3
fun fuelBar(entity: PokemonEntity, driver: Player): Float = 0.0f
fun canJump(entity: PokemonEntity, driver: Player) : Boolean
fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int) : Vec3
fun gravity(entity: PokemonEntity, regularGravity: Double) : Double? = null
fun inertia(entity: PokemonEntity ) : Double = 0.5
fun shouldRoll(entity: PokemonEntity): Boolean = false
fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
//If function is not overwritten by controllers then just perform the defualt
//rolling function which is roll on mousex and pitch on mousey
fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
if(driver !is Rollable) return Vec3.ZERO
//Might need to add the somothing here for default.
val rollable = driver as Rollable
return Vec3(0.0, yMouse, xMouse )
}
fun turnOffOnGround(entity: PokemonEntity): Boolean = false
fun dismountOnShift(entity: PokemonEntity): Boolean = false
fun getRuntime(entity: PokemonEntity) = entity.riding.runtime
fun shouldRotatePokemonHead(): Boolean = false
fun shouldRotatePlayerHead(): Boolean = false
fun <T : RidingState> getState(entity: PokemonEntity, constructor: (PokemonEntity) -> T): T {
return entity.riding.getState(key, constructor)
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeIdentifier(this.key)
}
}
@@ -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.controller
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
class RidingAnimation(
val fileName: String,
val animationName: String
) {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeString(fileName)
buffer.writeString(animationName)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf) : RidingAnimation {
return RidingAnimation(
buffer.readString(),
buffer.readString()
)
}
}
}
@@ -1,15 +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.controller.posing
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import java.util.function.Predicate
data class PoseOption(val pose: PoseType, val condition: Predicate<PokemonEntity>)
@@ -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.controller.posing
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
class PoseProvider(private val fallback: PoseType) {
private val options = mutableListOf<PoseOption>()
fun select(entity: PokemonEntity) : PoseType {
return this.options.stream()
.filter { it.condition.test(entity) }
.map { it.pose }
.findFirst()
.orElse(this.fallback)
}
fun with(option: PoseOption) : PoseProvider {
this.options.add(option)
return this
}
}
@@ -1,33 +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.events
import net.minecraft.world.entity.LivingEntity
data class SelectDriverEvent(val options: Set<LivingEntity>) {
private var result: DriverSuggestion? = null
fun result(): LivingEntity? {
return this.result?.entity
}
/**
* Suggests a particular living entity as a potential driver. Only non-negative
* values are permitted by this function. All priority values with lesser priorities
* will be outright ignored.
*/
fun suggest(target: LivingEntity, priority: Int) {
if (priority >= 0 && priority > (this.result?.priority ?: -1)) {
this.result = DriverSuggestion(target, priority)
}
}
data class DriverSuggestion(val entity: LivingEntity, val priority: Int)
}
@@ -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.api.riding.stats
import com.cobblemon.mod.common.api.berry.Flavor
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
/**
* A stat that is used for riding Pokémon. The exact implementation of each of these depends on the [com.cobblemon.mod.common.api.riding.controller.RideController].
*
* @author Hiroku
* @since February 17th, 2025
*/
enum class RidingStat(
val flavour: Flavor
) {
SPEED(Flavor.SWEET),
ACCELERATION(Flavor.SPICY),
SKILL(Flavor.DRY),
JUMP(Flavor.BITTER),
STAMINA(Flavor.SOUR);
companion object {
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
}
val displayName: Component
get() = lang("riding.stat.${name.lowercase()}.name")
val description: Component
get() = lang("riding.stat.${name.lowercase()}.desc")
}
@@ -1,68 +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.stats
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.readText
import com.cobblemon.mod.common.util.writeSizedInt
import com.cobblemon.mod.common.util.writeText
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
/**
* The definition of all the different information related to a specific [RidingStat] on a Pokémon species.
*
* @author Hiroku
* @since February 17th, 2025
*/
class RidingStatDefinitions {
/** A map from [RidingStyle] to a minimum and maximum value for this stat. */
val ranges = mutableMapOf<RidingStyle, IntRange>()
/** Display name for the stat, if null falls back to [RidingStat.displayName]. */
var displayName: Component? = null
/** Description for the stat, if null falls back to [RidingStat.description]. */
var description: Component? = null
fun calculate(style: RidingStyle, aprijuiceBoost: Int): Float {
val range = ranges[style] ?: ranges[RidingStyle.LAND] ?: return 0F
val boostQuotient = aprijuiceBoost / 255F
return range.first + (range.last - range.first) * boostQuotient
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeNullable(displayName) { _, it -> buffer.writeText(it) }
buffer.writeNullable(description) { _, it -> buffer.writeText(it) }
buffer.writeMap(
ranges,
{ _, it -> buffer.writeEnum(it) },
{ _, it ->
buffer.writeSizedInt(IntSize.U_BYTE, it.first)
buffer.writeSizedInt(IntSize.U_BYTE, it.last)
}
)
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): RidingStatDefinitions {
val displayName = buffer.readNullable { buffer.readText() }
val description = buffer.readNullable { buffer.readText() }
val ranges = buffer.readMap<RidingStyle, IntRange>(
{ buffer.readEnum<RidingStyle>(RidingStyle::class.java) },
{ IntRange(buffer.readSizedInt(IntSize.U_BYTE), buffer.readSizedInt(IntSize.U_BYTE)) }
)
return RidingStatDefinitions().apply {
this.displayName = displayName
this.description = description
this.ranges.putAll(ranges)
}
}
}
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.rules.component
import com.bedrockk.molang.Expression
import com.bedrockk.molang.ast.BooleanExpression
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
@@ -34,7 +33,7 @@ class FilterRuleComponent : SpawnRuleComponent {
val spawnSelector: SpawnDetailSelector = AllSpawnDetailSelector
val contextSelector: SpawningContextSelector = AllSpawningContextSelector
val allow: Expression = BooleanExpression(false)
val allow: Expression = BooleanExpression(true)
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
return if (spawnSelector.selects(detail) && contextSelector.selects(ctx)) {
@@ -0,0 +1,113 @@
/*
* 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.block
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.block.entity.StadiumBlockEntity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.Mirror
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.phys.BlockHitResult
class StadiumBlock(properties: Properties): BaseEntityBlock(properties) {
companion object {
val CODEC = simpleCodec(::StadiumBlock)
}
init {
registerDefaultState(stateDefinition.any().setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH))
}
override fun codec() = CODEC
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return StadiumBlockEntity(pos, state)
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState {
return this.defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, blockPlaceContext.horizontalDirection)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HorizontalDirectionalBlock.FACING)
}
override fun rotate(blockState: BlockState, rotation: Rotation): BlockState {
return blockState.setValue(
HorizontalDirectionalBlock.FACING, rotation.rotate(blockState.getValue(
HorizontalDirectionalBlock.FACING)))
}
override fun mirror(blockState: BlockState, mirror: Mirror): BlockState {
return blockState.rotate(mirror.getRotation(blockState.getValue(HorizontalDirectionalBlock.FACING)))
}
override fun onRemove(state: BlockState, world: Level, pos: BlockPos, newState: BlockState, moved: Boolean) {
if (!state.`is`(newState.block)) super.onRemove(state, world, pos, newState, moved)
}
//TODO: override getTicker
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
override fun useWithoutItem(
blockState: BlockState,
world: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
if (player !is ServerPlayer) return InteractionResult.SUCCESS
val blockEntity = world.getBlockEntity(blockPos)
if (blockEntity !is StadiumBlockEntity) return InteractionResult.SUCCESS
// Toggle isInUse state
blockEntity.isInUse = !blockEntity.isInUse
// debug
val activateMessage = if (blockEntity.isInUse) "Stadium Activated!" else "Stadium Deactivated!"
player.sendSystemMessage(text(activateMessage).red())
if (blockEntity.isInUse) {
// Generate flight path when activated
blockEntity.generateFlightPath(world, radiusX = 5.0, radiusZ = 3.0, height = 10.0, steps = 20)
} else {
// Clear path when deactivated
blockEntity.pathPoints.clear()
}
val message = "Stadium Pos: " + blockEntity.blockPos +
"\nPlayer 1 Pos: " + blockEntity.getPlayer1Pos() +
"\nPoke 1 Pos: " + blockEntity.getPoke1Pos() +
"\nPlayer 2 Pos: " + blockEntity.getPlayer2Pos() +
"\nPoke 2 Pos: " + blockEntity.getPoke2Pos()
player.sendSystemMessage(text(message).red())
blockEntity.setChanged()
world.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
return InteractionResult.SUCCESS
}
}
@@ -0,0 +1,132 @@
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneState
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.Vec3
class StadiumBlockEntity(
pos: BlockPos,
blockState: BlockState
): BlockEntity(
CobblemonBlockEntities.STADIUM,
pos,
blockState
) {
var isInUse = false
val droneState = DroneState()
val pathPoints: MutableList<Vec3> = mutableListOf()
fun generateFlightPath(world: Level, radiusX: Double, radiusZ: Double, height: Double, steps: Int) {
pathPoints.clear()
for (i in 0 until steps) {
val angle = (i.toDouble() / steps) * Math.PI * 2
val xOffset = radiusX * Math.cos(angle)
val zOffset = radiusZ * Math.sin(angle)
val basePos = Vec3(blockPos.x + xOffset, blockPos.y + height, blockPos.z + zOffset)
val adjustedPos = getValidPoint(world, basePos)
pathPoints.add(adjustedPos)
}
}
private fun getValidPoint(world: Level, start: Vec3): Vec3 {
val maxHeight = 40 // Max height for drone path
val stepHeight = 1.0 // Step size for raycast
var adjustedHeight = start.y
for (h in 0..maxHeight step stepHeight.toInt()) {
val checkPos = BlockPos(start.x.toInt(), start.y.toInt() - h, start.z.toInt()) // Convert to BlockPos
if (!world.getBlockState(checkPos).isAir) { // Check if block is solid
return Vec3(start.x, start.y - h - 1, start.z) // Adjust position
}
}
return start // No obstruction found
}
fun getPlayer1Pos(): BlockPos {
return blockPos.north(10)
}
fun getPlayer2Pos(): BlockPos {
return blockPos.south(10)
}
fun getPoke1Pos(): BlockPos {
return blockPos.north(5)
}
fun getPoke2Pos(): BlockPos {
return blockPos.south(5)
}
/**
* Saves data to NBT.
*/
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
// Save the isInUse state
tag.putBoolean("IsInUse", isInUse)
// Save path points
val pathTag = ListTag()
for (point in pathPoints) {
val pointTag = CompoundTag()
pointTag.putDouble("x", point.x)
pointTag.putDouble("y", point.y)
pointTag.putDouble("z", point.z)
pathTag.add(pointTag)
}
tag.put("PathPoints", pathTag)
}
/**
* Loads data from NBT.
*/
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
// Load the isInUse state
isInUse = tag.getBoolean("IsInUse")
// Load path points
pathPoints.clear()
val pathTag = tag.getList("PathPoints", 10) // 10 is the ID for CompoundTag
for (i in 0 until pathTag.size) {
val pointTag = pathTag.getCompound(i)
val point = Vec3(pointTag.getDouble("x"), pointTag.getDouble("y"), pointTag.getDouble("z"))
pathPoints.add(point)
}
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
//println("getUpdatePacket called for NestBlockEntity at $blockPos with egg: $eggComponent")
return ClientboundBlockEntityDataPacket.create(this)
}
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
val tag = saveWithoutMetadata(registryLookup)
//println("getUpdateTag called for NestBlockEntity at $blockPos with tag: $tag")
return tag
}
}
@@ -25,13 +25,7 @@ import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
@@ -256,7 +250,8 @@ object CobblemonClient {
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
CobblemonBlocks.GIMMIGHOUL_CHEST,
CobblemonBlocks.DISPLAY_CASE,
CobblemonBlocks.LECTERN
CobblemonBlocks.LECTERN,
CobblemonBlocks.STADIUM
)
this.createBoatModelLayers()
@@ -290,6 +285,7 @@ object CobblemonClient {
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.STADIUM, ::StadiumBlockRenderer)
}
private fun registerEntityRenderers() {
@@ -13,8 +13,8 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addEntityFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
@@ -42,11 +42,8 @@ import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Vector3f
class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
companion object {
@@ -21,18 +21,18 @@ import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.Multimap
import java.util.UUID
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import java.util.*
import org.joml.Vector3f
fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canRide: Boolean): InteractWheelGUI {
fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean): InteractWheelGUI {
val mountShoulder = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_shoulder.png"),
tooltipText = "cobblemon.ui.interact.mount.shoulder",
onPress = {
if (canMountShoulder) {
InteractPokemonPacket(pokemonID, true, false).sendToServer()
InteractPokemonPacket(pokemonID, true).sendToServer()
closeGUI()
}
}
@@ -41,32 +41,15 @@ fun createPokemonInteractGui(pokemonID: UUID, canMountShoulder: Boolean, canRide
iconResource = cobblemonResource("textures/gui/interact/icon_held_item.png"),
tooltipText = "cobblemon.ui.interact.give.item",
onPress = {
InteractPokemonPacket(pokemonID, false, false).sendToServer()
InteractPokemonPacket(pokemonID, false).sendToServer()
closeGUI()
}
)
val ride = InteractWheelOption(
iconResource = cobblemonResource("textures/gui/interact/icon_held_item.png"),
tooltipText = "cobblemon.ui.interact.ride",
onPress = {
if (canRide) {
InteractPokemonPacket(pokemonID, false, true).sendToServer()
closeGUI()
}
}
)
val options: Multimap<Orientation, InteractWheelOption> = ArrayListMultimap.create()
options.put(Orientation.TOP_RIGHT, giveItem)
if(canRide) {
options.put(Orientation.BOTTOM_LEFT, ride)
}
if (canMountShoulder) {
options.put(Orientation.TOP_LEFT, mountShoulder)
}
CobblemonEvents.POKEMON_INTERACTION_GUI_CREATION.post(PokemonInteractionGUICreationEvent(pokemonID, canMountShoulder, options))
return InteractWheelGUI(options, Component.translatable("cobblemon.ui.interact.pokemon"))
}
@@ -15,6 +15,6 @@ import net.minecraft.client.Minecraft
object InteractPokemonUIPacketHandler: ClientNetworkPacketHandler<InteractPokemonUIPacket> {
override fun handle(packet: InteractPokemonUIPacket, client: Minecraft) {
client.setScreen(createPokemonInteractGui(packet.pokemonID, packet.canMountShoulder, packet.canRide))
client.setScreen(createPokemonInteractGui(packet.pokemonID, packet.canMountShoulder))
}
}
@@ -0,0 +1,196 @@
/*
* 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.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.block.entity.StadiumBlockEntity
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneModel
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.state.BlockState
class StadiumBlockRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<StadiumBlockEntity> {
val context = RenderContext().also {
it.put(RenderContext.DO_QUIRKS, false)
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.BLOCK)
}
private var pathIndex = 0
private var animationTime = 0.0
private val droneSpeed = 0.10
override fun render(
blockEntity: StadiumBlockEntity,
tickDelta: Float,
poseStack: PoseStack,
multiBufferSource: MultiBufferSource,
light: Int,
overlay: Int
) {
val blockState = blockEntity.blockState
val yRot = blockState.getValue(HorizontalDirectionalBlock.FACING).toYRot()
val isClientSide = blockEntity.level?.isClientSide == true
if (!isClientSide) return
val stadiumBlockState = blockEntity.level?.getBlockState(blockEntity.blockPos) ?: return
if (stadiumBlockState.block != CobblemonBlocks.STADIUM) {
return
}
val droneState = blockEntity.droneState
poseStack.pushPose()
if (blockEntity.isInUse) {
if (blockEntity.pathPoints.isEmpty()) {
poseStack.popPose()
return
}
spawnDebugParticles(blockEntity) // debug particles
// Center the drone at the block's center
poseStack.translate(0.5, -3.0, 0.5)
//poseStack.mulPose(Axis.XP.rotationDegrees(180F))
//poseStack.mulPose(Axis.YP.rotationDegrees(180F))
val points = blockEntity.pathPoints
val nextIndex = (pathIndex + 1) % points.size
val currentPoint = points[pathIndex]
val nextPoint = points[nextIndex]
animationTime += tickDelta * droneSpeed
if (animationTime >= 1.0) {
animationTime = 0.0
pathIndex = nextIndex
}
val interpolatedPos = currentPoint.lerp(nextPoint, animationTime.toDouble())
println("DEBUG: Drone moving between points:")
println(" - Current Point: ${currentPoint}")
println(" - Next Point: ${nextPoint}")
println(" - Interpolated Position: $interpolatedPos")
poseStack.translate(interpolatedPos.x - blockEntity.blockPos.x,
interpolatedPos.y - blockEntity.blockPos.y,
interpolatedPos.z - blockEntity.blockPos.z)
println("DEBUG: Drone position interpolated to: $interpolatedPos")
poseStack.mulPose(Axis.YP.rotationDegrees(-yRot))
poseStack.mulPose(Axis.ZP.rotationDegrees(180F)) // flip the drone right side up
renderDrone(droneState, "sleep", tickDelta, poseStack, 1.0F, multiBufferSource, light)
} else {
val yRot = blockState.getValue(HorizontalDirectionalBlock.FACING).toYRot()
val poseEntry = poseStack.last()
val matrix = poseEntry.pose()
poseStack.translate(0.5, 0.0, 0.5)
poseStack.mulPose(Axis.YP.rotationDegrees(yRot))
val posX = matrix.m03()
val posY = matrix.m13()
val posZ = matrix.m23()
println("DEBUG: Drone is NOT in use. Rendering default position at: x=$posX, y=$posY, z=$posZ")
poseStack.mulPose(Axis.ZP.rotationDegrees(180F))
renderDrone(droneState, "sleep", tickDelta, poseStack, 1.0F, multiBufferSource, light)
}
poseStack.popPose()
}
fun renderDrone(
state: DroneState,
pose: String,
tickDelta: Float,
poseStack: PoseStack,
scale: Float = 1F,
multiBufferSource: MultiBufferSource,
light: Int,
) {
state.updatePartialTicks(tickDelta)
val droneResource: ResourceLocation = cobblemonResource("rotom_drone")
val model = VaryingModelRepository.getPoser(droneResource, state) as DroneModel
val texture = VaryingModelRepository.getTexture(droneResource, state)
val buffer = multiBufferSource.getBuffer(RenderType.entityCutout(texture))
model.bufferProvider = multiBufferSource
state.currentModel = model
state.setPose(pose)
model.context = context
context.put(RenderContext.TEXTURE, texture)
context.put(RenderContext.RENDER_STATE, RenderContext.RenderState.BLOCK)
context.put(RenderContext.POSABLE_STATE, state)
model.applyAnimations(
entity = null,
state = state,
headYaw = 0F,
headPitch = 0F,
limbSwing = 0F,
limbSwingAmount = 0F,
ageInTicks = state.animationSeconds * 20
)
poseStack.scale(scale, scale, scale)
// TODO: Make drone lighting dynamic (simply using the light arg makes the drone always appear dark)
model.withLayerContext(multiBufferSource, state, VaryingModelRepository.getLayers(droneResource, state)) {
model.render(context, poseStack, buffer, 200, OverlayTexture.NO_OVERLAY, -0x1)
}
model.setDefault()
}
private fun spawnDebugParticles(blockEntity: StadiumBlockEntity) {
val world = blockEntity.level ?: return
if (!world.isClientSide) return // Only run on the client-side
for (point in blockEntity.pathPoints) {
world.addParticle(
net.minecraft.core.particles.ParticleTypes.FLAME, // Test particle (changeable)
point.x, point.y, point.z, // Position of the particle
0.0, 0.02, 0.0 // Slight upward motion
)
}
// Spawn a special particle at the next interpolation point
if (blockEntity.pathPoints.isNotEmpty()) {
val nextPoint = blockEntity.pathPoints[(pathIndex + 1) % blockEntity.pathPoints.size]
world.addParticle(
net.minecraft.core.particles.ParticleTypes.END_ROD, // Different effect for next point
nextPoint.x, nextPoint.y, nextPoint.z,
0.0, 0.05, 0.0
)
}
}
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
@@ -51,11 +50,8 @@ import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderStateShard
import net.minecraft.client.renderer.RenderType
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import org.joml.Vector3f
/**
* A model that can be posed and animated using [PoseAnimation]s and [ActiveAnimation]s. This
@@ -566,16 +562,14 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
applyPose(state, pose, 1F)
val primaryAnimation = state.primaryAnimation
val shouldRotateHead = if(entity is PokemonEntity) entity.riding.shouldRotatePokemonHead(entity) else true
// Quirks will run if there is no primary animation running and quirks are enabled for this context.
if (primaryAnimation == null && context.request(RenderContext.DO_QUIRKS) != false) {
// Remove any quirk animations that don't exist in our current pose
state.quirks.keys.filterNot(pose.quirks::contains).forEach(state.quirks::remove)
// Tick all the quirks
pose.quirks.forEach {
it.apply(context, this, state, limbSwing, limbSwingAmount, ageInTicks, if(shouldRotateHead) headYaw else 0f, if(shouldRotateHead) headPitch else 0f, 1F)
it.apply(context, this, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch, 1F)
}
}
@@ -583,7 +577,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
// The pose intensity is the complement of the primary animation's curve. This is used to blend the primary.
state.poseIntensity = 1 - primaryAnimation.curve((state.animationSeconds - primaryAnimation.started) / primaryAnimation.duration)
// If the primary animation is done after running we're going to clean things up.
//Set headYaw and headPitch to zero if Pokemon shouldn't move head during riding
if (!primaryAnimation.run(
context,
this,
@@ -591,8 +584,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
limbSwing,
limbSwingAmount,
ageInTicks,
if(shouldRotateHead) headYaw else 0f,
if(shouldRotateHead) headPitch else 0f,
headYaw,
headPitch,
1 - state.poseIntensity
)
) {
@@ -604,11 +597,11 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
// Run active animations and return back any that are done and can be removed.
val removedActiveAnimations = state.activeAnimations.toList()
.filterNot { it.run(context, this, state, limbSwing, limbSwingAmount, ageInTicks, if(shouldRotateHead) headYaw else 0f, if(shouldRotateHead) headPitch else 0f, 1F) }
.filterNot { it.run(context, this, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch, 1F) }
state.activeAnimations.removeAll(removedActiveAnimations)
// Applies the pose's animations.
state.currentPose?.let(poses::get)
?.apply(context, this, state, limbSwing, limbSwingAmount, ageInTicks, if(shouldRotateHead) headYaw else 0f, if(shouldRotateHead) headPitch else 0f)
?.apply(context, this, state, limbSwing, limbSwingAmount, ageInTicks, headYaw, headPitch)
// Updates the locator positions now that all the animations are in effect. This is the last thing we do!
updateLocators(entity, state)
}
@@ -681,42 +674,24 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
var scale = 1F
// We could improve this to be generalized for other entities. First we'd have to figure out wtf is going on, though.
if (entity is PokemonEntity) {
if(entity.passengers.isNotEmpty() && entity.controllingPassenger is Rollable && (entity.controllingPassenger as Rollable).orientation != null){
val controllingPassenger = entity.controllingPassenger
val transformationMatrix = Matrix4f()
val center = Vector3f(0f, entity.bbHeight/2, 0f)
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f((controllingPassenger as Rollable).orientation!!))
transformationMatrix.translate(center.negate())
matrixStack.mulPose(transformationMatrix)
} else {
var yRot = Mth.lerp(state.getPartialTicks(), entity.yBodyRotO, entity.yBodyRot)
yRot = Mth.wrapDegrees(yRot)
matrixStack.mulPose(Axis.YP.rotationDegrees(180 - yRot))
}
matrixStack.mulPose(Axis.YP.rotationDegrees(180 - entity.yBodyRot))
matrixStack.pushPose()
matrixStack.scale(-1F, -1F, 1F)
scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
matrixStack.scale(scale, scale, scale)
} else if (entity is EmptyPokeBallEntity) {
var yRot = Mth.lerp(state.getPartialTicks(), entity.yRot, entity.yRotO)
yRot = Mth.wrapDegrees(yRot)
matrixStack.mulPose(Axis.YP.rotationDegrees(yRot))
matrixStack.mulPose(Axis.YP.rotationDegrees(entity.yRot))
matrixStack.pushPose()
matrixStack.scale(1F, -1F, -1F)
scale = 0.7F
matrixStack.scale(scale, scale, scale)
} else if (entity is GenericBedrockEntity) {
var yRot = Mth.lerp(state.getPartialTicks(), entity.yRot, entity.yRotO)
yRot = Mth.wrapDegrees(yRot)
matrixStack.mulPose(Axis.YP.rotationDegrees(yRot))
matrixStack.mulPose(Axis.YP.rotationDegrees(entity.yRot))
matrixStack.pushPose()
// Not 100% convinced we need the -1 on Y but if we needed it for the Poke Ball then probably?
matrixStack.scale(1F, -1F, 1F)
} else if (entity is NPCEntity) {
var yRot = Mth.lerp(state.getPartialTicks(), entity.yBodyRotO, entity.yBodyRot)
yRot = Mth.wrapDegrees(yRot)
matrixStack.mulPose(Axis.YP.rotationDegrees(180 - yRot))
matrixStack.mulPose(Axis.YP.rotationDegrees(180 - entity.yBodyRot))
matrixStack.pushPose()
matrixStack.scale(-1F, -1F, 1F)
}
@@ -51,7 +51,7 @@ open class BedrockActiveAnimation(
headPitch: Float,
intensity: Float
): Boolean {
return animation.run(model, state, state.animationSeconds - startedSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity).also {
return animation.run(context, model, state, state.animationSeconds - startedSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity).also {
if (!it) {
afterAction(context, state)
}
@@ -14,17 +14,15 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleOptions
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.genericRuntime
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.resolveDouble
import java.util.SortedMap
import java.util.TreeMap
import net.minecraft.CrashReport
import net.minecraft.ReportedException
import net.minecraft.client.Minecraft
@@ -35,6 +33,8 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import java.util.*
data class BedrockAnimationGroup(
val formatVersion: String,
@@ -149,40 +149,7 @@ data class BedrockAnimation(
/** Useful to have, gets set after loading the animation. */
var name: String = ""
fun run(
model: PosableModel,
state: PosableState,
animationSeconds: Float,
limbSwing: Float,
limbSwingAmount: Float,
ageInTicks: Float,
intensity: Float
): Boolean {
return run(
model.context,
model.relevantPartsByName,
model.rootPart,
state,
animationSeconds,
limbSwing,
limbSwingAmount,
ageInTicks,
intensity
)
}
fun run(
context: RenderContext?,
relevantPartsByName: Map<String, ModelPart>,
rootPart: Bone?,
state: PosableState,
animationSeconds: Float,
limbSwing: Float,
limbSwingAmount: Float,
ageInTicks: Float,
intensity: Float
): Boolean {
fun run(context: RenderContext, model: PosableModel, state: PosableState, animationSeconds: Float, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, intensity: Float): Boolean {
var animationSeconds = animationSeconds
if (shouldLoop) {
animationSeconds %= animationLength.toFloat()
@@ -197,7 +164,7 @@ data class BedrockAnimation(
runtime.environment.setSimpleVariable("age_in_ticks", DoubleValue(ageInTicks.toDouble()))
boneTimelines.forEach { (boneName, timeline) ->
val part = relevantPartsByName[boneName] ?: if (boneName == "root_part") (rootPart as? ModelPart) else null
val part = model.relevantPartsByName[boneName] ?: if (boneName == "root_part") (model.rootPart as ModelPart) else null
if (part !== null) {
if (!timeline.position.isEmpty()) {
val position = timeline.position.resolve(animationSeconds.toDouble(), runtime).scale(intensity.toDouble())
@@ -217,7 +184,7 @@ data class BedrockAnimation(
zRot += rotation.z.toFloat().toRadians()
}
} catch (e: Exception) {
val exception = IllegalStateException("Bad animation for entity: ${(context?.request(RenderContext.ENTITY))?.effectiveName()?.string ?: "unknown entity (riding animation?)"}", e)
val exception = IllegalStateException("Bad animation for entity: ${(model.context.request(RenderContext.ENTITY))!!.effectiveName().string}", e)
val crash = CrashReport("Cobblemon encountered an unexpected crash", exception)
val section = crash.addCategory("Animation Details")
section.setDetail("Pose", state.currentPose!!)
@@ -34,7 +34,7 @@ class BedrockPoseAnimation(val animation: BedrockAnimation) : PoseAnimation() {
}
override fun setupAnim(context: RenderContext, model: PosableModel, state: PosableState, limbSwing: Float, limbSwingAmount: Float, ageInTicks: Float, headYaw: Float, headPitch: Float, intensity: Float) {
animation.run(model, state, state.animationSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity)
animation.run(context, model, state, state.animationSeconds, limbSwing, limbSwingAmount, ageInTicks, intensity)
}
override fun applyEffects(entity: Entity?, state: PosableState, previousSeconds: Float, newSeconds: Float) {
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2025 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.drone
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
import com.cobblemon.mod.common.entity.PoseType
import com.google.gson.annotations.SerializedName
import net.minecraft.client.model.geom.ModelPart
/**
* A model for rendering the Rotom drone used by the stadium block.
*
* @author Flarkinater
* @since February 14th, 2025
*/
class DroneModel(root: Bone) : PosableModel(root) {
@Transient
@SerializedName("So this name can be literally anything, huh?")
override val rootPart = (root as ModelPart).registerChildWithAllChildren(root.children.entries.first().key)
var droneAnimations: Array<PoseAnimation> = emptyArray()
var droneQuirks: Array<ModelQuirk<*>> = emptyArray()
override fun registerPoses() {
registerPose(
poseType = PoseType.SLEEP,
animations = droneAnimations,
quirks = droneQuirks
)
}
}
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2025 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.drone
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
/**
* State for a drone Rotom attached to a stadium block.
*
* @author Flarkinater
* @since February 12th, 2025
*/
class DroneState() : PosableState() {
init {
setPose("sleep")
age = 0
currentPartialTicks = 0F
}
override fun getEntity() = null
override fun updatePartialTicks(partialTicks: Float) {
currentPartialTicks += partialTicks / 2
}
override val schedulingTracker = SchedulingTracker()
}
@@ -0,0 +1,49 @@
/*
* 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.gen3
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 WhiscashModel (root: ModelPart) : PokemonPosableModel(root) {
override val rootPart = root.registerChildWithAllChildren("whiscash")
override var portraitScale = 0.8F
override var portraitTranslation = Vec3(-0.35, 0.4, 0.0)
override var profileScale = 0.6F
override var profileTranslation = Vec3(-0.1, 0.6, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("whiscash", "cry") }
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
animations = arrayOf(
bedrock("whiscash", "water_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
animations = arrayOf(
bedrock("whiscash", "water_idle")
)
)
}
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.*
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.blockentity.BlockEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.fossil.FossilModel
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.AncientPokeBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.BeastBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PokeBallModel
@@ -79,12 +80,14 @@ object VaryingModelRepository {
"poke_balls",
"generic",
"block_entities",
"drone"
)
val poserDirectories: List<Pair<String, Class<out PosableModel>>> = listOf(
"bedrock/posers" to PosableModel::class.java,
"bedrock/pokemon/posers" to PokemonPosableModel::class.java,
"bedrock/fossils/posers" to FossilModel::class.java,
"bedrock/drone/posers" to DroneModel::class.java,
"bedrock/block_entities/posers" to BlockEntityModel::class.java,
"bedrock/npcs/posers" to PosableModel::class.java,
"bedrock/poke_balls/posers" to PosableModel::class.java,
@@ -116,6 +119,7 @@ object VaryingModelRepository {
.registerTypeAdapter(PosableModel::class.java, JsonModelAdapter(::PosableModel))
.registerTypeAdapter(PokemonPosableModel::class.java, JsonModelAdapter(::PokemonPosableModel))
.registerTypeAdapter(FossilModel::class.java, JsonModelAdapter(::FossilModel))
.registerTypeAdapter(DroneModel::class.java, JsonModelAdapter(::DroneModel))
.registerTypeAdapter(BlockEntityModel::class.java, JsonModelAdapter(::BlockEntityModel))
.registerTypeAdapter(Pose::class.java, PoseAdapter { JsonModelAdapter.model!! })
.registerTypeAdapter(ModelPartTransformation::class.java, ModelPartTransformationAdapter)
@@ -533,6 +537,7 @@ object VaryingModelRepository {
inbuilt("spewpa", ::SpewpaModel)
inbuilt("vivillon", ::VivillonModel)
inbuilt("barboach", ::BarboachModel)
inbuilt("whiscash", ::WhiscashModel)
inbuilt("combee", ::CombeeModel)
inbuilt("vespiquen", ::VespiquenModel)
inbuilt("lillipup", ::LillipupModel)
@@ -1,136 +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.player
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.Rideable
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.player.AbstractClientPlayer
import net.minecraft.util.Mth
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import org.joml.Vector3f
/**
* @author landonjw
*/
object MountedPlayerRenderer {
const val disableRollableRenderDebug: Boolean = false
fun render(player: AbstractClientPlayer, entity: PokemonEntity, stack: PoseStack, bob: Float, yBodyRot: Float, partialTicks: Float, i: Float) {
if(player.vehicle !is Rideable) return
val matrix = stack.last().pose()
val vehicle = player.vehicle as Rideable?
val entity = vehicle!!.riding.entity
val seatIndex = entity.passengers.indexOf(player)
val seat = entity.seats[seatIndex]
val delegate = entity.delegate as PokemonClientDelegate
val locator = delegate.locatorStates[seat.locator]
//Positions player
if (locator != null) {
val locatorOffset = locator.matrix.getTranslation(Vector3f())
val center = Vector3f(0f, entity.bbHeight/2, 0f)
var locatorOffsetToCenter = locatorOffset.sub(center, Vector3f())
//locatorOffsetToCenter.y -= 0.5f
val transformationMatrix = Matrix4f()
if (player is Rollable && player.shouldRoll()){
//transformationMatrix.rotate(Axis.YP.rotationDegrees(-yBodyRot))
//transformationMatrix.mulLocal(player.orientation)
}
val rotatedOffset = transformationMatrix.transformPosition(locatorOffsetToCenter, Vector3f()).add(center).sub(Vector3f(0f, player.bbHeight/2, 0f))
matrix.translate(rotatedOffset)
//Undo seat position
val playerPos = Vec3(
Mth.lerp(partialTicks.toDouble(), player.xOld, player.x),
Mth.lerp(partialTicks.toDouble(), player.yOld, player.y),
Mth.lerp(partialTicks.toDouble(), player.zOld, player.z),
)
val entityPos = Vec3(
Mth.lerp(partialTicks.toDouble(), entity.xOld, entity.x),
Mth.lerp(partialTicks.toDouble(), entity.yOld, entity.y),
Mth.lerp(partialTicks.toDouble(), entity.zOld, entity.z),
)
matrix.translate(playerPos.subtract(entityPos).toVector3f().negate())
}
//Rotates player
if (player is Rollable && player.shouldRoll() && !disableRollableRenderDebug) {
val center = Vector3f(0f, player.bbHeight / 2, 0f)
val transformationMatrix = Matrix4f()
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f(player.orientation))
transformationMatrix.translate(center.negate(Vector3f()))
//Pre-Undo Yaw
transformationMatrix.rotate(Axis.YP.rotationDegrees(yBodyRot+180f))
matrix.mul(transformationMatrix)
}
matrix.translate(0f, 0.5f, 0f)
}
fun animate(
pokemonEntity: PokemonEntity,
player: AbstractClientPlayer,
relevantPartsByName: Map<String, ModelPart>,
headYaw: Float,
headPitch: Float,
ageInTicks: Float,
limbSwing: Float,
limbSwingAmount: Float
) {
val seatIndex = pokemonEntity.passengers.indexOf(player).takeIf { it != -1 && it < pokemonEntity.seats.size } ?: return
val seat = pokemonEntity.seats[seatIndex]
val animations = seat.poseAnimations.firstOrNull { it.poseTypes.isEmpty() || it.poseTypes.contains(pokemonEntity.getCurrentPoseType()) }?.animations
?: return
val state = pokemonEntity.delegate as PokemonClientDelegate
relevantPartsByName.values.forEach {
it.xRot = 0F
it.yRot = 0F
it.zRot = 0F
}
state.runtime.environment.setSimpleVariable("age_in_ticks", DoubleValue(ageInTicks.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing", DoubleValue(limbSwing.toDouble()))
state.runtime.environment.setSimpleVariable("limb_swing_amount", DoubleValue(limbSwingAmount.toDouble()))
state.runtime.environment.setSimpleVariable("velocity", DoubleValue(pokemonEntity.deltaMovement.horizontalDistance()))
// TODO add the q.player reference, requires a cached thing or something on the client idk
for (animation in animations) {
val bedrockAnimation = BedrockAnimationRepository.getAnimationOrNull(animation.fileName, animation.animationName) ?: continue
bedrockAnimation.run(
context = null,
relevantPartsByName = relevantPartsByName,
rootPart = null,
state = state,
animationSeconds = state.animationSeconds,
limbSwing = limbSwing,
limbSwingAmount = limbSwingAmount,
ageInTicks = ageInTicks,
intensity = 1F
)
}
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.client.render.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.ClientBallDisplay
@@ -41,6 +40,7 @@ import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import kotlin.math.*
import net.minecraft.ChatFormatting
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Font.DisplayMode
@@ -56,8 +56,10 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import org.joml.*
import kotlin.math.*
import org.joml.Math
import org.joml.Quaternionf
import org.joml.Vector3f
import org.joml.Vector4f
class PokemonRenderer(
context: EntityRendererProvider.Context
@@ -78,7 +80,6 @@ class PokemonRenderer(
private const val SPACE = " "
private const val DISABLE_ROLLING_DEBUG = false
}
val ballContext = RenderContext().also {
@@ -139,12 +140,7 @@ class PokemonRenderer(
modelNow.setLayerContext(buffer, clientDelegate, VaryingModelRepository.getLayers(entity.pokemon.species.resourceIdentifier, clientDelegate))
if (entity.passengers.isNotEmpty()) {
renderRiding(entity, partialTicks, poseMatrix, buffer, packedLight)
}
else {
super.render(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
}
super.render(entity, entityYaw, partialTicks, poseMatrix, buffer, packedLight)
modelNow.green = 1F
modelNow.blue = 1F
@@ -155,38 +151,6 @@ class PokemonRenderer(
// Minecraft.getInstance().bufferBuilders.entityVertexConsumers.draw()
}
fun renderRiding(
entity: PokemonEntity,
partialTicks: Float,
poseMatrix: PoseStack,
buffer: MultiBufferSource,
packedLight: Int
) {
val driver = entity.firstPassenger ?: return
val rollable = driver as? Rollable ?: return
poseMatrix.pushPose()
if(!DISABLE_ROLLING_DEBUG && rollable.shouldRoll()){
val matrix = poseMatrix.last().pose()
val yaw = Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot)
val center = Vector3f(0f, entity.bbHeight/2, 0f)
val transformationMatrix = Matrix4f()
//Move origin to center of Pokemon
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f(rollable.orientation))
//Move origin to base of the entity
transformationMatrix.translate(center.negate(Vector3f()))
//Pre-undo yaw rotation
transformationMatrix.rotate(Axis.YP.rotationDegrees(yaw+180f))
matrix.mul(transformationMatrix)
}
super.render(entity, 0f, partialTicks, poseMatrix, buffer, packedLight)
poseMatrix.popPose()
}
fun renderTransition(
modelNow: PosableModel,
beamMode: Int,
@@ -492,4 +456,4 @@ class PokemonRenderer(
matrixStack.popPose()
}
}
}
@@ -8,9 +8,7 @@
package com.cobblemon.mod.common.entity
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.*
import net.minecraft.resources.ResourceLocation
import java.util.EnumSet
/**
* The type of a pose. Used for normalizing pose swapping for all models.
@@ -26,7 +24,6 @@ enum class PoseType {
FLY,
FLOAT,
SWIM,
GLIDE,
/** A pose for rendering on the left shoulder. Pose animations are given the player head yaw, pitch, and ageInTicks. */
SHOULDER_LEFT,
/** A pose for rendering on the right shoulder. Pose animations are given the player head yaw, pitch, and ageInTicks. */
@@ -42,18 +39,12 @@ enum class PoseType {
companion object {
val ALL_POSES = EnumSet.allOf(PoseType::class.java)
val FLYING_POSES = EnumSet.of(FLY, HOVER, GLIDE)
val FLYING_POSES = EnumSet.of(FLY, HOVER)
val SWIMMING_POSES = EnumSet.of(SWIM, FLOAT)
val STANDING_POSES = EnumSet.of(STAND, WALK)
val SHOULDER_POSES = EnumSet.of(SHOULDER_LEFT, SHOULDER_RIGHT)
val UI_POSES = EnumSet.of(PROFILE, PORTRAIT)
val MOVING_POSES = EnumSet.of(WALK, SWIM, FLY)
val STATIONARY_POSES = EnumSet.of(STAND, FLOAT, HOVER)
fun identified() : List<ResourceLocation> {
return ALL_POSES.stream()
.map { cobblemonResource(it.name.lowercase(Locale.getDefault())) }
.toList()
}
}
}
+18 -358
View File
@@ -8,8 +8,11 @@
package com.cobblemon.mod.common.entity.pokemon
import com.cobblemon.mod.common.*
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.entity.PokemonSender
@@ -34,10 +37,6 @@ import com.cobblemon.mod.common.api.pokemon.feature.StringSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.*
import com.cobblemon.mod.common.api.riding.events.SelectDriverEvent
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinitions
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.api.scheduling.afterOnServer
@@ -80,17 +79,9 @@ import com.cobblemon.mod.common.pokemon.ai.FormPokemonBehaviour
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.feature.StashHandler
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.riding.controllers.JetAirController.Companion.MAXTOPSPEED
import com.cobblemon.mod.common.pokemon.riding.controllers.JetAirController.Companion.MINTOPSPEED
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import com.mojang.serialization.Codec
import java.util.EnumSet
import java.util.Optional
import java.util.UUID
import java.util.concurrent.CompletableFuture
import kotlin.math.PI
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
@@ -139,18 +130,16 @@ import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Vector3f
import java.util.*
import java.util.concurrent.CompletableFuture
@Suppress("unused")
open class PokemonEntity(
world: Level,
pokemon: Pokemon = Pokemon().apply { isClient = world.isClientSide },
type: EntityType<out PokemonEntity> = CobblemonEntities.POKEMON,
) : ShoulderRidingEntity(type, world), PosableEntity, Shearable, Schedulable, Rideable, ScannableEntity {
) : ShoulderRidingEntity(type, world), PosableEntity, Shearable, Schedulable, ScannableEntity {
companion object {
@JvmStatic val SPECIES = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.STRING)
@JvmStatic val NICKNAME = SynchedEntityData.defineId(PokemonEntity::class.java, EntityDataSerializers.COMPONENT)
@@ -178,7 +167,7 @@ open class PokemonEntity(
const val BATTLE_LOCK = "battle"
const val EVOLUTION_LOCK = "evolving"
fun createAttributes(): AttributeSupplier.Builder = createLivingAttributes()
fun createAttributes(): AttributeSupplier.Builder = LivingEntity.createLivingAttributes()
.add(Attributes.FOLLOW_RANGE)
.add(Attributes.ATTACK_KNOCKBACK)
}
@@ -228,12 +217,6 @@ open class PokemonEntity(
get() = entityData.get(BATTLE_ID).isPresent
val friendship: Int
get() = entityData.get(FRIENDSHIP)
val seats: List<Seat>
get() = form.riding.seats
val rideProp: RidingProperties
get() = form.riding
var drops: DropTable? = null
@@ -243,8 +226,6 @@ open class PokemonEntity(
var enablePoseTypeRecalculation = true
override val riding: RidingManager = RidingManager(this)
/**
* The amount of steps this entity has traveled.
*/
@@ -348,7 +329,7 @@ open class PokemonEntity(
SPECIES -> refreshDimensions()
POSE_TYPE -> {
val value = entityData.get(data) as PoseType
if ((value == PoseType.FLY || value == PoseType.HOVER) && passengers.isEmpty()) {
if (value == PoseType.FLY || value == PoseType.HOVER) {
setNoGravity(true)
} else {
setNoGravity(false)
@@ -458,9 +439,6 @@ open class PokemonEntity(
this.tethering = null
this.pokemon.recall()
}
jumping = false
//This is so that pokemon in the pasture block are ALWAYS in sync with the pokemon box
//Before, pokemon entities in pastures would hold an old ref to a pokemon obj and changes to that would not appear to the underlying file
if (this.tethering != null && age % 20 == 0) {
@@ -533,6 +511,10 @@ open class PokemonEntity(
return super.isInvulnerableTo(damageSource)
}
override fun canRide(vehicle: Entity): Boolean {
return platform == PlatformType.NONE && super.canRide(vehicle)
}
/**
* A utility method that checks if this Pokémon has the [UncatchableProperty.uncatchable] property.
*
@@ -935,7 +917,7 @@ open class PokemonEntity(
if (hand == InteractionHand.MAIN_HAND && player is ServerPlayer && pokemon.getOwnerPlayer() == player) {
if (player.isShiftKeyDown) {
InteractPokemonUIPacket(this.getUUID(), canSitOnShoulder() && pokemon in player.party(), this.canRide(player)).sendToPlayer(
InteractPokemonUIPacket(this.getUUID(), canSitOnShoulder() && pokemon in player.party()).sendToPlayer(
player
)
} else {
@@ -1376,75 +1358,10 @@ open class PokemonEntity(
}
}
//Converted to kotin from the same named private function in entity.
private fun getInputVector(relative: Vec3, motionScaler: Float, facing: Float): Vec3 {
val d = relative.lengthSqr()
if (d < 1.0E-7) {
return Vec3.ZERO
}
// If the squared length is greater than 1, normalize; otherwise, use the vector as-is.
// May not want to normalize this
//val vec3 = (if (d > 1.0) relative.normalize() else relative).scale(motionScaler.toDouble())
val vec3 = relative
// Convert the facing angle (in degrees) to radians using the factor 0.017453292F.
val f = Mth.sin(facing * 0.017453292F)
val g = Mth.cos(facing * 0.017453292F)
// Rotate the vector by the facing angle.
return Vec3(
vec3.x * g - vec3.z * f,
vec3.y,
vec3.z * g + vec3.x * f
)
}
//override fun moveRelative(amount: Float, relative: Vec3 ) {
//super.moveRelative(amount, relative)
//val v = this.getInputVector(relative, 1.0f, this.getYRot());
//this.setDeltaMovement(v);
//}
override fun handleRelativeFrictionAndCalculateMovement(deltaMovement: Vec3 , friction: Float ): Vec3 {
val riders = this.passengers.filterIsInstance<LivingEntity>()
if ( riders.isEmpty() ) {
super.handleRelativeFrictionAndCalculateMovement(deltaMovement, friction)
}
//ignore this for now
else if( false )
{
val v = this.getInputVector(deltaMovement, 1.0f, this.getYRot());
//changing this will give the ride more or less inertia/handling/drift?
//A bit of overlap with other things but.
this.setDeltaMovement(this.deltaMovement.lerp(v, riding.inertia(this)));
//this.setDeltaMovement(this.deltaMovement.scale(this.speed.toDouble()));
//maybe move this to the travel function. The jump below never gets used.
this.move( MoverType.SELF, this.deltaMovement );
}
return this.deltaMovement
}
override fun travel(movementInput: Vec3) {
val prevBlockPos = this.blockPosition()
if (beamMode != 3) { // Don't let Pokémon move during recall
//this.deltaMovement = movementInput
//val riders = this.passengers.filterIsInstance<LivingEntity>()
//if ( !riders.isEmpty() ) {
//if ( false ) {
//val v = this.handleRelativeFrictionAndCalculateMovement(movementInput, 0.6f)
//this.moveRelative(this.speed, v);
//}
val riders = this.passengers.filterIsInstance<LivingEntity>()
if ( riders.isEmpty() ) {
super.travel(movementInput)
}
//super.travel(movementInput)
super.travel(movementInput)
this.updateBlocksTraveled(prevBlockPos)
}
if (isBattling && this.isInWater) {
@@ -1529,7 +1446,8 @@ open class PokemonEntity(
*
* @return If the backing [pokemon] has a non-null [Pokemon.nickname].
*/
override fun hasCustomName(): Boolean = pokemon.nickname != null && pokemon.nickname?.contents != PlainTextContents.EMPTY
override fun hasCustomName(): Boolean =
pokemon.nickname != null && pokemon.nickname?.contents != PlainTextContents.EMPTY
/**
* This method toggles the visibility of the entity name,
@@ -1653,9 +1571,6 @@ open class PokemonEntity(
}
override fun canBeLeashed() = true
// override fun canBeLeashedBy(player: Player): Boolean {
// return this.ownerUuid == null || this.ownerUuid == player.uuid
// }
override fun setLeashedTo(entity: Entity, bl: Boolean) {
super.setLeashedTo(entity, bl)
@@ -1674,262 +1589,6 @@ open class PokemonEntity(
*/
private fun createSidedPokemon(): Pokemon = Pokemon().apply { isClient = this@PokemonEntity.level().isClientSide }
override fun canRide(entity: Entity): Boolean {
return platform == PlatformType.NONE && super.canRide(entity)
}
// Takes in a requested stat type with a base minimum and base maximum and returns the interpolated
// stat based on the boost of that pokemons stat
fun getRideStat( stat: RidingStat, style: RidingStyle, baseMin: Double, baseMax: Double ): Double {
//Currenlty zero boost since aprijuice isn't implemented yet
val stat = this.rideProp.calculate(stat, style, 0 )
val statVal = (((baseMax - baseMin) / 100) * stat) + baseMin
return statVal
}
override fun canAddPassenger(passenger: Entity): Boolean {
return passengers.size < seats.size
}
fun getIsJumping() = jumping
fun setIsJumping(value: Boolean) {
jumping = value
}
// override fun moveRelative(amount: Float, relative: Vec3) {
// val vec3 = this.getInputVector(relative, amount, this.yRot)
// this.deltaMovement = deltaMovement.add(vec3)
// }
override fun tickRidden(driver: Player, movementInput: Vec3) {
super.tickRidden(driver, movementInput)
this.riding.tick(this, driver, movementInput)
val rotation = this.getControlledRotation(driver)
setRot(rotation.y, rotation.x)
this.yHeadRot = this.yRot
this.yBodyRot = this.yRot
this.yRotO = this.yRot
val riders = this.passengers.filterIsInstance<LivingEntity>()
//These are the only functions that matter and I have isolated them.
//I think using speed to scale the movement vector will not be working.
if ( !riders.isEmpty() ) {
val v = this.getInputVector(riding.velocity(this, driver, Vec3.ZERO), 1.0f, this.getYRot());
//changing this will give the ride more or less inertia/handling/drift?
//A bit of overlap with other things but.
this.setDeltaMovement(this.deltaMovement.lerp(v, riding.inertia(this)));
//this.setDeltaMovement(this.deltaMovement.scale(this.speed.toDouble()));
//maybe move this to the travel function. The jump below never gets used.
this.move( MoverType.SELF, this.deltaMovement );
}
if (this.riding.canJump(this, driver)) {
if (this.onGround()) {
if (this.jumpInputStrength > 0) {
// this.jump(this.jumpStrength, movementInput)
// this.jump()
val f = PI.toFloat() - this.yRot * PI.toFloat() / 180
val jumpVector = riding.jumpVelocity(this, driver, this.jumpInputStrength)
val velocity = jumpVector.yRot(f)
// Rotate the jump vector f degrees around the Y axis
// val velocity = Vec3d(-sin(f) * jumpVector.x, jumpVector.y, cos(f) * jumpVector.z)
this.addDeltaMovement(velocity)
hasImpulse = true
jumping = false
}
this.jumpInputStrength = 0
}
}
// val lookAngle: Vec3 = driver.getLookAngle()
// if (!driver.isNearGround() || lookAngle.y >= 0.3) {
// setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
// entityData.set(POSE_TYPE, PoseType.HOVER)
//
// val deltaMovement = this.deltaMovement
// this.setDeltaMovement(
// lookAngle.x * 0.1 + (lookAngle.x * 1.5 - deltaMovement.x) * 1,
// lookAngle.y * 0.1 + (lookAngle.y * 1.5 - deltaMovement.y) * 1,
// lookAngle.z * 0.1 + (lookAngle.z * 1.5 - deltaMovement.z) * 1
// )
// }
// else {
// setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
// this.setDeltaMovement(0.0, 0.0, 0.0)
// }
}
fun Entity.isNearGround(): Boolean {
val blockBelow: BlockPos = this.blockPosition().below()
return this.level().getBlockState(blockBelow).isSolid()
}
// override fun jump() {
//// val vec3d = this.velocity
////
////
////
//// this.setVelocity(vec3d.x, this.jumpVelocity.toDouble(), vec3d.z)
//// if (this.isSprinting) {
//// val f = this.yaw * 0.017453292f
//// this.velocity =
//// velocity.add((-MathHelper.sin(f) * 0.2f).toDouble(), 0.0, (MathHelper.cos(f) * 0.2f).toDouble())
//// }
////
//// this.velocityDirty = true
// }
private fun getControlledRotation(controller: LivingEntity): Vec2 {
return this.riding.controlledRotation(this, controller as Player)
}
override fun onPassengerTurned(entityToUpdate: Entity) {
return this.riding.clampPassengerRotation(this, entityToUpdate as? LivingEntity ?: return)
}
override fun positionRider(passenger: Entity, positionUpdater: MoveFunction) {
if (this.hasPassenger(passenger)) {
val index = this.passengers.indexOf(passenger).takeIf { it >= 0 && it < this.seats.size } ?: return
val seat = this.seats[index]
val seatOffset = seat.getOffset(this.getCurrentPoseType()).toVector3f()
val center = Vector3f(0f, this.bbHeight/2, 0f)
val seatToCenter = center.sub(seatOffset, Vector3f())
val matrix = (this.passengers.first() as Rollable).orientation ?: Matrix3f().rotate((180f-passenger.yRot).toRadians(), Vector3f(0f, 1f, 0f))
val rotatedOffset = matrix.transform(seatToCenter, Vector3f()).add(center).sub(Vector3f(0f, passenger.bbHeight/2, 0f))
positionUpdater.accept(passenger, this.x + rotatedOffset.x, this.y + rotatedOffset.y, this.z + rotatedOffset.z)
if (passenger is LivingEntity) {
this.riding.updatePassengerRotation(this, passenger)
this.riding.clampPassengerRotation(this, passenger)
}
}
}
override fun getControllingPassenger(): LivingEntity? {
val riders = this.passengers.filterIsInstance<LivingEntity>()
if (riders.isEmpty()) {
riding.states.clear()
return null
}
val event = SelectDriverEvent(riders.toSet())
val owner = riders.find { it.uuid == ownerUUID }
if (owner != null) {
event.suggest(owner, 0)
}
CobblemonEvents.SELECT_DRIVER.emit(event)
return event.result()
}
override fun getDismountLocationForPassenger(passenger: LivingEntity): Vec3 {
// val seat = this.riding.seats.firstOrNull { it.occupant() == passenger }
// seat?.dismount()
return super.getDismountLocationForPassenger(passenger)
}
override fun getRiddenInput(controller: Player, movementInput: Vec3): Vec3 {
return this.riding.velocity(this, controller, movementInput)
}
override fun getRiddenSpeed(controller: Player): Float {
return this.riding.speed(this, controller)
}
var jumpInputStrength: Int = 0 // move this
override fun onPlayerJump(strength: Int) {
// See if this controls the hot bar element
var strength = strength
if (strength < 0) {
strength = 0
} else {
// this.jumping = true
// update anger? hunwah
}
this.jumpInputStrength = strength
if (strength >= 90) {
// this.jumpStrength = 1.0f
} else {
// this.jumpStrength = 0.4f + 0.4f * strength.toFloat() / 90.0f
}
}
override fun canJump(): Boolean {
return true
}
override fun handleStartJump(height: Int) {
this.jumping = true
}
fun side() = if (delegate is PokemonServerDelegate) "SERVER" else "CLIENT"
override fun handleStopJump() {
jumping = false
// Set back to land pose type?
}
/*
These two functions (fluids and ground) need to become riding configurable and
dependent. Also, the onGround() function seems to affect quite a few spots in
code and would likely need to be changed to be something more robust instead of
just overriding this method.
*/
override fun isAffectedByFluids(): Boolean {
var fluidAffected = true
if( this.hasControllingPassenger() )
{
//Change this so it calls something from the controller to check
//if the specific controller wants to ignore fluid physics since
//not every single one will want to
fluidAffected = false
}
return fluidAffected
}
//this seems a bit hacky to me seeing as how many spots in the base classes its used.
//However, there are odd interactions with the controllers when they are meant to be
//flying or swimming but they are touching the ground and this needs to be prevented.
//Having it be able to be turned off by the flying or swimming controllers is the
//temp solution I have found.
override fun onGround() : Boolean
{
if( riding.turnOffOnGround( this ) )
{
return false
}
return super.onGround()
}
//I think already mentioned but should maybe be riding controller configurable
override fun dismountsUnderwater(): Boolean {
return false
}
override fun getDefaultGravity(): Double {
val regularGravity = super.getDefaultGravity()
if (this.passengers.isEmpty()) {
return regularGravity
}
return riding.gravity(this, regularGravity) ?: regularGravity
}
/**
* A utility method to resolve the [Codec] of [Pokemon] aware if the [world] is client sided or not.
*
@@ -1952,4 +1611,5 @@ open class PokemonEntity(
override fun resolveEntityScan(): LivingEntity {
return this
}
}
}
@@ -9,9 +9,9 @@
package com.cobblemon.mod.common.entity.pokemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.entity.PokemonSender
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
@@ -21,8 +21,8 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.getIsSubmerged
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.update
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
@@ -33,12 +33,9 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.network.chat.Component
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.pathfinder.PathType
import org.joml.Matrix3f
import org.joml.Vector3f
/** Handles purely server logic for a Pokémon */
class PokemonServerDelegate : PokemonSideDelegate {
@@ -199,7 +196,7 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
fun updatePoseType() {
if (!entity.enablePoseTypeRecalculation || entity.passengers.isNotEmpty()) {
if (!entity.enablePoseTypeRecalculation) {
return
}
@@ -1,33 +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.gametest
import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.api.pokemon.PokemonProperties.Companion.parse
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.core.BlockPos
import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper
class CobblemonGameTests {
@GameTest(template = "")
fun spawnBidoof(context: GameTestHelper) {
context.succeedWhen {
val bidoof = parse("bidoof").create()
bidoof.ivs[Stats.ATTACK] = 31
val target = BlockPos(context.relativePos(BlockPos(0, 0, 0)))
context.assertEntityPresent(CobblemonEntities.POKEMON)
context.assertEntityData(target, CobblemonEntities.POKEMON, PokemonEntity::pokemon, bidoof)
}
}
}
@@ -238,6 +238,7 @@ object CobblemonItemGroups {
entries.accept(CobblemonItems.PC)
entries.accept(CobblemonItems.HEALING_MACHINE)
entries.accept(CobblemonItems.PASTURE)
entries.accept(CobblemonItems.STADIUM)
entries.accept(CobblemonItems.GILDED_CHEST)
entries.accept(CobblemonItems.YELLOW_GILDED_CHEST)
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.orientation
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import org.joml.Matrix3f
class S2CUpdateOrientationPacket internal constructor(val entityId: Int, val orientation: Matrix3f?) : NetworkPacket<S2CUpdateOrientationPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeInt(entityId)
buffer.writeBoolean(orientation != null)
if (orientation != null) {
buffer.writeMatrix3f(orientation)
}
}
companion object {
val ID = cobblemonResource("s2c_update_orientation")
fun decode(buffer: RegistryFriendlyByteBuf): S2CUpdateOrientationPacket {
val entityId = buffer.readInt()
val orientation = if (buffer.readBoolean()) buffer.readMatrix3f() else null
return S2CUpdateOrientationPacket(entityId, orientation)
}
}
}
@@ -24,18 +24,17 @@ import java.util.UUID
* @author Village
* @since January 7th, 2023
*/
class InteractPokemonUIPacket(val pokemonID: UUID, val canMountShoulder: Boolean, val canRide: Boolean): NetworkPacket<InteractPokemonUIPacket> {
class InteractPokemonUIPacket(val pokemonID: UUID, val canMountShoulder: Boolean): NetworkPacket<InteractPokemonUIPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pokemonID)
buffer.writeBoolean(canMountShoulder)
buffer.writeBoolean(canRide)
}
companion object {
val ID = cobblemonResource("interact_pokemon_ui")
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonUIPacket(buffer.readUUID(), buffer.readBoolean(), buffer.readBoolean())
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonUIPacket(buffer.readUUID(), buffer.readBoolean())
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.server.orientation
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readMatrix3f
import com.cobblemon.mod.common.util.writeMatrix3f
import net.minecraft.network.RegistryFriendlyByteBuf
import org.joml.Matrix3f
class C2SUpdateOrientationPacket internal constructor(val orientation: Matrix3f?) : NetworkPacket<C2SUpdateOrientationPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeBoolean(orientation != null)
if (orientation != null) {
buffer.writeMatrix3f(orientation)
}
}
companion object {
val ID = cobblemonResource("c2s_update_orientation")
fun decode(buffer: RegistryFriendlyByteBuf): C2SUpdateOrientationPacket {
val orientation = if (buffer.readBoolean()) buffer.readMatrix3f() else null
return C2SUpdateOrientationPacket(orientation)
}
}
}
@@ -24,15 +24,14 @@ import java.util.UUID
* @author Village
* @since January 7th, 2023
*/
class InteractPokemonPacket(val pokemonID: UUID, val mountShoulder: Boolean, val ride: Boolean) : NetworkPacket<InteractPokemonPacket> {
class InteractPokemonPacket(val pokemonID: UUID, val mountShoulder: Boolean) : NetworkPacket<InteractPokemonPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pokemonID)
buffer.writeBoolean(mountShoulder)
buffer.writeBoolean(ride)
}
companion object {
val ID = cobblemonResource("interact_pokemon")
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonPacket(buffer.readUUID(), buffer.readBoolean(), buffer.readBoolean())
fun decode(buffer: RegistryFriendlyByteBuf) = InteractPokemonPacket(buffer.readUUID(), buffer.readBoolean())
}
}
@@ -1,23 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.serverhandling.orientation
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object OrientationPacketHandler : ServerNetworkPacketHandler<C2SUpdateOrientationPacket> {
override fun handle(packet: C2SUpdateOrientationPacket, server: MinecraftServer, player: ServerPlayer) {
if (player is Rollable) {
player.updateOrientation { _ -> packet.orientation }
}
}
}
@@ -24,11 +24,9 @@ object InteractPokemonHandler : ServerNetworkPacketHandler<InteractPokemonPacket
return
}
pokemonEntity.tryMountingShoulder(player)
} else if (packet.ride) {
player.startRiding(pokemonEntity)
} else {
pokemonEntity.offerHeldItem(player, player.mainHandItem)
}
}
}
}
}
@@ -28,7 +28,6 @@ import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroup
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.entity.PoseType
@@ -97,8 +96,6 @@ class FormData(
private var _height: Float? = null,
@SerializedName("weight")
private var _weight: Float? = null,
@SerializedName("riding")
private var _riding: RidingProperties? = null,
val requiredMove: String? = null,
val requiredItem: String? = null,
/** For forms that can accept different items (e.g. Arceus-Grass: Meadow Plate or Grassium-Z). */
@@ -175,8 +172,6 @@ class FormData(
val eggGroups: Set<EggGroup>
get() = _eggGroups ?: species.eggGroups
val riding: RidingProperties
get() = _riding ?: species.riding
/**
* The height in decimeters
@@ -298,7 +293,6 @@ class FormData(
pb.writeString(ability.template.name)
}
}
buffer.writeNullable(_riding) { pb, riding -> riding.encode(buffer) }
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
@@ -335,7 +329,6 @@ class FormData(
}.forEach { add(Priority.NORMAL, it) }
}
}
this._riding = buffer.readNullable { pb -> RidingProperties.decode(buffer) }
}
/**
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonBuildDetails
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToPlayers
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.abilities.Abilities
@@ -59,8 +58,6 @@ import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SettableObservable
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.storage.StoreCoordinates
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
@@ -543,9 +540,6 @@ open class Pokemon : ShowdownIdentifiable {
*/
internal var heldItem: ItemStack = ItemStack.EMPTY
val riding: RidingProperties
get() = this.form.riding
init {
storeCoordinates.subscribe { if (it != null && it.store !is PCStore && this.tetheringId != null) afterOnServer(seconds = 0.05F) { this.tetheringId = null } }
storeCoordinates.subscribe {
@@ -555,7 +549,6 @@ open class Pokemon : ShowdownIdentifiable {
}
}
open fun getStat(stat: Stat) = Cobblemon.statProvider.getStatForPokemon(this, stat)
/**
@@ -31,7 +31,6 @@ import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.entity.PoseType.Companion.FLYING_POSES
@@ -124,9 +123,6 @@ class Species : ClientDataSynchronizer<Species>, ShowdownIdentifiable {
var forms = mutableListOf<FormData>()
private set
var riding: RidingProperties = RidingProperties()
private set
val standardForm by lazy { FormData(_evolutions = this.evolutions).initialize(this) }
var labels = hashSetOf<String>()
@@ -253,8 +249,6 @@ class Species : ClientDataSynchronizer<Species>, ShowdownIdentifiable {
pb.writeBoolean(ability is CommonAbility)
pb.writeString(ability.template.name)
}
this.riding.encode(buffer)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
@@ -296,7 +290,6 @@ class Species : ClientDataSynchronizer<Species>, ShowdownIdentifiable {
pool.add(Priority.NORMAL, it)
}
}
this.riding = RidingProperties.decode(buffer)
this.initialize()
}
@@ -1,383 +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.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.controllers.HelicopterAirController.Companion.ROTATION_LIMIT
import com.cobblemon.mod.common.pokemon.riding.controllers.JetAirController.Companion
import com.cobblemon.mod.common.pokemon.riding.controllers.JetAirController.Companion.MAXTOPSPEED
import com.cobblemon.mod.common.pokemon.riding.controllers.JetAirController.Companion.MINSPEED
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
import net.minecraft.world.phys.Vec3
import kotlin.math.*
class BirdAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.GLIDE) {
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
player.zza == 0.0f && it.deltaMovement.length() > 0.3 })
.with(PoseOption(PoseType.FLY) {
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
player.zza > 0.0f // Checks if the player is actively controlling movement // Checks if the player is actively controlling movement
})
//.with(PoseOption(PoseType.FLY) { entity ->
// val player = entity.passengers.firstOrNull() as? Player ?: false
// player.// This is the `zza` value })
//.with(PoseOption(PoseType.GLIDE) { it.deltaMovement.length() > 0.51 })
//.with(PoseOption(PoseType.FLY) { it.deltaMovement.length() > 0.1 && it.deltaMovement.length() <= 0.51 })
override val condition: (PokemonEntity) -> Boolean = { true }
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var gravity: Expression = "0".asExpression()
private set
var horizontalAcceleration: Expression = "0.1".asExpression()
private set
var verticalAcceleration: Expression = "0.1".asExpression()
private set
var speed: Expression = "1.0".asExpression()
private set
var lastVelocity = Vec3(0.0,0.0,0.0)
private set
var currSpeed = 0.0
private set
var lastPitch = 0.0f
private set
//This function might just be good for syncing the ridecontroller speed
//with the entity speed?
override fun speed(entity: PokemonEntity, driver: Player): Float {
//Set the speed since this will need to be done to transfer speed between rides?
//maxSpeed = entity.speed.toDouble()
//retrieve stats
val topSpeed = entity.getRideStat( RidingStat.SPEED, RidingStyle.AIR, MINTOPSPEED, MAXTOPSPEED )
val accel = entity.getRideStat( RidingStat.ACCELERATION, RidingStyle.AIR, MINACCEL, MAXACCEL )
val maxGlideSpeed = topSpeed * 2
var glideSpeedChange = 0.00
//speed up and slow down based on input
if( driver.zza > 0.0 && currSpeed < topSpeed)
{
currSpeed = min( currSpeed + accel , topSpeed)
}
else if ( driver.zza < 0.0 && currSpeed > 0.0)
{
//Decelerate is now always a constant half of max acceleration.
currSpeed = max( currSpeed - (MAXACCEL / 2) , 0.0)
}
val rollable = driver as? Rollable
if ( rollable != null )
{
glideSpeedChange = sin(Math.toRadians( rollable.pitch.toDouble() ))
glideSpeedChange = glideSpeedChange.pow(3)
//TODO: Possibly create a deadzone around parallel where speed isn't taken away from?
if( glideSpeedChange <= 0.0 )
{
//Ensures that a propelling force is still able to be applied when
//climbing in height
if( driver.zza <= 0 )
{
//speed decrease should be 2x speed increase?
currSpeed = max( currSpeed + (MAXACCEL) * glideSpeedChange , 0.0 )
}
}
else
{
// only add to the speed if it hasn't exceeded the current
//glide angles maximum amount of speed that it can give.
currSpeed = min( currSpeed + ((MAXACCEL * 2) * glideSpeedChange) , topSpeed * 2)
}
}
//air resistance
currSpeed = max( currSpeed - 0.005, 0.0)
println(currSpeed)
return 0.0f
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
var yVel = 0.0
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
val rollable = driver as? Rollable
if( rollable != null ) {
//yVel = sin(Math.toRadians(rollable.pitch.toDouble() - lastPitch ))*-10
lastPitch = rollable.pitch
//Need to deadzone this when straight up or down
leftForce = -1.0 * sin(Math.toRadians( rollable.roll.toDouble() ) )
upForce = -1.0 * sin(Math.toRadians( rollable.pitch.toDouble() ))
forwardForce = cos(Math.toRadians( rollable.pitch.toDouble() ))
}
//TODO: Add in an upwards and downwards force when pressing space maybe?
//Leave out tap to flap right now
/*
var jumpVector = Vec3.ZERO
if (entity.jumpInputStrength > 0) {
val runtime = getRuntime(entity)
runtime.environment.query.addFunction("jump_strength") { DoubleValue(entity.jumpInputStrength.toDouble()) }
val jumpForces = this.jumpVector.map { runtime.resolveFloat(it) }
jumpVector = Vec3(jumpForces[0].toDouble(), jumpForces[1].toDouble(), jumpForces[2].toDouble())
jumpVector = jumpVector.toVector3f().mul((driver as Rollable).orientation).toVec3d()
entity.jumpInputStrength = 0
entity.addDeltaMovement(jumpVector)
}
*/
val velocity = Vec3(0.0 , upForce * currSpeed, forwardForce * currSpeed)
return velocity
}
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val rollable = driver as? Rollable
val handling = entity.getRideStat( RidingStat.SKILL, RidingStyle.AIR, MINHANDLING, MAXHANDLING)
val rotationChangeRate = 10.0
if( rollable != null )
{
var yawForce = rotationChangeRate * sin( Math.toRadians( rollable.roll.toDouble() ))
//for a bit of correction on the rolls limit it to a quarter the amount
var pitchForce = -0.25 * rotationChangeRate * abs(sin( Math.toRadians( rollable.roll.toDouble() )))
//limit rotation modulation when pitched up heavily or pitched down heavily
yawForce *= abs(cos( Math.toRadians( rollable.pitch.toDouble() )))
pitchForce *= abs(cos( Math.toRadians( rollable.pitch.toDouble() ))) * 1.5
//Create a deadzone for easier leveling out
if( rollable.roll <= 0.0 && rollable.roll >= -0.0)
{
yawForce = 0.0
pitchForce = 0.0
}
return Vec3( yawForce, pitchForce, 0.0)
}
/*
val rotLimit = 15.0
var rollForce = -1.0 * driver.xxa * rotationChangeRate
//TODO: Fix accumulated movement causing pitch and roll out of set bounds
if( rollable != null ) {
//If the roll or pitch have exceeded the limit then do not modulate that
//rotation in that direction further
val rotLim = 30.0
rollForce *= 1.0.pow(-1.0 * (abs(rollable.roll.toDouble()) / rotLimit))
if( rollable.roll >= rotLimit && rollForce > 0.0)
{
rollForce = 0.0;
}
if( rollable.roll <= -rotLimit && rollForce < 0.0)
{
rollForce = 0.0;
}
//Ignore x,y,z its angular velocity:
//yaw, pitch, roll
return Vec3( 0.0, 0.0, rollForce)
}
*/
return Vec3(0.0, 0.0, 0.0)
}
override fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
if(driver !is Rollable) return Vec3.ZERO
val rollable = driver as Rollable
val handling = entity.getRideStat( RidingStat.SKILL, RidingStyle.AIR, MINHANDLING, MAXHANDLING)
//var yMouse = yMouseSmoother.getNewDeltaValue(angVecMouse.y * 0.15f, d);
//var xMouse = xMouseSmoother.getNewDeltaValue(cursorDeltaX * 0.15f, d);
var xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1, deltaTime * 4);
val YInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1, deltaTime * 4);
//limit rolling based on healing
val rotLimit = handling * 1.5
if( abs( rollable.roll + xInput ) < rotLimit )
{
if( sign(xInput) == sign( rollable.roll ).toDouble() ) {
val s = sign(rollable.roll)
val d = abs(abs(rollable.roll) - rotLimit)
xInput *= (d.pow(2)) / (rotLimit.pow(2))
}
}
//if( abs(rollable.roll) > rotLimit )
else
{
val s = sign(rollable.roll)
val d = abs(rollable.roll) - rotLimit
if( sign(xInput) == sign( rollable.roll ).toDouble() ) {
xInput = 0.0
}
}
/*
if( xMouse > 0 )
{
if( rollable.roll > rotLimit )
{
//val adjustedX = 0.5* xMouse * ( (rotLimit - rollable.roll) / ( rotLimit) )
return Vec3(0.0, YInput, 0.0 )
}
else
{
return Vec3(0.0, YInput, xInput * -0.1 )
}
}else{
if( rollable.roll < -1.0 * rotLimit )
{
//val adjustedX = 0.5* xMouse * ( (rotLimit + rollable.roll) / ( rotLimit) )
return Vec3(0.0, YInput, 0.0 )
}
else
{
return Vec3(0.0, YInput, xInput * -0.1 )
}
}
*/
//Ignore x,y,z its angular velocity:
//yaw, pitch, roll
return Vec3(0.0, YInput, xInput )
}
override fun canJump(
entity: PokemonEntity,
driver: Player
): Boolean {
return true
}
override fun jumpForce(
entity: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
//Going to use my own gravity calculations for now.
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double {
return 0.0
}
override fun shouldRoll(entity: PokemonEntity) = true
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(gravity.toString())
buffer.writeString(horizontalAcceleration.toString())
buffer.writeString(verticalAcceleration.toString())
buffer.writeString(speed.toString())
buffer.writeDouble(this.lastVelocity.x)
buffer.writeDouble(this.lastVelocity.y)
buffer.writeDouble(this.lastVelocity.z)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readString().asExpression()
horizontalAcceleration = buffer.readString().asExpression()
verticalAcceleration = buffer.readString().asExpression()
speed = buffer.readString().asExpression()
val x = buffer.readDouble()
val y = buffer.readDouble()
val z = buffer.readDouble()
this.lastVelocity = Vec3(x, y, z)
}
companion object {
val KEY = cobblemonResource("air/bird")
val MAXTOPSPEED = 1.0 // 20 bl/s
val MINTOPSPEED = 0.35 // 7 bl/s
//val MINSPEED = 0.25 // 5 bl/s
//Accel will lie between 1.0 second and 5.0 seconds
val MAXACCEL = (MAXTOPSPEED ) / (20*3) //3.0 second to max speed
val MINACCEL = (MAXTOPSPEED ) / (20*8) // 8 seconds to max speed
//Can rotate 90 degrees
val MAXHANDLING = 90.0
val MINHANDLING = 30.0
val MAXYAWHANDLING = 16.0
val MINYAWHANDLING = 8.0
}
}
@@ -1,202 +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.pokemon.riding.controllers
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
class DolphinController : RideController {
companion object {
val KEY: ResourceLocation = cobblemonResource("swim/dolphin")
}
var canJump = "true".asExpression()
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var speed = "1.0".asExpression()
private set
var driveFactor = "1.0".asExpression()
private set
var reverseDriveFactor = "0.25".asExpression()
private set
var strafeFactor = "0.2".asExpression()
private set
var lastVelocity = Vec3(0.0,0.0,0.0)
private set
private val rollSmoother = SmoothDouble()
override val key: ResourceLocation = KEY
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { it.deltaMovement.length() >= 0.05 })
override val condition: (PokemonEntity) -> Boolean = { entity ->
//If there are only fluid blocks or air block below the ride
//or if the entity is in water then activate the controller
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
if (it.y.toDouble() == (entity.position().y)) {
val blockState = entity.level().getBlockState(it.below())
return@any (blockState.isAir || !blockState.fluidState.isEmpty ) ||
(entity.isInWater || entity.isUnderWater)
}
true
}
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getRuntime(entity).resolveFloat(speed)
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot )
}
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
return 0.0
}
override fun inertia(entity: PokemonEntity ): Double
{
return 0.05
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val runtime = getRuntime(entity)
if (!entity.isInWater && !entity.isUnderWater)
{
lastVelocity = Vec3(lastVelocity.x, lastVelocity.y - 0.035, lastVelocity.z)
return lastVelocity
}
val driveFactor = runtime.resolveFloat(this.driveFactor)
val strafeFactor = runtime.resolveFloat(this.strafeFactor)
val f = driver.xxa * strafeFactor
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(this.reverseDriveFactor)
}
var yComp = 0.0
//Get roll to add a left and right strafe during roll
//Not sure if this is desired? Will need to mess around with this
//a bit more
var zComp = 0.0
val rollable = driver as? Rollable
if (rollable != null)
{
//Can be used again if I better figure out how to deadzone it near the tops?
zComp = -1.0 * g.toDouble() * sin(Math.toRadians( rollable.roll.toDouble() ) )
yComp = -1.0 * g.toDouble() * sin(Math.toRadians( rollable.pitch.toDouble() ) )
}
val currVelocity = Vec3(0.0, yComp , g.toDouble())
lastVelocity = currVelocity
return currVelocity
}
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
if (!entity.isInWater && !entity.isUnderWater)
{
return Vec3(0.0, 0.0, 0.0)
}
val rollable = driver as? Rollable
//this should be changed to be speed maybe?
val movingForce = driver.zza
if( rollable != null ) {
var yawAngVel = 3 * sin(Math.toRadians(rollable.roll.toDouble())).toFloat()
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(rollable.roll.toDouble())).toFloat())
//limit rotation modulation when pitched up heavily or pitched down heavily
yawAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
pitchAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
//if you are not pressing forward then don't turn
//yawAngVel *= movingForce
//Ignore x,y,z its angular velocity:
//yaw, pitch, roll
return Vec3(yawAngVel.toDouble(), pitchAngVel.toDouble(), 0.0)
}
return Vec3(0.0, 0.0, 0.0)
}
override fun canJump(entity: PokemonEntity, driver: Player) = getRuntime(entity).resolveBoolean(canJump)
override fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
val runtime = getRuntime(entity)
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
}
override fun shouldRoll(entity: PokemonEntity) = true
override fun turnOffOnGround(entity: PokemonEntity): Boolean = true
override fun dismountOnShift(entity: PokemonEntity): Boolean = false
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(this.speed.getString())
buffer.writeString(this.canJump.getString())
buffer.writeString(this.jumpVector[0].getString())
buffer.writeString(this.jumpVector[1].getString())
buffer.writeString(this.jumpVector[2].getString())
buffer.writeString(this.driveFactor.getString())
buffer.writeString(this.reverseDriveFactor.getString())
buffer.writeString(this.strafeFactor.getString())
buffer.writeDouble(this.lastVelocity.x)
buffer.writeDouble(this.lastVelocity.y)
buffer.writeDouble(this.lastVelocity.z)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
this.speed = buffer.readString().asExpression()
this.canJump = buffer.readString().asExpression()
this.jumpVector = listOf(
buffer.readString().asExpression(),
buffer.readString().asExpression(),
buffer.readString().asExpression()
)
this.driveFactor = buffer.readString().asExpression()
this.reverseDriveFactor = buffer.readString().asExpression()
this.strafeFactor = buffer.readString().asExpression()
this.strafeFactor = buffer.readString().asExpression()
val x = buffer.readDouble()
val y = buffer.readDouble()
val z = buffer.readDouble()
this.lastVelocity = Vec3(x, y, z)
}
}
@@ -1,152 +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.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.states.CompositeState
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeString
import net.minecraft.network.RegistryFriendlyByteBuf
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
class FallToFlightCompositeController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
var minimumForwardSpeed: Expression = "0.5".asExpression()
private set
var minimumFallSpeed: Expression = "0.5".asExpression()
private set
var landController: RideController = GenericLandController()
private set
var flightController: RideController = GliderAirController()
private set
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
fun getActiveController(entity: PokemonEntity): RideController {
val state = getState(entity, ::CompositeState)
return state.activeController ?: let {
val controller = if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
flightController
} else {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
landController
}
state.activeController = controller
state.timeTransitioned = entity.level().gameTime
controller
}
}
fun checkShouldBeFlying(entity: PokemonEntity, isFlyingAlready: Boolean): Boolean {
val runtime = getRuntime(entity)
val minFallingSpeed = runtime.resolveFloat(minimumFallSpeed)
val minForwardSpeed = runtime.resolveFloat(minimumForwardSpeed)
val grounded = entity.onGround()
return if (isFlyingAlready) {
!grounded
} else {
entity.deltaMovement.y <= -minFallingSpeed && entity.deltaMovement.horizontalDistance() >= minForwardSpeed
}
}
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::CompositeState)
val shouldBeFlying = checkShouldBeFlying(entity, state.activeController == flightController)
if (state.activeController == flightController && !shouldBeFlying) { // && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
} else if (state.activeController == landController && shouldBeFlying) {
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
}
return getActiveController(entity).speed(entity, driver)
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return getActiveController(entity).rotation(entity, driver)
}
override fun velocity(
entity: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
return getActiveController(entity).velocity(entity, driver, input)
}
override fun canJump(entity: PokemonEntity, driver: Player) = true
override fun jumpForce(
entity: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
val controller = getActiveController(entity)
return controller.jumpForce(entity, driver, jumpStrength)
}
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
return getActiveController(entity).gravity(entity, regularGravity)
}
override fun shouldRoll(entity: PokemonEntity): Boolean = getActiveController(entity).shouldRoll(entity)
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(minimumForwardSpeed.getString())
buffer.writeString(minimumFallSpeed.getString())
landController.encode(buffer)
flightController.encode(buffer)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
minimumForwardSpeed = buffer.readString().asExpression()
minimumFallSpeed = buffer.readString().asExpression()
landController = buffer.readResourceLocation().let { key ->
val controller = RideControllerAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
controller.decode(buffer)
controller
}
flightController = buffer.readResourceLocation().let { key ->
val controller = RideControllerAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
controller.decode(buffer)
controller
}
}
companion object {
val KEY = cobblemonResource("composite/fall_to_flight")
}
}
@@ -1,140 +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.pokemon.riding.controllers
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeString
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.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
class GenericLandController : RideController {
companion object {
val KEY: ResourceLocation = cobblemonResource("land/generic")
}
// private var previousVelocity = Vec3d.ZERO
var canJump = "true".asExpression()
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var speed = "1.0".asExpression()
private set
var driveFactor = "1.0".asExpression()
private set
var reverseDriveFactor = "0.25".asExpression()
private set
var strafeFactor = "0.2".asExpression()
private set
@Transient
override val key: ResourceLocation = KEY
@Transient
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.deltaMovement.horizontalDistance() > 0.1 })
@Transient
override val condition: (PokemonEntity) -> Boolean = { entity ->
// Are there any blocks under the mon that aren't air or fluid
// Cant just check one block since some mons may be more than one block big
// This should be changed so that the any predicate is only ran on blocks under the mon
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
//Need to check other fluids
if (entity.isInWater || entity.isUnderWater) {
return@any false
}
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
if (it.y.toDouble() == (entity.position().y)) {
val blockState = entity.level().getBlockState(it.below())
return@any !blockState.isAir && blockState.fluidState.isEmpty
}
true
}
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return 0.0f
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val currSpeed = 1.0
val runtime = getRuntime(entity)
val driveFactor = runtime.resolveFloat(this.driveFactor)
val strafeFactor = runtime.resolveFloat(this.strafeFactor)
val f = driver.xxa * strafeFactor
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(this.reverseDriveFactor)
}
val gravity = -0.8
val velocity = Vec3(f.toDouble() * currSpeed, gravity, g.toDouble()*0.7 * currSpeed)
return velocity
}
override fun canJump(entity: PokemonEntity, driver: Player) = getRuntime(entity).resolveBoolean(canJump)
override fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
val runtime = getRuntime(entity)
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
}
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double {
return 0.0
}
override fun inertia(entity: PokemonEntity ): Double = 0.3
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(this.speed.getString())
buffer.writeString(this.canJump.getString())
buffer.writeString(this.jumpVector[0].getString())
buffer.writeString(this.jumpVector[1].getString())
buffer.writeString(this.jumpVector[2].getString())
buffer.writeString(this.driveFactor.getString())
buffer.writeString(this.reverseDriveFactor.getString())
buffer.writeString(this.strafeFactor.getString())
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
this.speed = buffer.readString().asExpression()
this.canJump = buffer.readString().asExpression()
this.jumpVector = listOf(
buffer.readString().asExpression(),
buffer.readString().asExpression(),
buffer.readString().asExpression()
)
this.driveFactor = buffer.readString().asExpression()
this.reverseDriveFactor = buffer.readString().asExpression()
this.strafeFactor = buffer.readString().asExpression()
}
}
@@ -1,123 +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.pokemon.riding.controllers
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeString
import kotlin.math.max
import kotlin.math.min
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.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
class GenericLiquidController : RideController {
companion object {
val KEY: ResourceLocation = cobblemonResource("swim/generic")
}
var canJump = "true".asExpression()
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var speed = "1.0".asExpression()
private set
var driveFactor = "1.0".asExpression()
private set
var reverseDriveFactor = "0.25".asExpression()
private set
var strafeFactor = "0.2".asExpression()
private set
override val key: ResourceLocation = KEY
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { it.isSwimming && it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { entity ->
//This could be kinda weird... what if the top of the mon is in a fluid but the bottom isnt?
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
if (entity.isInWater || entity.isUnderWater) {
return@any true
}
val blockState = entity.level().getBlockState(it)
return@any !blockState.fluidState.isEmpty
}
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getRuntime(entity).resolveFloat(speed)
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val runtime = getRuntime(entity)
val driveFactor = runtime.resolveFloat(this.driveFactor)
val strafeFactor = runtime.resolveFloat(this.strafeFactor)
val f = driver.xxa * strafeFactor
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(this.reverseDriveFactor)
}
val velocity = Vec3(f.toDouble(), 0.0, g.toDouble())
return velocity
}
override fun canJump(entity: PokemonEntity, driver: Player) = getRuntime(entity).resolveBoolean(canJump)
override fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
val runtime = getRuntime(entity)
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(this.speed.getString())
buffer.writeString(this.canJump.getString())
buffer.writeString(this.jumpVector[0].getString())
buffer.writeString(this.jumpVector[1].getString())
buffer.writeString(this.jumpVector[2].getString())
buffer.writeString(this.driveFactor.getString())
buffer.writeString(this.reverseDriveFactor.getString())
buffer.writeString(this.strafeFactor.getString())
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
this.speed = buffer.readString().asExpression()
this.canJump = buffer.readString().asExpression()
this.jumpVector = listOf(
buffer.readString().asExpression(),
buffer.readString().asExpression(),
buffer.readString().asExpression()
)
this.driveFactor = buffer.readString().asExpression()
this.reverseDriveFactor = buffer.readString().asExpression()
this.strafeFactor = buffer.readString().asExpression()
}
}
@@ -1,87 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.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.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeString
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.Vec2
import net.minecraft.world.phys.Vec3
class GliderAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.horizontalDistance() > 0.1 })
override val condition: (PokemonEntity) -> Boolean = { true }
var glideSpeed: Expression = "0.1".asExpression()
private set
var speed: Expression = "1.0".asExpression()
private set
var canStrafe: Expression = "false".asExpression()
private set
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getRuntime(entity).resolveFloat(speed)
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot, driver.yRot)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val xVector = if (getRuntime(entity).resolveBoolean(canStrafe)) driver.xxa.toDouble() else 0.0
val yVector = -getRuntime(entity).resolveDouble(glideSpeed)
val zVector = driver.zza.toDouble()
val statSpeed = entity.rideProp.calculate(RidingStat.SPEED, RidingStyle.AIR, 0 )
return Vec3(xVector, yVector, zVector)
}
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
return 0.0
}
override fun canJump(entity: PokemonEntity, driver: Player): Boolean {
return false
}
override fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int) = Vec3.ZERO
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(glideSpeed.toString())
buffer.writeString(speed.toString())
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
glideSpeed = buffer.readString().asExpression()
speed = buffer.readString().asExpression()
}
companion object {
val KEY: ResourceLocation = cobblemonResource("air/glider")
}
}
@@ -1,224 +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.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.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
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import java.lang.Math.pow
import kotlin.math.*
/**
* Allows for flight like a bird controller, except you don't have to do any "flapping" motion,
* you can just go up and down like creative
*
* @author Apion/Jackowes
* @since January 1, 2025
*/
class HelicopterAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.horizontalDistance() > 0.1 })
//If there are only fluid blocks or air block below the ride
//then activate the controller. If it is in water the ride will
//dismount accordingly
override val condition: (PokemonEntity) -> Boolean = { entity ->
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
if (it.y.toDouble() == (entity.position().y)) {
val blockState = entity.level().getBlockState(it.below())
return@any (blockState.isAir || !blockState.fluidState.isEmpty )
}
true
}
}
var gravity: Expression = "1.0".asExpression()
private set
var horizontalAcceleration: Expression = "0.1".asExpression()
private set
var verticalVelocity: Expression = "1.0".asExpression()
private set
var speed: Expression = "1.0".asExpression()
private set
override fun speed(entity: PokemonEntity, driver: Player): Float {
//Increased max speed to exaggerate movement.
//This likely just needs to be a static number if it really is just
//the scalar for the velocity vector
return 2.0f
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val runtime = getRuntime(entity)
//If the player is not modulating height then hover
var yVel = if (driver.jumping) runtime.resolveDouble(verticalVelocity)
else if (driver.isShiftKeyDown) -runtime.resolveDouble(verticalVelocity)
else (0.0)
yVel *= 0.25
var xVel = 0.0
var zVel = 0.0
val rollable = driver as? Rollable
//Horizontal velocity is based on pitch and roll
if( rollable != null ) {
xVel = -1.0 * sin(Math.toRadians(rollable.roll.toDouble()))
zVel = sin(Math.toRadians(rollable.pitch.toDouble()))
}
return Vec3(xVel, yVel, zVel)
}
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val rollable = driver as? Rollable
//In degrees per second? It was supposed to be I think but
//I have messed something up
val rotationChangeRate = 2.0
val rotLimit = 30.0
var pitchForce = driver.zza * rotationChangeRate
var rollForce = -1.0 * driver.xxa * rotationChangeRate
//TODO: Fix accumulated movement causing pitch and roll out of set bounds
if( rollable != null ) {
//If the roll or pitch have exceeded the limit then do not modulate that
//rotation in that direction further
//modulate the forces so that they trend towards 0 as they approach rotation limit.
//The current function has it drop off very quickly which may not be ideal.
//Prev function:
// pitchForce = pitchForce * max(rotLim - abs( rollable.pitch.toDouble() ), 0.0)
//The three denotes that the force will be 1/3 what it would have been at
//the rotation limit
rollForce *= 2.0.pow(-1.0 * (abs(rollable.roll.toDouble()) / ROTATION_LIMIT))
pitchForce *= 2.0.pow(-1.0 * (abs(rollable.pitch.toDouble()) / ROTATION_LIMIT))
if( rollable.roll >= ROTATION_LIMIT && rollForce > 0.0)
{
rollForce = 0.0;
}
if( rollable.roll <= -ROTATION_LIMIT && rollForce < 0.0)
{
rollForce = 0.0;
}
if( rollable.pitch >= ROTATION_LIMIT && pitchForce > 0.0 )
{
pitchForce = 0.0;
}
if( rollable.pitch <= -ROTATION_LIMIT && pitchForce < 0.0 )
{
pitchForce = 0.0;
}
//Ignore x,y,z its angular velocity:
//yaw, pitch, roll
return Vec3( 0.0, pitchForce.toDouble(), rollForce)
}
return Vec3(0.0, 0.0, 0.0)
}
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = true
override fun inertia(entity: PokemonEntity ): Double = 0.1
override fun canJump(
entity: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun jumpForce(
entity: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun shouldRoll(entity: PokemonEntity): Boolean = true
override fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
if(driver !is Rollable) return Vec3.ZERO
val rollable = driver as Rollable
//Ignore x,y,z its angular velocity:
//yaw, pitch, roll
return Vec3(xMouse, 0.0, 0.0 )
}
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double {
return 0.0
}
//Need to ensure that crouching lowers altitude and doesn't eject the player
override fun dismountOnShift(entity: PokemonEntity): Boolean = false
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(gravity.toString())
buffer.writeString(horizontalAcceleration.toString())
buffer.writeString(verticalVelocity.toString())
buffer.writeString(speed.toString())
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readString().asExpression()
horizontalAcceleration = buffer.readString().asExpression()
verticalVelocity = buffer.readString().asExpression()
speed = buffer.readString().asExpression()
}
companion object {
val KEY = cobblemonResource("air/helicopter")
val ROTATION_LIMIT = 30.0f
}
}
@@ -1,300 +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.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.controllers.HelicopterAirController.Companion.ROTATION_LIMIT
import com.cobblemon.mod.common.util.*
import net.minecraft.client.Minecraft
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
import net.minecraft.world.phys.Vec3
import kotlin.math.*
class JetAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.length() > 0 })
override val condition: (PokemonEntity) -> Boolean = { true }
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var gravity: Expression = "0".asExpression()
private set
var horizontalAcceleration: Expression = "0.1".asExpression()
private set
var verticalAcceleration: Expression = "0.1".asExpression()
private set
var speed: Expression = "1.0".asExpression()
private set
var lastVelocity = Vec3(0.0,0.0,0.0)
private set
var maxSpeed = 0.4
private set
var lastPitch = 0.0f
private set
var currSpeed = MINSPEED
private set
var currMouseXForce = 0.0
private set
var currMouseYForce = 0.0
private set
override fun speed(entity: PokemonEntity, driver: Player): Float {
//0 boost so just the base value.
//This should be a function in pokemonEntity.kt so it can easily be called.
val speedStat = entity.rideProp.calculate(RidingStat.SPEED, RidingStyle.AIR, 0 )
//now calculate the true top speed value using the speed stat
val topSpeed = (((MAXTOPSPEED - MINTOPSPEED) / 100) * speedStat) + MINTOPSPEED
val accelStat = entity.rideProp.calculate(RidingStat.ACCELERATION, RidingStyle.AIR, 0 )
val accel = (((MAXACCEL - MINACCEL) / 100) * accelStat) + MINACCEL
val drivingForce = if ( Minecraft.getInstance().options.keySprint.isDown() ) 1.0
else if (driver.isShiftKeyDown) -1.0
else(0.0)
//speed up and slow down based on input
if( driver.zza > 0.0 && currSpeed < topSpeed)
{
currSpeed = min( currSpeed + accel , topSpeed)
}
else if ( driver.zza < 0.0 && currSpeed > MINSPEED)
{
//Decelerate is now always a constant half of max acceleration.
currSpeed = max( currSpeed - (MAXACCEL / 2) , MINSPEED)
}
return currSpeed.toFloat()
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
var yVel = 0.0
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
val rollable = driver as? Rollable
if( rollable != null ) {
yVel = sin(Math.toRadians(rollable.pitch.toDouble() - lastPitch ))*-10
lastPitch = rollable.pitch
leftForce = -1.0 * sin(Math.toRadians( rollable.roll.toDouble() ) )
upForce = -1.0 * sin(Math.toRadians( rollable.pitch.toDouble() ))
//limit how fast you can descend?
upForce *= 1.0
forwardForce = cos(Math.toRadians( rollable.pitch.toDouble() ))
}
val velocity = Vec3(0.0 , (upForce)* currSpeed, forwardForce * currSpeed)
//entity.setDeltaMovement(velocity)
return velocity
}
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = true
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
//Get handling in degrees per second
val handlingStat = entity.rideProp.calculate(RidingStat.SKILL, RidingStyle.AIR, 0 )
//var handling = (((MAXHANDLING - MINHANDLING) / 100) * handlingStat) + MINHANDLING
var handlingYaw = (((MAXYAWHANDLING - MINYAWHANDLING) / 100) * handlingStat) + MINYAWHANDLING
//convert it to partial ticks
handlingYaw *= (deltaTime)
//A+D to yaw
val yawForce = -2.0 * driver.xxa * handlingYaw
return Vec3( yawForce, 0.0, 0.0)
}
override fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
if(driver !is Rollable) return Vec3.ZERO
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
val cappedDeltaTime = min( deltaTime, 0.2)
// Accumulate the mouse input
currMouseXForce = (currMouseXForce + ( 0.0015 * xMouse )).coerceIn(-1.0, 1.0)
currMouseYForce = (currMouseYForce + ( 0.0015 * yMouse )).coerceIn(-1.0, 1.0)
//Get handling in degrees per second
val handlingStat = entity.rideProp.calculate(RidingStat.SKILL, RidingStyle.AIR, 0 )
var handling = (((MAXHANDLING - MINHANDLING) / 100) * handlingStat) + MINHANDLING
//convert it to partial ticks
handling *= (deltaTime)
val poke = driver.vehicle as? PokemonEntity
//val pitchRot = min( handling, max(-handling, yMouse ) )
//increase on an amount.
//if its below 0.2 it should be zero force.
// And then between 0.2-1.0 it should map to a value between 0.0 and 1.0
//val yInput = if(currMouseYForce < 0.2) 0.0 else ( ( currMouseYForce - 0.2 ) / 0.8)
//val xInput = if(currMouseXForce < 0.2) 0.0 else ( ( currMouseXForce - 0.2 ) / 0.8)
//create deadzones for the constant input values.
val xInput = remapWithDeadzone(currMouseXForce, 0.025, 1.0)
val yInput = remapWithDeadzone(currMouseYForce, 0.025, 1.0)
val pitchRot = handling * currMouseYForce
//double roll
val rollRot = handling * 1.5 * currMouseXForce
//mult by 2 so you can roll twice as fast as you can pitch.
//val rollRot = min( handling*2, max(-handling*2, xMouse ) )
//Ignore x,y,z its angular velocity:
//yaw, pitch, roll
return Vec3(0.0, pitchRot, rollRot)
}
override fun fuelBar(entity: PokemonEntity, driver: Player): Float {
return 0.5f
}
override fun canJump(
entity: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun jumpForce(
entity: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
//Going to use my own gravity calculations for now.
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double {
var grav = 0.0
if( maxSpeed <= 0.5 ) grav = 0.5 - maxSpeed
return grav
}
override fun shouldRoll(entity: PokemonEntity) = true
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(gravity.toString())
buffer.writeString(horizontalAcceleration.toString())
buffer.writeString(verticalAcceleration.toString())
buffer.writeString(speed.toString())
buffer.writeDouble(maxSpeed)
buffer.writeDouble(this.lastVelocity.x)
buffer.writeDouble(this.lastVelocity.y)
buffer.writeDouble(this.lastVelocity.z)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readString().asExpression()
horizontalAcceleration = buffer.readString().asExpression()
verticalAcceleration = buffer.readString().asExpression()
speed = buffer.readString().asExpression()
this.maxSpeed = buffer.readDouble()
val x = buffer.readDouble()
val y = buffer.readDouble()
val z = buffer.readDouble()
this.lastVelocity = Vec3(x, y, z)
}
companion object {
val KEY = cobblemonResource("air/jet")
//I need a top and a bottom for the speed stat
// I need a minimum speed stat for this ride specifically so that it caps
// at this or slows below it and falls
// 32 bl/s and 8 bl/s
// min is 4 bl/s
val MAXTOPSPEED = 2.5 // 50 bl/s
val MINTOPSPEED = 0.35 // 7 bl/s
val MINSPEED = 0.25 // 5 bl/s
//Accel will lie between 1.0 second and 5.0 seconds
val MAXACCEL = (MAXTOPSPEED - MINSPEED) / 20 //1.0 second to max speed
val MINACCEL = (MAXTOPSPEED - MINSPEED) / (20*5) // 5 seconds to max speed
//full 180 in 1 second.
//30 degrees in 1 second.
val MAXHANDLING = 140.0
val MINHANDLING = 20.0
val MAXYAWHANDLING = 16.0
val MINYAWHANDLING = 8.0
//Fuel measured by seconds at full throttle.
//minimum speed will consume a quarter of the fuel.
//val MAXSTAMINA =
}
//helper functions
//Creates a deadzone around zero that extends out to deadzoneLow in both +- and then remaps
//the value linearly between -1.0 and 1.0 outside of the deadzone
fun remapWithDeadzone(value: Double, deadzoneLow: Double = 0.2, deadzoneHigh: Double = 1.0): Double {
// If the absolute value is below the deadzone, return 0.
if (kotlin.math.abs(value) < deadzoneLow) return 0.0
// Otherwise, subtract the deadzone and remap the remaining range to 0-1 (preserving the sign).
val sign = kotlin.math.sign(value)
val adjusted = (kotlin.math.abs(value) - deadzoneLow) / (deadzoneHigh - deadzoneLow)
//Attach the sign back to the vale
return adjusted * sign
}
}
@@ -1,178 +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.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.states.CompositeState
import com.cobblemon.mod.common.util.adapters.riding.RideControllerAdapter
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.writeString
import net.minecraft.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
import net.minecraft.world.phys.Vec3
class RunUpToFlightCompositeController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
var minimumSpeed: Expression = "0.5".asExpression()
private set
var minimumJump: Expression = "0.5".asExpression()
private set
var landController: RideController = GenericLandController()
private set
var flightController: RideController = BirdAirController()
private set
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
fun getActiveController(entity: PokemonEntity): RideController {
val state = getState(entity, ::CompositeState)
return state.activeController ?: let {
val controller = if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
flightController
} else {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
landController
}
state.activeController = controller
state.timeTransitioned = entity.level().gameTime
controller
}
}
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::CompositeState)
if (state.activeController == flightController && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
getState(entity, ::CompositeState).activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
}
return getActiveController(entity).speed(entity, driver)
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return getActiveController(entity).rotation(entity, driver)
}
override fun velocity(
entity: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
return getActiveController(entity).velocity(entity, driver, input)
}
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
return getActiveController(entity).angRollVel(entity, driver, deltaTime)
}
override fun canJump(entity: PokemonEntity, driver: Player): Boolean {
return getActiveController(entity).canJump(entity, driver)
}
override fun jumpForce(
entity: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
val controller = getActiveController(entity)
if (controller == landController && jumpStrength >= getRuntime(entity).resolveInt(minimumJump)) {
getState(entity, ::CompositeState).let {
it.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
return controller.jumpForce(entity, driver, jumpStrength)
}
override fun gravity(entity: PokemonEntity, regularGravity: Double): Double? {
return getActiveController(entity).gravity(entity, regularGravity)
}
override fun inertia(entity: PokemonEntity): Double {
return getActiveController(entity).inertia(entity)
}
override fun shouldRoll(entity: PokemonEntity): Boolean = getActiveController(entity).shouldRoll(entity)
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = getActiveController(entity).useAngVelSmoothing(entity)
override fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
yMouse: Double,
xMouse: Double,
yMouseSmoother: SmoothDouble,
xMouseSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double ): Vec3
{
return getActiveController(entity).rotationOnMouseXY(entity,
driver,
yMouse,
xMouse,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime )
}
override fun dismountOnShift(entity: PokemonEntity): Boolean = getActiveController(entity).dismountOnShift(entity)
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(minimumSpeed.getString())
buffer.writeString(minimumJump.getString())
landController.encode(buffer)
flightController.encode(buffer)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
minimumSpeed = buffer.readString().asExpression()
minimumJump = buffer.readString().asExpression()
landController = buffer.readResourceLocation().let { key ->
val controller = RideControllerAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
controller.decode(buffer)
controller
}
flightController = buffer.readResourceLocation().let { key ->
val controller = RideControllerAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
controller.decode(buffer)
controller
}
}
companion object {
val KEY = cobblemonResource("composite/run_up_to_flight")
}
}
@@ -1,94 +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.pokemon.riding.controllers
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
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.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
class SwimDashController : RideController {
companion object {
val KEY: ResourceLocation = cobblemonResource("swim/dash")
const val DASH_TICKS: Int = 60
}
var dashSpeed = 1F
private set
override val key: ResourceLocation = KEY
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT).with(PoseOption(PoseType.SWIM) { it.isSwimming && it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { entity ->
//This could be kinda weird... what if the top of the mon is in a fluid but the bottom isnt?
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
if (entity.isInWater || entity.isUnderWater) {
return@any true
}
val blockState = entity.level().getBlockState(it)
return@any !blockState.fluidState.isEmpty
}
}
/** Indicates that we are currently enacting a dash, and that further movement inputs should be ignored */
private var dashing = false
private var ticks = 0
override fun speed(entity: PokemonEntity, driver: Player): Float {
if(this.dashing) {
if(this.ticks++ >= DASH_TICKS) {
this.dashing = false
}
return 0.0F
}
this.dashing = true
return this.dashSpeed
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val f = driver.xxa * 0.05f
var g = driver.zza * 0.6f
if (g <= 0.0f) {
g *= 0.25f
}
return Vec3(f.toDouble(), 0.0, g.toDouble())
}
override fun canJump(entity: PokemonEntity, driver: Player): Boolean {
TODO("Not yet implemented")
}
override fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
TODO("Not yet implemented")
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeFloat(this.dashSpeed)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
this.dashSpeed = buffer.readFloat()
}
}
@@ -1,204 +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.pokemon.riding.controllers
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.controllers.BirdAirController.Companion
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeString
import kotlin.math.absoluteValue
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.max
import kotlin.math.min
class VehicleLandController : RideController {
companion object {
val KEY: ResourceLocation = cobblemonResource("land/vehicle")
val MAXTOPSPEED = 1.0 // 20 bl/s
val MINTOPSPEED = 0.35 // 7 bl/s
//val MINSPEED = 0.25 // 5 bl/s
//Accel will lie between 1.0 second and 5.0 seconds
val MAXACCEL = (MAXTOPSPEED ) / (20*3) //3.0 second to max speed
val MINACCEL = (MAXTOPSPEED ) / (20*8) // 8 seconds to max speed
//Can rotate 90 degrees
val MAXHANDLING = 90.0
val MINHANDLING = 30.0
val MAXYAWHANDLING = 16.0
val MINYAWHANDLING = 8.0
}
var canJump = "true".asExpression()
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var speed = "0.3".asExpression()
private set
var driveFactor = "1.0".asExpression()
private set
var reverseDriveFactor = "0.25".asExpression()
private set
var minimumSpeedToTurn = "0.1".asExpression()
private set
var rotationSpeed = "45/20".asExpression()
private set
var lookYawLimit = "101".asExpression()
private set
var currSpeed = 0.0
private set
@Transient
override val key: ResourceLocation = KEY
@Transient
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.deltaMovement.horizontalDistance() > 0.1 })
@Transient
override val condition: (PokemonEntity) -> Boolean = { entity ->
// Are there any blocks under the mon that aren't air or fluid
// Cant just check one block since some mons may be more than one block big
// This should be changed so that the any predicate is only ran on blocks under the mon
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
//Need to check other fluids
if (entity.isInWater || entity.isUnderWater) {
return@any false
}
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
if (it.y.toDouble() == (entity.position().y)) {
val blockState = entity.level().getBlockState(it.below())
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
}
true
}
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
val topSpeed = entity.getRideStat( RidingStat.SPEED, RidingStyle.AIR,
VehicleLandController.MINTOPSPEED,
VehicleLandController.MAXTOPSPEED
)
val accel = entity.getRideStat( RidingStat.ACCELERATION, RidingStyle.AIR,
VehicleLandController.MINACCEL,
VehicleLandController.MAXACCEL
)
//speed up and slow down based on input
if( driver.zza > 0.0 && currSpeed < topSpeed)
{
currSpeed = min( currSpeed + accel , topSpeed)
}
else if ( driver.zza < 0.0 && currSpeed > 0.0)
{
//Decelerate is now always a constant half of max acceleration.
currSpeed = max( currSpeed - (BirdAirController.MAXACCEL / 2) , 0.0)
}
return currSpeed.toFloat()
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
val rotationDegrees = driver.xxa * getRuntime(entity).resolveFloat(this.rotationSpeed)
return Vec2(driver.xRot, entity.yRot - rotationDegrees)
}
override fun velocity(entity: PokemonEntity, driver: Player, input: Vec3): Vec3 {
val runtime = getRuntime(entity)
val driveFactor = runtime.resolveFloat(this.driveFactor)
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(this.reverseDriveFactor)
}
if (driver.xxa != 0F && g.absoluteValue < runtime.resolveFloat(this.minimumSpeedToTurn)) {
driver.xxa = 0F
}
val velocity = Vec3(0.0 /* Maybe do drifting here later */, 0.0, g.toDouble() * currSpeed)
return velocity
}
override fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
driver.yRot += (entity.riding.deltaRotation.y)
driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y))
}
override fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
val f = Mth.wrapDegrees(driver.yRot - entity.yRot)
val runtime = getRuntime(entity)
val lookYawLimit = runtime.resolveFloat(this.lookYawLimit)
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
driver.yRotO += g - f
driver.yRot = driver.yRot + g - f
driver.setYHeadRot(driver.yRot)
}
override fun canJump(entity: PokemonEntity, driver: Player) = getRuntime(entity).resolveBoolean(canJump)
override fun jumpForce(entity: PokemonEntity, driver: Player, jumpStrength: Int): Vec3 {
val runtime = getRuntime(entity)
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(this.speed.getString())
buffer.writeString(this.canJump.getString())
buffer.writeString(this.jumpVector[0].getString())
buffer.writeString(this.jumpVector[1].getString())
buffer.writeString(this.jumpVector[2].getString())
buffer.writeString(this.driveFactor.getString())
buffer.writeString(this.reverseDriveFactor.getString())
buffer.writeString(this.minimumSpeedToTurn.getString())
buffer.writeString(this.rotationSpeed.getString())
buffer.writeString(this.lookYawLimit.getString())
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
this.speed = buffer.readString().asExpression()
this.canJump = buffer.readString().asExpression()
this.jumpVector = listOf(
buffer.readString().asExpression(),
buffer.readString().asExpression(),
buffer.readString().asExpression()
)
this.driveFactor = buffer.readString().asExpression()
this.reverseDriveFactor = buffer.readString().asExpression()
this.minimumSpeedToTurn = buffer.readString().asExpression()
this.rotationSpeed = buffer.readString().asExpression()
this.lookYawLimit = buffer.readString().asExpression()
}
}
@@ -1,18 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.pokemon.riding.states
import com.cobblemon.mod.common.api.riding.RidingState
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
open class CompositeState(pokemonEntity: PokemonEntity) : RidingState {
var activeController: RideController? = null
var timeTransitioned = -100L
}
@@ -30,8 +30,6 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.EntityDimensions
import net.minecraft.world.item.ItemStack
import org.joml.Matrix3f
import org.joml.Matrix4f
import java.io.IOException
import java.util.*
@@ -236,54 +234,4 @@ fun ByteBuf.readBitSet(size: Int): BitSet {
return BitSet.valueOf(bs)
}
fun ByteBuf.writeMatrix3f(matrix: Matrix3f) {
this.writeFloat(matrix.m00)
this.writeFloat(matrix.m01)
this.writeFloat(matrix.m02)
this.writeFloat(matrix.m10)
this.writeFloat(matrix.m11)
this.writeFloat(matrix.m12)
this.writeFloat(matrix.m20)
this.writeFloat(matrix.m21)
this.writeFloat(matrix.m22)
}
fun ByteBuf.readMatrix3f(): Matrix3f {
return Matrix3f(
readFloat(), readFloat(), readFloat(),
readFloat(), readFloat(), readFloat(),
readFloat(), readFloat(), readFloat(),
)
}
fun ByteBuf.writeMatrix4f(matrix: Matrix4f) {
this.writeFloat(matrix.m00())
this.writeFloat(matrix.m01())
this.writeFloat(matrix.m02())
this.writeFloat(matrix.m03())
this.writeFloat(matrix.m10())
this.writeFloat(matrix.m11())
this.writeFloat(matrix.m12())
this.writeFloat(matrix.m13())
this.writeFloat(matrix.m20())
this.writeFloat(matrix.m21())
this.writeFloat(matrix.m22())
this.writeFloat(matrix.m23())
this.writeFloat(matrix.m30())
this.writeFloat(matrix.m31())
this.writeFloat(matrix.m32())
this.writeFloat(matrix.m33())
}
fun ByteBuf.readMatrix4f(): Matrix4f {
return Matrix4f(
readFloat(), readFloat(), readFloat(), readFloat(),
readFloat(), readFloat(), readFloat(), readFloat(),
readFloat(), readFloat(), readFloat(), readFloat(),
readFloat(), readFloat(), readFloat(), readFloat(),
)
}
//fun
@@ -18,8 +18,6 @@ import net.minecraft.client.gui.screens.Screen
import java.util.UUID
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.min
import kotlin.random.Random
import net.minecraft.client.resources.model.ModelResourceLocation
@@ -87,21 +85,6 @@ fun isUuid(string: String) : Boolean {
return Regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\$").matches(string)
}
fun VoxelShape.blockPositionsAsListRounded(): List<BlockPos> {
val result = mutableListOf<BlockPos>()
forAllBoxes { minX, minY, minZ, maxX, maxY, maxZ ->
for (x in floor(minX).toInt() until ceil(maxX).toInt()) {
for (y in floor(minY).toInt() until ceil(maxY).toInt()) {
for (z in floor(minZ).toInt() until ceil(maxZ).toInt()) {
result.add(BlockPos(x, y, z))
}
}
}
}
return result
}
fun VoxelShape.blockPositionsAsList(): List<BlockPos> {
val result = mutableListOf<BlockPos>()
forAllBoxes { minX, minY, minZ, maxX, maxY, maxZ ->
@@ -37,8 +37,6 @@ object IntRangeAdapter : JsonSerializer<IntRange>, JsonDeserializer<IntRange> {
}
override fun deserialize(json: JsonElement, type: Type, context: JsonDeserializationContext): IntRange {
val value = json.asString
println("Deserializing IntRange from: $value")
val (start, end) = PATTERN.find(json.asString)!!.destructured
return if (end.isEmpty()) {
IntRange(start.toInt(), start.toInt())
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.util.adapters.riding
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.pokemon.riding.controllers.*
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import net.minecraft.resources.ResourceLocation
import java.lang.reflect.Type
/**
* Adapter for deserializing [RideController] types.
*
* @author Hiroku
* @since April 30th, 2024
*/
object RideControllerAdapter : JsonDeserializer<RideController> {
val types: MutableMap<ResourceLocation, Class<out RideController>> = mutableMapOf(
GenericLandController.KEY to GenericLandController::class.java,
GenericLiquidController.KEY to GenericLiquidController::class.java,
SwimDashController.KEY to SwimDashController::class.java,
RunUpToFlightCompositeController.KEY to RunUpToFlightCompositeController::class.java,
BirdAirController.KEY to BirdAirController::class.java,
HelicopterAirController.KEY to HelicopterAirController::class.java,
GliderAirController.KEY to GliderAirController::class.java,
FallToFlightCompositeController.KEY to FallToFlightCompositeController::class.java,
VehicleLandController.KEY to VehicleLandController::class.java,
DolphinController.KEY to DolphinController::class.java,
JetAirController.KEY to JetAirController::class.java
)
override fun deserialize(element: JsonElement, type: Type, context: JsonDeserializationContext): RideController {
val root = element.asJsonObject
val key = root.get("key").asString
val controllerType = types[key.asIdentifierDefaultingNamespace()] ?: throw IllegalArgumentException("Unknown controller: $key")
return context.deserialize(element, controllerType)
}
}
@@ -0,0 +1,41 @@
{
"format_version": "1.8.0",
"animations": {
"animation.rotom_drone.sleep": {
"loop": true,
"bones": {
"body": {
"rotation": [
"math.sin(query.anim_time * 120) * 6",
0,
"math.cos(query.anim_time * 100) * 4"
],
"position": [
0,
"18 + math.sin(query.anim_time * 90) * 1",
0
]
},
"head": {
"relative_to": {
"rotation": "entity"
},
"rotation": [
0,
0,
0.01
]
}
}
},
"animation.rotom_drone.circle": {
"loop": true,
"bones": {
"rotom_drone": {
"rotation": [30, "90 - q.anim_time*90", 0],
"position": ["48*math.cos(q.anim_time*90)", 64, "48*math.sin(q.anim_time*90)"]
}
}
}
}
}
@@ -0,0 +1,208 @@
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.drone_rotom",
"texture_width": 64,
"texture_height": 32,
"visible_bounds_width": 8,
"visible_bounds_height": 6.5,
"visible_bounds_offset": [0, 2.75, 0]
},
"bones": [
{
"name": "rotom_drone",
"pivot": [0, 0, 0]
},
{
"name": "body",
"parent": "rotom_drone",
"pivot": [0, 8, 0]
},
{
"name": "head",
"parent": "body",
"pivot": [0, 6.5, 0],
"cubes": [
{"origin": [-5.5, 6.5, -3.5], "size": [11, 6, 7], "uv": [0, 0]}
]
},
{
"name": "eyes",
"parent": "head",
"pivot": [0.5, 6.5, -2.5]
},
{
"name": "eye_right",
"parent": "eyes",
"pivot": [-2.9, 9.5, -3.5],
"rotation": [0, 0, -7.5],
"cubes": [
{"origin": [-3.9, 7.5, -3.5], "size": [2, 4, 0], "inflate": 0.01, "uv": [0, 0], "mirror": true},
{"origin": [-3.9, 9, -3.5], "size": [2, 1, 0], "inflate": 0.02, "uv": [0, 6], "mirror": true}
]
},
{
"name": "pupil_right",
"parent": "eye_right",
"pivot": [-2.9, 9.5, -3.5],
"cubes": [
{"origin": [-3.4, 8, -3.5], "size": [1, 3, 0], "inflate": 0.03, "uv": [5, 0], "mirror": true}
]
},
{
"name": "eye_left",
"parent": "eyes",
"pivot": [2.9, 9.5, -3.5],
"rotation": [0, 0, 7.5],
"cubes": [
{"origin": [1.9, 7.5, -3.5], "size": [2, 4, 0], "inflate": 0.01, "uv": [0, 13]},
{"origin": [1.9, 9, -3.5], "size": [2, 1, 0], "inflate": 0.02, "uv": [0, 5]}
]
},
{
"name": "pupil_left",
"parent": "eye_left",
"pivot": [2.9, 9.5, -3.5],
"cubes": [
{"origin": [2.4, 8, -3.5], "size": [1, 3, 0], "inflate": 0.03, "uv": [6, 13]}
]
},
{
"name": "mouth_smile",
"parent": "head",
"pivot": [0, 7.55, -3.5],
"cubes": [
{"origin": [-1, 6.95, -3.5], "size": [2, 1, 0], "inflate": 0.02, "uv": [36, 7]},
{"origin": [-1.5, 7.25, -3.5], "size": [1, 1, 0], "inflate": 0.01, "uv": [40, 7]},
{"origin": [0.5, 7.25, -3.5], "size": [1, 1, 0], "inflate": 0.01, "uv": [42, 7]}
]
},
{
"name": "mouth_closed",
"parent": "head",
"pivot": [0, 7.45, -3.4],
"cubes": [
{"origin": [-0.5, 6.95, -3.4], "size": [1, 1, 0], "inflate": 0.02, "uv": [38, 8]}
]
},
{
"name": "mouth_open",
"parent": "head",
"pivot": [0, 7.45, -3.4],
"cubes": [
{"origin": [-0.5, 6.95, -3.4], "size": [1, 1, 0], "inflate": 0.02, "uv": [36, 8]}
]
},
{
"name": "mouth_sad",
"parent": "head",
"pivot": [0, 7.6, -3.3],
"cubes": [
{"origin": [-1, 7.3, -3.3], "size": [2, 1, 0], "inflate": 0.02, "uv": [36, 9]},
{"origin": [-1.5, 7, -3.3], "size": [1, 1, 0], "inflate": 0.01, "uv": [40, 9]},
{"origin": [0.5, 7, -3.3], "size": [1, 1, 0], "inflate": 0.01, "uv": [42, 9]}
]
},
{
"name": "head_spike1",
"parent": "head",
"pivot": [0, 12.5, 0],
"cubes": [
{"origin": [-1.5, 11.5, -1.5], "size": [3, 4, 3], "uv": [29, 0]}
]
},
{
"name": "head_spike2",
"parent": "head_spike1",
"pivot": [0, 15.5, 0],
"cubes": [
{"origin": [-1, 14.5, -1], "size": [2, 4, 2], "uv": [8, 13]}
]
},
{
"name": "head_spike3",
"parent": "head_spike2",
"pivot": [0, 18.5, 0],
"cubes": [
{"origin": [-0.5, 17.5, -0.5], "size": [1, 3, 1], "uv": [44, 6]}
]
},
{
"name": "torso",
"parent": "body",
"pivot": [0, 7, 0],
"cubes": [
{"origin": [-2, 3, -1.5], "size": [4, 4, 3], "uv": [34, 10]}
]
},
{
"name": "camera",
"parent": "torso",
"pivot": [0, 4.65, -1.25],
"cubes": [
{"origin": [-1, 3.65, -2.75], "size": [2, 2, 2], "uv": [24, 13]},
{"origin": [-0.5, 4.15, -2.75], "size": [1, 1, 0], "inflate": 0.01, "uv": [38, 0]}
]
},
{
"name": "propeller_front_right",
"parent": "torso",
"pivot": [-1.5, 5, -1],
"rotation": [0, 45, 0],
"cubes": [
{"origin": [-2, 5, -3.5], "size": [1, 0, 3], "uv": [37, 0]},
{"origin": [-5.5, 5, -11.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "propeller_front_left",
"parent": "torso",
"pivot": [1.5, 5, -1],
"rotation": [0, -45, 0],
"cubes": [
{"origin": [1, 5, -3.5], "size": [1, 0, 3], "uv": [27, 0]},
{"origin": [-2.5, 5, -11.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "propeller_back_right",
"parent": "torso",
"pivot": [-1.5, 5, 1],
"rotation": [0, -45, 0],
"cubes": [
{"origin": [-2, 5, 0.5], "size": [1, 0, 3], "uv": [39, 0]},
{"origin": [-5.5, 5, 3.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "propeller_back_left",
"parent": "torso",
"pivot": [1.5, 5, 1],
"rotation": [0, 45, 0],
"cubes": [
{"origin": [1, 5, 0.5], "size": [1, 0, 3], "uv": [42, 3]},
{"origin": [-2.5, 5, 3.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "tail1",
"parent": "torso",
"pivot": [0, 3, 0],
"cubes": [
{"origin": [-1, 1, -1], "size": [2, 3, 2], "uv": [16, 13]}
]
},
{
"name": "tail2",
"parent": "tail1",
"pivot": [0, 1, 0],
"cubes": [
{"origin": [-0.5, -1, -0.5], "size": [1, 3, 1], "uv": [41, 3]}
]
}
]
}
]
}
@@ -0,0 +1,8 @@
{
"poses": {
"sleep": {
"poseTypes": ["SLEEP"],
"animations": ["q.bedrock('rotom_drone', 'sleep')"]
}
}
}
@@ -0,0 +1,13 @@
{
"name": "cobblemon:rotom_drone",
"order": 0,
"variations": [
{
"aspects": [],
"poser": "cobblemon:rotom_drone",
"model": "cobblemon:rotom_drone.geo",
"texture": "cobblemon:textures/drone/rotom_drone.png",
"layers": []
}
]
}
@@ -6,32 +6,26 @@
"bones": {
"arm_left": {
"rotation": [
"-23.2941+math.r2d(-math.cos(v.limb_swing * 38.17) * v.limb_swing_amount - math.sin(v.age_in_ticks * 5.16) * 0.05)",
3.57079,
"6.5998-math.r2d(math.cos(v.age_in_ticks * 5.16) * 0.05 + 0.05)"
"math.r2d(-math.cos(v.limb_swing * 38.17) * v.limb_swing_amount - math.sin(v.age_in_ticks * 5.16) * 0.05)",
0,
"-math.r2d(math.cos(v.age_in_ticks * 5.16) * 0.05 + 0.05)"
]
},
"arm_right": {
"rotation": [
"-20.8359+math.r2d(math.cos(v.limb_swing * 38.17) * v.limb_swing_amount + math.sin(v.age_in_ticks * 5.16) * 0.05)",
-2.68108,
"-7.007+math.r2d(math.cos(v.age_in_ticks * 5.16) * 0.05 + 0.05)"
"math.r2d(math.cos(v.limb_swing * 38.17) * v.limb_swing_amount + math.sin(v.age_in_ticks * 5.16) * 0.05)",
0,
"math.r2d(math.cos(v.age_in_ticks * 5.16) * 0.05 + 0.05)"
]
},
"pokeball": {
"scale": 0
},
"leg_right": {
"rotation": ["-87.8218+math.r2d(math.cos(v.limb_swing * 38.17 + 180) * v.limb_swing_amount * 1.4)", 8.11827, 0.65893]
"rotation": ["math.r2d(math.cos(v.limb_swing * 38.17 + 180) * v.limb_swing_amount * 1.4)", 0, 0]
},
"leg_left": {
"rotation": ["-88.8357+math.r2d(math.cos(v.limb_swing * 38.17) * v.limb_swing_amount * 1.4)", -8.23239, 1.90829]
},
"body": {
"rotation": [0, 0, 0]
},
"head": {
"rotation": ["7.5-v.velocity*10", 0, 0]
"rotation": ["math.r2d(math.cos(v.limb_swing * 38.17) * v.limb_swing_amount * 1.4)", 0, 0]
}
}
},
@@ -130,14 +124,8 @@
},
"pokeball": {
"scale": {
"0.4583": {
"pre": [0, 0, 0],
"post": [0.3, 0.3, 0.3]
},
"0.5417": {
"pre": [0.3, 0.3, 0.3],
"post": [0.3, 0.3, 0.3]
},
"0.4583": {"pre": [0, 0, 0], "post": [0.3, 0.3, 0.3]},
"0.5417": {"pre": [0.3, 0.3, 0.3], "post": [0.3, 0.3, 0.3]},
"0.7083": [1, 1, 1]
}
},
@@ -634,21 +622,12 @@
},
"pokeball": {
"scale": {
"0.9375": {
"pre": [0, 0, 0],
"post": [0.3, 0.3, 0.3]
},
"1.0": {
"pre": [0.3, 0.3, 0.3],
"post": [0.3, 0.3, 0.3]
},
"0.9375": {"pre": [0, 0, 0], "post": [0.3, 0.3, 0.3]},
"1.0": {"pre": [0.3, 0.3, 0.3], "post": [0.3, 0.3, 0.3]},
"1.1875": [1, 1, 1],
"3.0625": [1, 1, 1],
"3.25": [0.4, 0.4, 0.4],
"3.375": {
"pre": [0.4, 0.4, 0.4],
"post": [0, 0, 0]
}
"3.375": {"pre": [0.4, 0.4, 0.4], "post": [0, 0, 0]}
}
},
"leg_right": {
@@ -2272,59 +2272,23 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.4167": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"0.5833": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.0417": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.375": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"3.0": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"5.875": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.4167": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"0.5833": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.0417": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.375": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"3.0": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"5.875": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.4167": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"0.5833": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.0417": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.375": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"3.0": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"5.875": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.4167": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"0.5833": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.0417": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.375": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"3.0": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"5.875": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
@@ -2589,27 +2553,15 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"1.25": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"3.6667": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"1.25": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"3.6667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"1.25": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"3.6667": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"1.25": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"3.6667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"jaw": {
@@ -8811,43 +8763,19 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.25": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"0.5": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.25": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.4583": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.25": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"0.5": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.25": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.4583": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.25": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"0.5": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.25": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.4583": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.25": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"0.5": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.25": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.4583": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
@@ -11293,59 +11221,23 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.25": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"0.4167": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"0.8333": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"1.125": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.5417": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.6667": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.25": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"0.4167": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"0.8333": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"1.125": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.5417": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.6667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.25": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"0.4167": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"0.8333": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"1.125": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.5417": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.6667": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.25": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"0.4167": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"0.8333": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"1.125": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.5417": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.6667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"locator_target": {
@@ -13637,43 +13529,19 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"1.2083": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.5417": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.7083": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.3333": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"1.2083": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.5417": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.7083": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"1.2083": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.5417": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.7083": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.3333": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"1.2083": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.5417": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.7083": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"locator_mouth": {
@@ -13872,20 +13740,14 @@
"position": {
"0.0": [-0.1, 0.1, 0],
"0.125": [-0.1, 0.1, 0],
"0.1667": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.125": [0.1, 0.1, 0],
"0.1667": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"jaw": {
@@ -19235,59 +19097,23 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.4167": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"0.5833": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.4167": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.5833": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"3.9167": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"4.0833": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.4167": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"0.5833": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.4167": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.5833": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"3.9167": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"4.0833": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.4167": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"0.5833": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.4167": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.5833": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"3.9167": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"4.0833": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.4167": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"0.5833": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.4167": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.5833": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"3.9167": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"4.0833": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
@@ -22632,10 +22458,7 @@
},
"0.5": [0.78, -10.13, 0.45],
"0.5417": [-0.45, -8.03, 0.45],
"0.5833": {
"pre": [-0.45, -8.03, 0.45],
"post": [-0.45, "-7.79+360", 0.45]
},
"0.5833": {"pre": [-0.45, -8.03, 0.45], "post": [-0.45, "-7.79+360", 0.45]},
"0.625": [-2.7, 355.37, 0.45],
"1.0": {
"pre": [-9.46, "10.71+360", 0.5],
@@ -26306,59 +26129,23 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.0833": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"0.3333": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"1.625": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"1.8333": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.625": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.0833": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"0.3333": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"1.625": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"1.8333": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.625": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.0833": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"0.3333": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"1.625": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"1.8333": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.625": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.0833": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"0.3333": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"1.625": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"1.8333": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.625": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"locator_root": {
@@ -29598,15 +29385,9 @@
"brow": {
"position": {
"0.9583": [0, 0, 0],
"1.1667": {
"pre": [0, 0, 0],
"post": [0, 0, -0.3]
},
"1.1667": {"pre": [0, 0, 0], "post": [0, 0, -0.3]},
"2.2917": [0, 0, -0.3],
"2.5417": {
"pre": [0, 0, -0.3],
"post": [0, 0, 0]
}
"2.5417": {"pre": [0, 0, -0.3], "post": [0, 0, 0]}
}
},
"jaw": {
@@ -29941,43 +29722,19 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"1.2083": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.375": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.6667": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.3333": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"1.2083": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.375": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.6667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"1.2083": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.375": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.6667": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.3333": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"1.2083": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.375": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.6667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"locator_root": {
@@ -33237,43 +32994,19 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"1.2083": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.2083": {
"pre": [0, 0, 0],
"post": [-0.1, 0.1, 0]
},
"2.4583": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.3333": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"1.2083": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]},
"2.2083": {"pre": [0, 0, 0], "post": [-0.1, 0.1, 0]},
"2.4583": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"1.2083": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
},
"2.2083": {
"pre": [0, 0, 0],
"post": [0.1, 0.1, 0]
},
"2.4583": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.3333": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"1.2083": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]},
"2.2083": {"pre": [0, 0, 0], "post": [0.1, 0.1, 0]},
"2.4583": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"locator_root": {
@@ -34699,20 +34432,14 @@
"position": {
"0.0": [-0.1, 0.1, 0],
"0.125": [-0.1, 0.1, 0],
"0.1667": {
"pre": [-0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.1667": {"pre": [-0.1, 0.1, 0], "post": [0, 0, 0]}
}
},
"eyelid_left": {
"position": {
"0.0": [0.1, 0.1, 0],
"0.125": [0.1, 0.1, 0],
"0.1667": {
"pre": [0.1, 0.1, 0],
"post": [0, 0, 0]
}
"0.1667": {"pre": [0.1, 0.1, 0], "post": [0, 0, 0]}
}
}
}
@@ -105,7 +105,7 @@
},
"animation.pidgeot.air_idle": {
"loop": true,
"animation_length": 1.3333,
"animation_length": 1.33333,
"bones": {
"body": {
"rotation": ["-10+math.sin(q.anim_time*90*3-60)*-1", 0, 0],
@@ -116,8 +116,7 @@
},
"wing_closed_left": {
"rotation": [-93.47882, -7.48957, -87.29442],
"position": [-3.8, -1, 6],
"scale": 0.1
"position": [0, -1, 6]
},
"wing_closed_left2": {
"rotation": [5.08957, -4.43385, -2.31336]
@@ -382,8 +381,7 @@
},
"wing_closed_right": {
"rotation": [-93.47882, 7.48957, 87.29442],
"position": [3.8, -1, 6],
"scale": 0.1
"position": [0, -1, 6]
},
"wing_closed_right2": {
"rotation": [5.08957, 4.43385, 2.31336]
@@ -550,9 +548,8 @@
"rotation": ["-17.5+math.sin(q.anim_time*90*0.8-90)*-1", 0, "0+math.sin(q.anim_time*90*0.5-120)*1"]
},
"wing_closed_left": {
"rotation": [-93.47882, -7.48957, -87.29442],
"position": [-3.8, -1, 6],
"scale": 0.1
"rotation": [-92.46677, -7.8791, -94.85228],
"position": [0, -1, 6]
},
"wing_closed_left2": {
"rotation": [5.08145, 3.03671, -1.64897]
@@ -690,9 +687,8 @@
"rotation": [0, 0, "-7.5+math.sin(q.anim_time*90*0.8-240)*10+math.sin(q.anim_time*90*12-120)*-0.4"]
},
"wing_closed_right": {
"rotation": [-93.47882, 7.48957, 87.29442],
"position": [3.8, -1, 6],
"scale": 0.1
"rotation": [-92.46677, 7.8791, 94.85228],
"position": [0, -1, 6]
},
"wing_closed_right2": {
"rotation": [5.08145, -3.03671, 1.64897]
@@ -1152,13 +1152,13 @@
"rotation": [-7.32417, 1.61888, "12.3963+math.sin(q.anim_time*90*1-120)*6"]
},
"tail1": {
"rotation": [-52.5, "-math.sin(q.anim_time*90*2-40)*4", 0]
"rotation": [-47.5, "0-math.sin(q.anim_time*90*2-40)*4", 0]
},
"tail2": {
"rotation": ["math.sin(q.anim_time*90*2-200)*2", "-math.sin(q.anim_time*90*2-80)*4", 0]
"rotation": ["5+math.sin(q.anim_time*90*2-200)*2", "0-math.sin(q.anim_time*90*2-80)*4", 0]
},
"tail3": {
"rotation": ["7.5+math.sin(q.anim_time*90*2-240)*2", "-math.sin(q.anim_time*90*2-120)*4", 0]
"rotation": ["10+math.sin(q.anim_time*90*2-240)*2", "-math.sin(q.anim_time*90*2-120)*4", 0]
},
"tail4": {
"rotation": ["-2.5+math.sin(q.anim_time*90*2-280)*2", "0-math.sin(q.anim_time*90*2-160)*8", 0]
@@ -1207,16 +1207,16 @@
"rotation": [-7.32417, -1.61888, "-12.3963-math.sin(q.anim_time*90*1-120)*6"]
},
"wing_left2": {
"rotation": [0, "27.5+math.sin(q.anim_time*90*2-60)*10", 0]
"rotation": [0, "27.5+math.sin(q.anim_time*90*2-60)*40", 0]
},
"wing_right2": {
"rotation": [0, "-27.5+math.sin(q.anim_time*90*2-60)*-10", 0]
"rotation": [0, "-27.5-math.sin(q.anim_time*90*2-60)*40", 0]
},
"wing_left": {
"rotation": ["0.1201+math.cos(q.anim_time*90*2+60)*5", "-19.9232+math.sin(q.anim_time*90*2+60)*10", 4.11867]
"rotation": ["0.1201+math.cos(q.anim_time*90*2+60)*20", "-19.9232+math.sin(q.anim_time*90*2+60)*40", 4.11867]
},
"wing_right": {
"rotation": ["0.1201+math.cos(q.anim_time*90*2+60)*5", "19.9232+math.sin(q.anim_time*90*2+60)*-10", -4.11867]
"rotation": ["0.1201+math.cos(q.anim_time*90*2+60)*20", "19.9232-math.sin(q.anim_time*90*2+60)*40", -4.11867]
},
"neck": {
"rotation": ["-5+math.sin(q.anim_time*90*2-160)*3", 0, 0]
@@ -1234,7 +1234,7 @@
"rotation": [0, 0, "math.sin(q.anim_time*90*1-360)*9"]
},
"body": {
"rotation": ["15-math.sin(q.anim_time*90*2-120)*4", 0, 0],
"rotation": ["-math.sin(q.anim_time*90*2-120)*4", 0, 0],
"position": [0, "7.8+math.sin(q.anim_time*90*2)*2", -5.6]
}
}
@@ -26,10 +26,7 @@
"pivot": [0, 7.75, -0.5],
"cubes": [
{"origin": [-4, 4.25, -4], "size": [8, 8, 7], "uv": [0, 44]}
],
"locators": {
"seat_1": [0, 12, -4]
}
]
},
{
"name": "upper_torso",
@@ -6,7 +6,7 @@
"identifier": "geometry.dragonite",
"texture_width": 256,
"texture_height": 128,
"visible_bounds_width": 9,
"visible_bounds_width": 8,
"visible_bounds_height": 5.5,
"visible_bounds_offset": [0, 2.25, 0]
},
@@ -34,10 +34,7 @@
"pivot": [0, 21, -0.5],
"cubes": [
{"origin": [-8, 19.5, -8], "size": [16, 9, 15], "inflate": 0.01, "uv": [33, 35]}
],
"locators": {
"seat_1": [0, 26, 7]
}
]
},
{
"name": "chest2",
@@ -199,6 +196,7 @@
"name": "head",
"parent": "neck",
"pivot": [0, 46, 1],
"rotation": [0, 0, 0],
"cubes": [
{"origin": [-5, 41, -6], "size": [10, 11, 11], "inflate": 0.01, "uv": [0, 0]}
]
@@ -7,8 +7,8 @@
"texture_width": 128,
"texture_height": 128,
"visible_bounds_width": 9,
"visible_bounds_height": 3,
"visible_bounds_offset": [0, 0.5, 0]
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
@@ -27,10 +27,7 @@
"cubes": [
{"origin": [-12, 0, -20], "size": [24, 16, 23], "uv": [0, 0]},
{"origin": [-11, 6, -22.5], "size": [22, 2, 3], "inflate": 0.01, "uv": [50, 44]}
],
"locators": {
"seat_1": [0, 16, -11]
}
]
},
{
"name": "whisker_right",
@@ -239,6 +236,7 @@
"name": "tail",
"parent": "torso",
"pivot": [0, 6, 1],
"rotation": [0, 0, 0],
"cubes": [
{"origin": [-8, 0.5, 1], "size": [16, 11, 9], "uv": [0, 39]}
]
@@ -263,6 +261,7 @@
"name": "tail4",
"parent": "tail3",
"pivot": [0, 5, 18],
"rotation": [0, 0, 0],
"cubes": [
{"origin": [-6.5, 5, 17], "size": [13, 0, 8], "uv": [42, 49]}
]
@@ -34,10 +34,7 @@
"pivot": [0, 26.2, 0],
"cubes": [
{"origin": [-5, 25.2, -4], "size": [10, 7, 8], "inflate": 0.01, "uv": [26, 32]}
],
"locators": {
"seat_1": [0, 29, 2]
}
]
},
{
"name": "fin",

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