Compare commits

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

See merge request cable-mc/cobblemon!1575
2025-03-29 04:58:25 +00:00
JacksonB b6e20d69cc fix to crouch but sprint during riding is still broken 2025-03-28 15:18:10 -05:00
JacksonB 9d04e20a30 Remove client only code causing server crash and replace with proper code. 2025-03-28 14:51:33 -05:00
Landon Wills d2624813f7 Merge branch 'refs/heads/bug-fixes' into multiplayer-riding 2025-03-28 13:36:22 -04:00
Landon Wills a2789e70ed Use readString in readExpression to be a bit more clear on it's function 2025-03-28 13:35:05 -04:00
Landon Wills f6f607904e Merge branch 'refs/heads/bug-fixes' into multiplayer-riding 2025-03-28 01:53:19 -04:00
Landon Wills 22ea0296c0 Fix issues with riding controller packet decoding 2025-03-28 01:52:03 -04:00
Landon Wills 24ae2f5324 Fix issue with registering species on servers 2025-03-28 01:51:38 -04:00
Landon Wills 9284497cec Refactor Rollable interface, fix issue with mixins crossing sides, causing server to fail to load 2025-03-28 00:21:26 -04:00
Landon Wills 36cb12952a Move more functions out of Rollable to mouse handling 2025-03-27 21:55:29 -04:00
Landon Wills 2700dc7f39 Refactor function to get angular velocity out of Rollable 2025-03-27 21:23:12 -04:00
Polymeta c9d27d46b6 license header
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-03-26 11:39:18 +00:00
Polymeta b68756378c Merge branch 'Box-count-command' into 'main'
Added /boxcount

See merge request cable-mc/cobblemon!1565
2025-03-25 17:01:22 +00:00
Polymeta 1ff1880951 Merge branch 'main' into 'Box-count-command'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
2025-03-25 16:59:25 +00:00
Polymeta fea2d32de8 Merge branch 'spectate-command' into 'main'
Added a spectate battle command.

See merge request cable-mc/cobblemon!1264
2025-03-25 16:56:49 +00:00
Conan e3e6d4452c # 2025-03-25 14:12:49 +01:00
Conan f687e8e658 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into spectate-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2025-03-25 14:07:49 +01:00
Hiroku 2413981f6f Merge branch 'charizardmarketing' into 'main'
Updated Charizard with the latest model and animations to be used for marketing on the main branch.

See merge request cable-mc/cobblemon!1572
2025-03-24 01:23:40 +00:00
Tyzillion 4ac244003a -6+ ?????? 2025-03-23 21:22:47 -04:00
Tyzillion 9ac92a084f Updated Charizard with new locators. 2025-03-23 21:18:31 -04:00
Apion aacbcb1f28 Merge branch 'jackowes/concrete_bird' into 'main'
Overhaul of bird and fix of jet velocity bug

See merge request cable-mc/cobblemon!1563
2025-03-23 23:37:47 +00:00
Jackson BowesandApion 39ff7e418c Overhaul of bird and fix of jet velocity bug 2025-03-23 23:37:47 +00:00
Hiroku 58498a091d Merge branch 'josh/npc-item-rendering' into 'main'
Molang for rendering items during animations.

See merge request cable-mc/cobblemon!1570
2025-03-23 23:12:06 +00:00
Hiroku a806f77aff Merge branch 'entity-scale' into 'main'
Pokemon hitboxes scale with attribute generic.scale

Closes #1410

See merge request cable-mc/cobblemon!1529
2025-03-23 22:05:38 +00:00
JordanandHiroku 60ae9e3a55 Pokemon hitboxes scale with attribute generic.scale 2025-03-23 22:05:37 +00:00
Josh 4bfe147730 Merge branch 'main' into josh/npc-item-rendering 2025-03-22 12:41:31 -04:00
Josh 963614f5fa Updated CHANGELOG.md 2025-03-21 11:51:18 -04:00
Josh 7fdb70e3c3 Updated trainer npcs for testing 2025-03-21 11:20:24 -04:00
Josh 37e8f0eab6 multiple items can render on a model per locator and the render_item() molang function now supports items with components 2025-03-20 16:14:16 -04:00
ShiroFenixlm a6d17659f8 Added removeListOfBoxes to PCStore 2025-03-19 23:31:43 -04:00
Josh 05edafa5c4 NPC Trainers send out/return animation are able to show the correct ball. 2025-03-19 22:35:00 -04:00
ShiroFenixlm 997a6de23b Remove and Set now only delete empty boxes unless forced 2025-03-19 21:52:08 -04:00
Josh cea600617e Added render_item() molang function for PosableState. Allows rendering dummy items for animations. 2025-03-19 18:47:11 -04:00
ShiroFenixlm c948b6d885 Added force remove button 2025-03-18 19:40:53 -04:00
ShiroFenixlm 5508980384 Final Cleanup, i hope 2025-03-17 15:24:02 -04:00
ShiroFenixlm e39dee01e5 Cleanup 2025-03-17 15:16:06 -04:00
ShiroFenixlm a81412e176 Merge remote-tracking branch 'origin/Box-count-command' into Box-count-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/command/ChangePCBoxesCommand.kt
2025-03-17 15:15:01 -04:00
ShiroFenixlm 09f9dd91aa Fixed some issues and added error handling when removing too many boxes 2025-03-17 15:08:07 -04:00
ShiroFenixlm 2efc3c72ac Fixed some issues and added error handling when removing too many boxes 2025-03-17 15:02:57 -04:00
ShiroFenix a92fb03093 Updated command logic to improve legibility 2025-03-17 12:56:15 -04:00
ShiroFenix ad5847983f Updated command logic to improve legibility 2025-03-17 12:55:59 -04:00
WhiteFenix19 bb25300513 Updated command logic to improve legibility 2025-03-17 12:32:46 -04:00
WhiteFenix19 de33804eb4 Updated command logic to improve legibility 2025-03-17 12:19:47 -04:00
WhiteFenix19 de042f71dd Updated command logic to improve legibility 2025-03-17 12:18:28 -04:00
WhiteFenix19 5a1e85bbf4 Added /boxcount command 2025-03-17 00:47:00 -04:00
ShiroFenix 3489e0df74 Revert "Added /boxcount"
This reverts commit afe8eec343
2025-03-17 03:59:02 +00:00
ShiroFenix afe8eec343 Added /boxcount 2025-03-17 03:43:32 +00:00
Conan 8913d3df11 Reworked SpectateBattleHandler.kt 2024-12-30 18:23:58 +01:00
Conan 3a55965704 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into spectate-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
2024-12-29 12:30:54 +01:00
Conan 7112c74bf2 Reworked spectate command. 2024-11-09 22:37:11 +01:00
Conan faa1906290 Added a spectate battle command. 2024-11-09 17:30:01 +01:00
71 changed files with 2020 additions and 1126 deletions
+4
View File
@@ -2,6 +2,7 @@
## [1.7.0 (Month xth, 2025)](#1-7-0)
### Additions
- Added `/spectateBattle <player>` command to spectate battles without having to manually walk up to the target.
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
- Added `blacklisted_items_to_hold` and `whitelisted_items_to_hold` tags to allow for controlling which items players can give to their Pokémon. If the whitelist is empty, it will consider all item as allowed (unless they are in the blacklist).
@@ -10,6 +11,7 @@
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
- Added the various log blocks as cosmetic items for Timburr and Komala.
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
- Added `/boxcount` command to change PC boxes amount
### Changes
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
@@ -17,6 +19,7 @@
- Substantially optimised spawning checks mainly by front-loading biome filtering.
- When using the `cobblemon` or `generation_9` capture calculators a critical capture with a single shake will always play for successful captures when you've already registered the Pokémon as caught in your Pokédex.
- Improved the performance of saving Pokédex and player data.
- Pokémon hitbox now scales with entity attribute `generic.scale`.
### Fixes
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
@@ -71,6 +74,7 @@
- Added `interpolate` boolean property to animated textures to allow gradual colour changes between frames.
- Fixed species additions not being capable of changing implemented status.
- Added support for action effects that are triggered by `|-activate|` Showdown instructions. `activate_{effect_id}` is the syntax.
- Added Molang functions for rendering items `render_item(item_id, locator_name)` and `clear_items()`.
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import java.util.function.Function;
public interface OrientationControllable {
OrientationController getOrientationController();
}
@@ -1,143 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common;
import net.minecraft.util.Mth;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import java.util.function.Function;
public interface Rollable {
// From Minecraft's Camera
Vector3f FORWARDS = new Vector3f(0.0F, 0.0F, -1.0F);
Vector3f UP = new Vector3f(0.0F, 1.0F, 0.0F);
Vector3f LEFT = new Vector3f(-1.0F, 0.0F, 0.0F);
@Nullable Matrix3f getOrientation();
Rollable updateOrientation(Function<Matrix3f, Matrix3f> update);
/**
* @return The forward vector (0, 0, -1) after the rotation matrix is applied.
*/
default Vector3f getForwardVector() {
if (getOrientation() == null) return new Vector3f(FORWARDS);
return getOrientation().transform(FORWARDS, new Vector3f());
}
/**
* @return The left vector (-1, 0, 0) after the rotation matrix is applied.
*/
default Vector3f getLeftVector() {
if (getOrientation() == null) return new Vector3f(LEFT);
return getOrientation().transform(LEFT, new Vector3f());
}
/**
* @return The up vector (0, 1, 0) after the rotation matrix is applied.
*/
default Vector3f getUpVector() {
if (getOrientation() == null) return new Vector3f(UP);
return getOrientation().transform(UP, new Vector3f());
}
/**
*
* @param yaw change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateYaw(float yaw) {
return updateOrientation(prev -> prev.rotateY((float) Math.toRadians(-yaw)));
}
/**
*
* @param pitch change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotatePitch(float pitch) {
return updateOrientation(prev -> prev.rotateX((float) Math.toRadians(-pitch)));
}
/**
*
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotateRoll(float roll) {
return updateOrientation(prev -> prev.rotateZ((float) Math.toRadians(-roll)));
}
/**
* Order matters when rotating an object. If you need a different order, individually call rotateYaw, rotatePitch, and rotateRoll
*
* @param yaw change in degrees
* @param pitch change in degrees
* @param roll change in degrees
* @return The Rollable object this method was called on
*/
default Rollable rotate(float yaw, float pitch, float roll) {
return rotateYaw(yaw).rotatePitch(pitch).rotateRoll(roll);
}
/**
* @return The pitch angle in degrees as a float.
* Value is between [-180, 180] with 0 being South, 90 being West, 180/-180 being North, and -90 being East
*/
default float getYaw() {
return (float) Mth.wrapDegrees(-Math.toDegrees(FORWARDS.angleSigned(getForwardVector(), UP)) + 180);
}
/**
* @return The yaw angle in degrees as a float.
* Value is between [-90, 90] with 0 being straight forward, 90 being straight down, and -90 being straight up
*/
default float getPitch() {
return (float) Mth.wrapDegrees(-Math.toDegrees(Math.asin(getForwardVector().y)));
}
/**
* @return The roll angle in degrees as a float.
* Value is between [-180, 180] with 0 being no roll (normal), 90 being 90 degrees CW, 180/-180 being 180 degrees CW/CCW, and -90 being 90 degrees CCW
*/
default float getRoll() {
//Cobblemon.LOGGER.info("{}", (float) Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
return (float) Mth.wrapDegrees(-Math.toDegrees(getUpVector().angleSigned(UP, getForwardVector())));
}
/**
* Get the angular velocity in degrees per second as a Vec3
*/
Vec3 angRollVel( double deltaTime );
public Vec3 rotationOnMouseXY(
double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime
);
boolean shouldRoll();
boolean useAngVelSmoothing();
void clearRotation();
}
@@ -10,9 +10,10 @@ package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.CobblemonItems;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.events.CobblemonEvents;
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.api.storage.party.PartyStore;
@@ -31,14 +32,11 @@ import com.cobblemon.mod.common.util.DataKeys;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.SmoothDouble;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@@ -46,18 +44,14 @@ import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -65,11 +59,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements Rollable, ScannableEntity {
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable {
@Shadow public abstract CompoundTag getShoulderEntityLeft();
@@ -87,7 +80,7 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Shadow public abstract void displayClientMessage(Component message, boolean overlay);
@Unique private Matrix3f orientation = null;
@Unique private OrientationController cobblemon$orientationController = new OrientationController(this);
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
super(p_20966_, p_20967_);
@@ -241,7 +234,7 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
@Override
public void absMoveTo(double x, double y, double z, float yaw, float pitch) {
if (shouldRoll()) {
if (cobblemon$orientationController.getActive()) {
this.absMoveTo(x, y, z);
this.setYRot(yaw % 360.0f);
this.setXRot(pitch % 360.0f);
@@ -254,111 +247,6 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
}
}
@Inject(method = "tick", at = @At("HEAD"))
public void tick(CallbackInfo ci) {
if (this.shouldRoll()) {
// Proof of concept for yaw movement
if (this.getMainHandItem().is(Items.STICK)) {
this.rotateYaw(2);
}
else if (this.getMainHandItem().is(Items.BLAZE_ROD)) {
this.rotateYaw(-2);
}
}
}
@Override
public Matrix3f getOrientation() {
return orientation;
}
@Override
public Rollable updateOrientation(Function<Matrix3f, Matrix3f> update) {
//Disabled to allow non mouse inputs to modify roll
if (!shouldRoll()) return this;
if (this.orientation == null) {
this.orientation = new Matrix3f();
this.rotate(getYRot() - 180, getXRot(), 0);
}
this.orientation = update.apply(this.orientation);
setYRot(this.getYaw());
setXRot(this.getPitch());
return this;
}
@Override
public boolean shouldRoll() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return false;
if (player.isHolding(Items.DIAMOND)) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Override
public Vec3 rotationOnMouseXY(
double yMouse,
double xMouse,
SmoothDouble yMouseSmoother,
SmoothDouble xMouseSmoother,
double sensitivity,
double deltaTime
) {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return Vec3.ZERO;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return Vec3.ZERO;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
return pokemonEntity.getRiding().rotationOnMouseXY(
pokemonEntity,
player,
yMouse,
xMouse,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime
);
}
@Override
public boolean useAngVelSmoothing() {
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return true;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
return pokemonEntity.getRiding().useAngVelSmoothing(pokemonEntity);
}
@Override
public Vec3 angRollVel( double deltaTime ) {
var ret = new Vec3(0.0, 0.0, 0.0);
LocalPlayer player = Minecraft.getInstance().player;
if (!this.equals(player)) return ret;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return ret;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return ret;
return pokemonEntity.getRiding().angRollVel( pokemonEntity, player, deltaTime );
}
@Override
public void clearRotation() {
this.orientation = null;
}
@WrapOperation(
method = "rideTick()V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Player;wantsToStopRiding()Z")
@@ -370,4 +258,8 @@ public abstract class PlayerMixin extends LivingEntity implements Rollable, Scan
return original.call(instance);
}
@Override
public OrientationController getOrientationController() {
return cobblemon$orientationController;
}
}
@@ -0,0 +1,103 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.mixin.accessor.ChunkMapAccessor;
import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
import com.cobblemon.mod.common.net.messages.client.orientation.S2CUpdateOrientationPacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.network.protocol.Packet;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Set;
import java.util.function.Consumer;
@Mixin(ServerEntity.class)
public abstract class ServerEntityMixin {
@Shadow @Final private Consumer<Packet<?>> broadcast;
@Shadow @Final private Entity entity;
@Unique
private Matrix3f cobblemon$lastSentOrientation;
private boolean cobblemon$lastSentActive;
@Inject(method = "sendChanges", at = @At("TAIL"))
private void cobblemon$sendOrientationChanges(CallbackInfo ci) {
if (!(this.entity instanceof OrientationControllable controllable)) return;
OrientationController controller = controllable.getOrientationController();
Matrix3f currOrientation = controller.getOrientation();
boolean currActive = controller.getActive();
if (currOrientation == null || currOrientation.equals(cobblemon$lastSentOrientation) && (currActive == cobblemon$lastSentActive)) return;
cobblemon$lastSentOrientation = new Matrix3f(currOrientation);
cobblemon$lastSentActive = currActive;
if (!(entity.level() instanceof ServerLevel level)) return;
ChunkMap chunkMap = level.getChunkSource().chunkMap;
Int2ObjectMap<?> entityMap = ((ChunkMapAccessor) chunkMap).getEntityMap();
Object tracked = entityMap.get(entity.getId());
if (!(tracked instanceof TrackedEntityAccessor tracker)) return;
Set<ServerPlayerConnection> seenBy = tracker.getSeenBy();
for (ServerPlayerConnection conn : seenBy) {
ServerPlayer player = conn.getPlayer();
if (player == entity) continue;
CobblemonNetwork.INSTANCE.sendPacketToPlayer(player, new S2CUpdateOrientationPacket(currOrientation, currActive, entity.getId()));
}
/*
OrientationController controller = controllable.getOrientationController();
Matrix3f currOrientation = controller.getOrientation();
if(currOrientation != cobblemon$lastSentOrientation) {
cobblemon$lastSentOrientation = new Matrix3f(currOrientation);
//this.broadcast.accept(new S2CUpdateOrientationPacket(currOrientation,this.entity.getId()))
if (!(entity.level() instanceof ServerLevel level)) return;
ChunkMap chunkMap = level.getChunkSource().chunkMap;
Long2ObjectMap<ChunkMap.TrackedEntity> entityMap = ((ChunkMapAccessor) chunkMap).getEntityMap();
TrackedEntity trackedEntity = entityMap.get(entity.getId());
if (trackedEntity == null) return;
Set<ServerPlayerConnection> seenBy = ((TrackedEntityAccessor) trackedEntity).getSeenBy();
for (ServerPlayerConnection conn : seenBy) {
ServerPlayer player = conn.getPlayer();
if (player == entity) continue; // optional safety filter
CobblemonNetwork.sendPacketToPlayer(player, new S2CUpdateOrientationPacket(orientation, entity.getId()));
}
*/
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import net.minecraft.server.level.ServerPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayer.class)
public abstract class ServerPlayerMixin {
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((ServerPlayer)(Object)this);
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(ServerPlayer player) {
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Inject(method = "stopRiding", at = @At("HEAD"))
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.server.level.ChunkMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ChunkMap.class)
public interface ChunkMapAccessor {
@Accessor("entityMap")
Int2ObjectMap<?> getEntityMap();
}
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.server.network.ServerPlayerConnection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Set;
@Mixin(targets = "net.minecraft.server.level.ChunkMap$TrackedEntity")
public interface TrackedEntityAccessor {
@Accessor("seenBy")
Set<ServerPlayerConnection> getSeenBy();
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
@@ -35,10 +35,6 @@ public abstract class CameraMixin {
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow public abstract float getXRot();
@Shadow public abstract float getYRot();
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@@ -47,51 +43,53 @@ public abstract class CameraMixin {
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void open_camera$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof Rollable rollable) || disableRollableCameraDebug) return;
if (!rollable.shouldRoll() && rollable.getOrientation() != null) {
if (!(this.entity instanceof OrientationControllable controllable) || disableRollableCameraDebug) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() && controller.getOrientation() != null) {
if(this.returnTimer < 1) {
//Rotation is taken from entity since we no longer handle mouse ourselves
//Stops a period of time when you can't input anything.
rollable.getOrientation().set(
controller.getOrientation().set(
new Matrix3f()
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
);
if(rollAngleStart == 0){
this.returnTimer = 1;
rollable.clearRotation();
controller.reset();
return;
}
rollable.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
this.returnTimer = 1;
rollable.clearRotation();
controller.reset();
}
return;
}
if (rollable.getOrientation() == null) return;
if (controller.getOrientation() == null) return;
applyRotation();
this.returnTimer = 0;
this.rollAngleStart = rollable.getRoll();
this.rollAngleStart = controller.getRoll();
ci.cancel();
}
@Unique
private void applyRotation(){
if (!(this.entity instanceof Rollable rollable) || rollable.getOrientation() == null) return;
var newRotation = rollable.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null) return;
var controller = controllable.getOrientationController();
var newRotation = controller.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
if (this.minecraft.options.getCameraType().isMirrored()) {
newRotation.rotateY((float)Math.toRadians(180));
}
this.rotation.set(newRotation);
this.xRot = rollable.getPitch();
this.yRot = rollable.getYaw();
this.forwards.set(rollable.getForwardVector());
this.up.set(rollable.getUpVector());
this.left.set(rollable.getLeftVector());
this.xRot = controller.getPitch();
this.yRot = controller.getYaw();
this.forwards.set(controller.getForwardVector());
this.up.set(controller.getUpVector());
this.left.set(controller.getLeftVector());
}
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.item.PokedexItem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
@@ -31,14 +31,14 @@ public class ItemInHandRendererMixin {
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
ci.cancel();
}
else if (abstractClientPlayer instanceof Rollable rollable && rollable.shouldRoll()) {
else if (abstractClientPlayer instanceof OrientationControllable controllable && controllable.getOrientationController().isActive()) {
}
}
@Redirect(method = "renderHandsWithItems", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V", ordinal = 1))
private void cobblemon$renderHandswithItems(PoseStack instance, Quaternionf quaternion) {
if (!(Minecraft.getInstance().player instanceof Rollable rollable && rollable.shouldRoll())) {
if (!(Minecraft.getInstance().player instanceof OrientationControllable controllable && controllable.getOrientationController().isActive())) {
instance.mulPose(quaternion);
}
}
@@ -9,9 +9,15 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import org.joml.Matrix3f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@@ -20,24 +26,53 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LocalPlayer.class)
public class LocalPlayerMixin {
public abstract class LocalPlayerMixin extends LivingEntity {
@Unique Matrix3f cobblemon$lastOrientation;
protected LocalPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
}
@Inject(method = "sendPosition", at = @At("TAIL"))
private void cobblemon_updateRotationMatrix(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
private void cobblemon$updateRotationMatrix(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(controller.getOrientation()));
}
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
private void cobblemon_updateRotationMatrixPassenger(CallbackInfo ci) {
if (!(this instanceof Rollable rollable)) return;
if (rollable.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = rollable.getOrientation() != null ? new Matrix3f(rollable.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(rollable.getOrientation()));
private void cobblemon$updateRotationMatrixPassenger(CallbackInfo ci) {
if (!(this instanceof OrientationControllable controllable)) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
CobblemonNetwork.INSTANCE.sendToServer(new C2SUpdateOrientationPacket(controller.getOrientation()));
}
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this instanceof OrientationControllable controllable)) return;
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((LocalPlayer)(Object)this);
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return false;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
return pokemonEntity.getRiding().shouldRoll(pokemonEntity);
}
@Override
public void stopRiding() {
super.stopRiding();
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this instanceof OrientationControllable controllable)) return;
controllable.getOrientationController().setActive(false);
}
}
@@ -8,13 +8,13 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Rollable;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.client.CobblemonClient;
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.Blaze3D;
import net.minecraft.client.Minecraft;
import net.minecraft.client.MouseHandler;
import net.minecraft.client.player.LocalPlayer;
@@ -49,8 +49,6 @@ public class MouseHandlerMixin {
@Shadow private double accumulatedDY;
@Shadow private double accumulatedScrollX;
@Inject(
method = "onScroll",
at = @At(
@@ -121,9 +119,9 @@ public class MouseHandlerMixin {
return false;
}
if (!(player instanceof Rollable rollable)) return true;
if (!(player instanceof OrientationControllable controllable)) return true;
if (!rollable.shouldRoll()) {
if (!controllable.getOrientationController().isActive()) {
xMouseSmoother.reset();
yMouseSmoother.reset();
pitchSmoother.reset();
@@ -132,68 +130,100 @@ public class MouseHandlerMixin {
return true;
}
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
var ridingSensitivity = Math.pow(defaultSensitivity, 3);
//Send mouse input to be interpreted into rotation
//deltas by the ride controller
Vec3 angVecMouse = rollable.rotationOnMouseXY(
cursorDeltaY,
cursorDeltaX ,
yMouseSmoother,
xMouseSmoother,
ridingSensitivity,
movementTime
);
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
//Perform Rotation using mouse influenced rotation deltas.
rollable.rotate(
controllable.getOrientationController().rotate(
(float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z
);
//Gather and apply the current rotation deltas
var angRot = rollable.angRollVel(movementTime);
var angRot = cobblemon$getAngularVelocity(movementTime);
//Apply smoothing if requested by the controller.
//This Might be best if done by the controller itself?
if( rollable.useAngVelSmoothing() )
if(cobblemon$shouldUseAngVelSmoothing())
{
if( angRot != null ) {
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
rollable.rotate((float) yaw, (float) pitch, (float) roll);
}
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
}
//Otherwise simply apply the smoothing
else
{
if( angRot != null ) {
rollable.rotate((float) (
angRot.x * 10 * d),
(float) (angRot.y * 10 * d),
(float) (angRot.z * 10 * d)
);
}
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
}
return false;
}
@Inject(method = "handleAccumulatedMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MouseHandler;isMouseGrabbed()Z", ordinal = 0))
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci, @Local(ordinal = 1) double e) {
if (minecraft.player == null) return;
if (!(minecraft.player instanceof Rollable rollable)) return;
if (!rollable.shouldRoll()) return;
if (!(minecraft.player instanceof OrientationControllable controllable)) return;
if (!controllable.getOrientationController().isActive()) return;
if (minecraft.isPaused()) return;
var pitch = pitchSmoother.getNewDeltaValue(0, e);
var roll = rollSmoother.getNewDeltaValue(0, e);
rollable.rotate(0.0F, (float)pitch, (float)roll);
controllable.getOrientationController().rotate(0.0F, (float)pitch, (float)roll);
}
@Unique
private Vec3 cobblemon$getAngularVelocity(double deltaTime) {
var player = minecraft.player;
if (player == null) return Vec3.ZERO;
if (!(player instanceof OrientationControllable)) return Vec3.ZERO;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return Vec3.ZERO;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return Vec3.ZERO;
return pokemonEntity.getRiding().angRollVel(pokemonEntity, player, deltaTime);
}
@Unique
private Vec3 cobblemon$getRideMouseRotation(double mouseX, double mouseY, double deltaTime) {
var player = minecraft.player;
if (player == null) return Vec3.ZERO;
var vehicle = player.getVehicle();
if (vehicle == null) return Vec3.ZERO;
if (!(vehicle instanceof PokemonEntity pokemon)) return Vec3.ZERO;
var sensitivity = cobblemon$getRidingSensitivity();
return pokemon.getRiding().rotationOnMouseXY(
pokemon,
player,
mouseY,
mouseX,
yMouseSmoother,
xMouseSmoother,
sensitivity,
deltaTime
);
}
@Unique
private double cobblemon$getRidingSensitivity() {
var sensitivity = this.minecraft.options.sensitivity().get() * 0.6000000238418579 + 0.20000000298023224;
return Math.pow(sensitivity, 3);
}
@Unique
private boolean cobblemon$shouldUseAngVelSmoothing() {
var player = minecraft.player;
if (player == null) return true;
var playerVehicle = player.getVehicle();
if (playerVehicle == null) return true;
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return true;
return pokemonEntity.getRiding().useAngVelSmoothing(pokemonEntity);
}
}
@@ -60,8 +60,10 @@ object CobblemonCommands {
RenameBoxCommand.register(dispatcher)
ChangeBoxWallpaperCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
SpectateBattleCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
UnlockPCBoxWallpaperCommand.register(dispatcher)
ChangePCBoxesCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.client.net.gui.PokedexUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.npc.CloseNPCEditorHandler
import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
import com.cobblemon.mod.common.client.net.orientation.S2CUpdateOrientationHandler
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
@@ -73,6 +74,7 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityP
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.orientation.S2CUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
@@ -387,6 +389,9 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(PokemonPasturedPacket.ID, PokemonPasturedPacket::decode, PokemonPasturedHandler))
list.add(PacketRegisterInfo(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler))
// Orientation
list.add(PacketRegisterInfo(S2CUpdateOrientationPacket.ID, S2CUpdateOrientationPacket::decode, S2CUpdateOrientationHandler))
// Behaviours
list.add(PacketRegisterInfo(PlayPosableAnimationPacket.ID, PlayPosableAnimationPacket::decode, PlayPosableAnimationHandler))
@@ -71,6 +71,8 @@ import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.pokemon.riding.controllers.BirdAirController
import com.cobblemon.mod.common.pokemon.riding.states.BirdAirState
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import java.util.UUID
@@ -786,6 +788,7 @@ object MoLangFunctions {
map.put("in_battle") { DoubleValue(pokemonEntity.isBattling) }
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
map.put("is_gliding") { DoubleValue(pokemonEntity.useRidingAltPose()) }
map.put("has_aspect") { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
map.put("is_pokemon") { DoubleValue.ONE }
map.put("is_holding_item") { DoubleValue(!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).isEmpty) }
@@ -0,0 +1,71 @@
package com.cobblemon.mod.common.api.orientation
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.util.Mth
import net.minecraft.world.entity.LivingEntity
import org.joml.Matrix3f
import org.joml.Vector3f
import kotlin.math.asin
open class OrientationController(val entity: LivingEntity) {
companion object {
val FORWARDS: Vector3f = Vector3f(0.0f, 0.0f, -1.0f)
val UP: Vector3f = Vector3f(0.0f, 1.0f, 0.0f)
val LEFT: Vector3f = Vector3f(-1.0f, 0.0f, 0.0f)
}
open var active: Boolean = false
var orientation: Matrix3f? = null
private set
/** Adding this simply because it irritates me seeing `getActive` when most of this is Java. */
fun isActive() = active
fun updateOrientation(fn: (Matrix3f) -> Matrix3f?) {
if (!active) return
if (orientation == null) {
orientation = Matrix3f()
rotate(entity.yRot - 180, entity.xRot, 0f)
}
orientation = fn(orientation!!)
entity.yRot = yaw
entity.xRot = pitch
}
fun reset() {
orientation = null
active = false
}
fun rotate(yaw: Float, pitch: Float, roll: Float) {
rotateYaw(yaw)
rotatePitch(pitch)
rotateRoll(roll)
}
val forwardVector: Vector3f
get() = orientation?.transform(FORWARDS, Vector3f()) ?: Vector3f(FORWARDS)
val leftVector: Vector3f
get() = orientation?.transform(LEFT, Vector3f()) ?: Vector3f(LEFT)
val upVector : Vector3f
get() = orientation?.transform(UP, Vector3f()) ?: Vector3f(UP)
fun rotateYaw(yaw: Float) = updateOrientation { it.rotateY(-yaw.toRadians()) }
fun rotatePitch(pitch: Float) = updateOrientation { it.rotateX(-pitch.toRadians()) }
fun rotateRoll(roll: Float) = updateOrientation { it.rotateZ(-roll.toRadians()) }
val yaw: Float
get() = Mth.wrapDegrees(-FORWARDS.angleSigned(forwardVector, UP).toDegrees() + 180)
val pitch: Float
get() = Mth.wrapDegrees(-asin(forwardVector.y).toDegrees())
val roll: Float
get() = Mth.wrapDegrees(-upVector.angleSigned(UP, forwardVector).toDegrees())
}
@@ -128,8 +128,12 @@ object CobblemonPermissions {
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val COBBLEMON_CONFIG_RELOAD = this.create("${COMMAND_PREFIX}cobblemonconfig.reload", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val CALCULATE_SEAT_POSITIONS = this.create("${COMMAND_PREFIX}calculateseatpositions", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val CHANGE_BOX_COUNT = this.create("${COMMAND_PREFIX}boxcount", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val SPECTATE_BATTLE = this.create("${COMMAND_PREFIX}spectatebattle", PermissionLevel.ALL_COMMANDS)
fun all(): Iterable<Permission> = this.permissions
@@ -50,6 +50,7 @@ import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.adapters.RideControllerAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.google.common.collect.HashBasedTable
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -211,6 +212,11 @@ object PokemonSpecies : JsonDataRegistry<Species> {
this.speciesByDex.remove(old.resourceIdentifier.namespace, old.nationalPokedexNumber)
}
this.speciesByDex.put(species.resourceIdentifier.namespace, species.nationalPokedexNumber, species)
ifClient {
if (species.implemented) {
this.implemented.add(species)
}
}
}
}
@@ -285,4 +291,4 @@ object PokemonSpecies : JsonDataRegistry<Species> {
return "${species.resourceIdentifier.namespace}:${species.name}"
}
}
}
@@ -10,23 +10,17 @@ package com.cobblemon.mod.common.api.riding
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.toProperties
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import kotlin.math.sin
import kotlin.math.sin
data class RidingManager(val entity: PokemonEntity) {
var lastSpeed = 0F
@@ -74,15 +68,11 @@ data class RidingManager(val entity: PokemonEntity) {
*/
fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val controller = getController(entity) ?: return
val pose = controller.pose(entity)
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
//val speedPlayer = driver.deltaMovement.horizontalDistance()
//driver.displayClientMessage(Component.literal("Speed player: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("$speedPlayer b/t")), true)
//val speedEntity = entity.deltaMovement.length() //* 20 * 60 * 60) / ( 1000 )
//println(speedEntity)
//driver.displayClientMessage(Component.literal("Speed: ").withStyle { it.withColor(ChatFormatting.GREEN) }.append(Component.literal("${String.format("%.2f", speedEntity)} km/h")), true)
controller.tick(entity, driver, input)
if (!entity.level().isClientSide) {
val pose = controller.pose(entity)
entity.entityData.set(PokemonEntity.POSE_TYPE, pose)
}
}
fun speed(entity: PokemonEntity, driver: Player): Float {
@@ -134,6 +124,11 @@ data class RidingManager(val entity: PokemonEntity) {
return controller.jumpForce(entity, driver, jumpStrength)
}
fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean {
val controller = getController(entity) ?: return false
return controller.useRidingAltPose(entity, driver)
}
/**
* Used to supply the delta for the lerp() between velocity
* vectors. Called inertia for now as the way it feels when
@@ -212,4 +207,4 @@ data class RidingManager(val entity: PokemonEntity) {
val controller = getController(entity) ?: return false
return controller.dismountOnShift(entity)
}
}
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.api.riding.controller
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingState
@@ -54,6 +54,8 @@ interface RideController : Encodable, Decodable {
*/
val condition: (PokemonEntity) -> Boolean
fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {}
/**
* Specifies the pose the ridden mount should be positioned in. This queries the pose provider using
* the entity as a means of determining the conditions the entity is currently under. This allows for
@@ -84,9 +86,7 @@ interface RideController : Encodable, Decodable {
fun clampPassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {
driver.yRot = entity.yRot
}
fun updatePassengerRotation(entity: PokemonEntity, driver: LivingEntity) {}
/**
* Manipulates the movement input as necessary by the controller. This is primarily used to apply limits
@@ -111,7 +111,9 @@ interface RideController : Encodable, Decodable {
fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
//If function is not overwritten by controllers then just perform the defualt
fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean = false
//If function is not overwritten by controllers then just perform the defauit
//rolling function which is roll on mousex and pitch on mousey
fun rotationOnMouseXY(
entity: PokemonEntity,
@@ -123,10 +125,9 @@ interface RideController : Encodable, Decodable {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is Rollable) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
//Might need to add the smoothing here for default.
val rollable = driver as Rollable
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
return Vec3(0.0, yMouse * invertPitch, xMouse * invertRoll)
@@ -148,4 +149,4 @@ interface RideController : Encodable, Decodable {
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeIdentifier(this.key)
}
}
}
@@ -137,6 +137,12 @@ open class PCStore(
pcChangeObservable.emit(Unit)
}
fun removeListOfBoxes(boxList: List<PCBox>,lockNewSize: Boolean = false, overflowHandler: (Pokemon) -> Unit = ::relocateEvictedBoxPokemon){
this.lockedSize = lockNewSize
boxes.removeAll(boxList)
boxList.flatMap { it.asIterable() }.forEach(overflowHandler)
pcChangeObservable.emit(Unit)
}
fun tryRestoreBackedUpPokemon() {
var newPosition = getFirstAvailablePosition()
@@ -13,7 +13,6 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.pokeball.PokeBalls
@@ -42,11 +41,8 @@ import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Vector3f
class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
companion object {
@@ -440,4 +436,4 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
}
cryAnimation = animation
}
}
}
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
import org.joml.Quaternionf
import org.joml.Vector3f
@@ -115,9 +116,10 @@ class ModelWidget(
heldItemRenderer.renderOnModel(
state.currentModel!!,
heldItem?: ItemStack.EMPTY,
state.locatorStates,
state,
matrices,
context.bufferSource()
context.bufferSource(),
Vec3(0.0,-90.0,-90.0)
)
matrices.popPose()
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.orientation
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.client.orientation.S2CUpdateOrientationPacket
import net.minecraft.client.Minecraft
object S2CUpdateOrientationHandler : ClientNetworkPacketHandler<S2CUpdateOrientationPacket> {
override fun handle(packet: S2CUpdateOrientationPacket, client: Minecraft) {
val level = client.level ?: return
val entity = level.getEntity(packet.entityId)
if (entity is OrientationControllable) {
entity.orientationController.updateOrientation { _ -> packet.orientation }
packet.active?.let {
entity.orientationController.active = it
}
}
}
}
@@ -11,24 +11,24 @@ package com.cobblemon.mod.common.client.render.item
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_FACE_ITEMS
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_HAT_ITEMS
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.models.blockbench.NullObjectParser
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemDisplayContext
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class HeldItemRenderer() {
private val itemRenderer = Minecraft.getInstance().itemRenderer
private var displayContext = ItemDisplayContext.FIXED
private var scale = 1.0f
class HeldItemRenderer {
private val itemRenderer: ItemRenderer = Minecraft.getInstance().itemRenderer
private var displayContext: ItemDisplayContext = ItemDisplayContext.FIXED
companion object {
const val ITEM_FACE = "item_face"
@@ -37,99 +37,130 @@ class HeldItemRenderer() {
}
fun render(
entity: PokemonEntity?,
entity: LivingEntity?,
model: PosableModel,
item: ItemStack,
locators: Map<String, MatrixWrapper>,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int,
seed: Int
seed: Int,
rotation: Vec3
) {
if (item.isEmpty) return
displayContext = ItemDisplayContext.FIXED
poseStack.pushPose()
when {
//item_face locator
(locators.containsKey(ITEM_FACE) && item.`is`(WEARABLE_FACE_ITEMS)) -> {
displayContext = model.getLocatorDisplayContext(ITEM_FACE) ?: ItemDisplayContext.HEAD
poseStack.mulPose(locators[ITEM_FACE]!!.matrix)
poseStack.translate(0f, 0f, .28f * scale)
poseStack.scale(0.7f * scale, 0.7f * scale, 0.7f * scale)
poseStack.translate(0f, 0f, .28f)
poseStack.scale(0.7f, 0.7f, 0.7f)
}
(locators.containsKey("item_hat") && item.`is`(WEARABLE_HAT_ITEMS)) -> {
//item_hat locator
(locators.containsKey(ITEM_HAT) && item.`is`(WEARABLE_HAT_ITEMS)) -> {
displayContext = model.getLocatorDisplayContext(ITEM_HAT) ?: ItemDisplayContext.HEAD
poseStack.mulPose(locators["item_hat"]!!.matrix)
poseStack.translate(0f, -0.26f * scale, 0f)
poseStack.scale(.68f * scale, .68f * scale, .68f * scale)
poseStack.mulPose(locators[ITEM_HAT]!!.matrix)
poseStack.translate(0f, -0.26f, 0f)
poseStack.scale(.68f, .68f, .68f)
}
(locators.containsKey("item")) -> {
updateModifiers("item",locators)
poseStack.mulPose(locators["item"]!!.matrix)
//item locator
(locators.containsKey(ITEM)) -> {
displayContext = model.getLocatorDisplayContext(ITEM) ?: ItemDisplayContext.FIXED
when (displayContext) {
ItemDisplayContext.FIXED -> {
poseStack.translate(0f, 0.01666f * scale, 0f)
poseStack.scale(.5f * scale, .5f * scale, .5f * scale)
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
}
ItemDisplayContext.THIRD_PERSON_RIGHT_HAND -> {
poseStack.translate(0.075f* scale, 0f * scale, -0.05f* scale)
poseStack.scale(scale, scale, scale)
poseStack.mulPose(Axis.XP.rotationDegrees(-90f))
poseStack.mulPose(Axis.YP.rotationDegrees(-90f))
}
else -> {
poseStack.scale(scale, scale, scale)
}
}
poseStack.mulPose(locators[ITEM]!!.matrix)
applyContextTransformation(poseStack, rotation)
}
else -> { // Don't render any item
// Don't render any item
else -> {
poseStack.popPose()
return
}
}
itemRenderer.renderStatic(entity, item, displayContext, false, poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, seed)
itemRenderer.renderStatic(entity, item, displayContext, (displayContext==ItemDisplayContext.THIRD_PERSON_LEFT_HAND), poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, seed)
poseStack.popPose()
}
private fun renderAnimationItem(
model: PosableModel,
state: PosableState,
locators: Map<String, MatrixWrapper>,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int,
entity: LivingEntity?,
rotation: Vec3
) {
state.itemRenderingLocations.forEach { (locatorName, item) ->
poseStack.pushPose()
if (locators.containsKey(locatorName)) {
displayContext = model.getLocatorDisplayContext(locatorName)?: ItemDisplayContext.FIXED
poseStack.mulPose(locators[locatorName]!!.matrix)
applyContextTransformation(poseStack, rotation)
}
itemRenderer.renderStatic(entity, item, displayContext, (displayContext==ItemDisplayContext.THIRD_PERSON_LEFT_HAND), poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, 0)
poseStack.popPose()
}
}
fun renderOnModel(
model: PosableModel,
item: ItemStack,
locators: Map<String, MatrixWrapper>,
state: PosableState,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int = LightTexture.pack(11, 7)
){
if (!item.`is`(CobblemonItemTags.HIDDEN_ITEMS)) render(null, model, item, locators, poseStack, buffer, light, 0)
rotation: Vec3 = Vec3(0.0,0.0,0.0),
light: Int = LightTexture.pack(11, 7),
) {
val locators: Map<String, MatrixWrapper> = state.locatorStates
if (state.itemRenderingLocations.isNotEmpty()) {
renderAnimationItem(model, state, locators, poseStack, buffer, light, null, rotation)
}
else if (!item.`is`(CobblemonItemTags.HIDDEN_ITEMS)) {
render(null, model, item, locators, poseStack, buffer, light, 0, rotation)
}
}
fun renderOnEntity(
entity: PokemonEntity,
delegate: PokemonClientDelegate,
entity: LivingEntity,
item: ItemStack,
model: PosableModel,
state: PosableState,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int
light: Int,
rotation: Vec3 = Vec3(0.0,0.0,0.0)
) {
val shownItem = entity.shownItem
val locators: Map<String, MatrixWrapper> = delegate.locatorStates
render(entity, model, shownItem, locators, poseStack, buffer, light, 0)
val locators: Map<String, MatrixWrapper> = state.locatorStates
if (state.itemRenderingLocations.isNotEmpty()) {
renderAnimationItem(model, state, locators, poseStack, buffer, light, entity, rotation)
}
else {
render(entity, model, item, locators, poseStack, buffer, light, 0, rotation)
}
}
private fun updateModifiers(name: String, locators: Map<String, MatrixWrapper>) {
locators.forEach { (locator: String, m: MatrixWrapper) ->
val modifiers: Map<String, Float>
if (locator.startsWith("_null_$name[")) {
modifiers = NullObjectParser.parseNullObject(locator).modifiers
if (modifiers.containsKey("scale")) scale = modifiers["scale"]!!
if (modifiers.containsKey("mode")) displayContext = ItemDisplayContext.entries[((modifiers["mode"]!!).toInt()) % 9]
return
private fun applyContextTransformation(poseStack: PoseStack, rotation: Vec3) {
when (displayContext) {
ItemDisplayContext.FIXED -> {
poseStack.mulPose(Axis.XP.rotationDegrees(90.0f))
poseStack.scale(.5f, .5f, .5f )
poseStack.translate(0f, 0.01666f, 0f)
}
ItemDisplayContext.THIRD_PERSON_RIGHT_HAND, ItemDisplayContext.THIRD_PERSON_LEFT_HAND -> {
poseStack.mulPose(Axis.XP.rotationDegrees((rotation.x).toFloat()))
poseStack.mulPose(Axis.YP.rotationDegrees((rotation.y).toFloat()))
poseStack.mulPose(Axis.ZP.rotationDegrees((rotation.z).toFloat()))
poseStack.translate(0.025f * if (displayContext==ItemDisplayContext.THIRD_PERSON_LEFT_HAND) 1 else -1 , 0.0f, 0.0f)
}
ItemDisplayContext.HEAD -> {
poseStack.mulPose(Axis.XP.rotationDegrees(90.0f))
poseStack.scale(.6f, .6f, .6f)
poseStack.translate(0f, -0.6f, 0f)
}
else -> {}
}
}
}
@@ -37,6 +37,7 @@ import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent<T, PlayerModel<T>>) : RenderLayer<T, PlayerModel<T>>(renderLayerParent) {
@@ -163,9 +164,10 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
heldItemRenderer.renderOnModel(
model,
shoulderData.shownItem,
state.locatorStates,
state,
matrixStack,
buffer
buffer,
Vec3(0.0,-90.0,-90.0)
)
model.setDefault()
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench
/**
* Uses the name of null objects in a model to get modifier values for a locator.
* @author Josh
*/
object NullObjectParser {
fun parseNullObject(input: String): NullObjectModifier {
// Extract modifiedLocator
val modifiedLocator = input.replaceFirst("^_null_".toRegex(), "").replace("\\[.*\\]$".toRegex(), "")
// Extract modifiers
val modifiers: MutableMap<String, Float> = HashMap()
val start = input.indexOf('[')
val end = input.indexOf(']')
if (start != -1 && end != -1 && end > start) {
val modifierPart = input.substring(start + 1, end)
val modifierPairs = modifierPart.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (pair in modifierPairs) {
val keyValue = pair.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (keyValue.size == 2) {
val key = keyValue[0].trim { it <= ' ' }
try {
val value = keyValue[1].trim { it <= ' ' }.toFloat()
modifiers[key] = value
} catch (e: NumberFormatException) {
System.err.println("Invalid number format for: " + keyValue[1])
}
}
}
}
return NullObjectModifier(modifiedLocator, modifiers)
}
/**
* Stores modifiers for a particular locator.
*/
class NullObjectModifier(val locatorName: String, val modifiers: Map<String, Float>)
}
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.client.render.models.blockbench
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
@@ -56,6 +56,7 @@ import net.minecraft.client.renderer.texture.DynamicTexture
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemDisplayContext
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
@@ -715,12 +716,13 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
var scale = 1F
// We could improve this to be generalized for other entities. First we'd have to figure out wtf is going on, though.
if (entity is PokemonEntity) {
if(entity.passengers.isNotEmpty() && entity.controllingPassenger is Rollable && (entity.controllingPassenger as Rollable).orientation != null){
if(entity.passengers.isNotEmpty() && entity.controllingPassenger is OrientationControllable
&& (entity.controllingPassenger as OrientationControllable).orientationController.orientation != null){
val controllingPassenger = entity.controllingPassenger
val transformationMatrix = Matrix4f()
val center = Vector3f(0f, entity.bbHeight/2, 0f)
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f((controllingPassenger as Rollable).orientation!!))
transformationMatrix.mul(Matrix4f((controllingPassenger as OrientationControllable).orientationController.orientation!!))
transformationMatrix.translate(center.negate())
matrixStack.mulPose(transformationMatrix)
} else {
@@ -817,4 +819,4 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
condition = condition,
animations = { animations(it) }
)
}
}
@@ -36,14 +36,18 @@ import com.cobblemon.mod.common.client.render.models.blockbench.quirk.QuirkData
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.mojang.brigadier.StringReader
import net.minecraft.client.Minecraft
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.commands.arguments.item.ItemParser
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
/**
@@ -124,6 +128,10 @@ abstract class PosableState : Schedulable {
/** This gets called 500 million times so use a mutable value for runtime */
private val reusableAnimTime = DoubleValue(0.0)
val itemRenderingLocations = mutableMapOf<String, ItemStack>()
// var itemRenderingLocation = ""
// var animationItem : ItemStack? = null
/** All of the MoLang functions that can be applied to something with this state. */
val functions = QueryStruct(hashMapOf())
.addFunction("anim_time") {
@@ -148,6 +156,24 @@ abstract class PosableState : Schedulable {
)
}
}
.addFunction("render_item") { params ->
if (params.get<MoValue>(0) !is StringValue) return@addFunction Unit
val client = Minecraft.getInstance().connection ?: return@addFunction Unit
val result = ItemParser(client.registryAccess()).parse(StringReader(params.getString(0)))
val item = ItemStack(result.item)
item.applyComponents(result.components)
val renderLocation = if (params.contains(1)) params.getString(1) else "item"
if (!item.isEmpty && locatorStates.containsKey(renderLocation)) {
itemRenderingLocations.put(renderLocation, item)
} else {
if (itemRenderingLocations.containsKey(renderLocation)) itemRenderingLocations.remove(renderLocation)
return@addFunction Unit
}
}
.addFunction("clear_items") { itemRenderingLocations.clear() }
.addFunction("play_animation") { params ->
val animationParameter = params.get<MoValue>(0)
val animation = if (animationParameter is ObjectValue<*>) {
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.render.npc
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.PosableEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.npc.PosableNPCModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
@@ -19,6 +20,7 @@ import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.entity.EntityRendererProvider.Context
import net.minecraft.client.renderer.entity.LivingEntityRenderer
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
import kotlin.math.min
class NPCRenderer(context: Context) : LivingEntityRenderer<NPCEntity, PosableEntityModel<NPCEntity>>(context, PosableNPCModel(), 0.5f) {
@@ -26,6 +28,8 @@ class NPCRenderer(context: Context) : LivingEntityRenderer<NPCEntity, PosableEnt
return VaryingModelRepository.getTexture(entity.npc.resourceIdentifier, (entity.delegate as NPCClientDelegate))
}
private val heldItemRenderer = HeldItemRenderer()
override fun render(
entity: NPCEntity,
entityYaw: Float,
@@ -56,6 +60,20 @@ class NPCRenderer(context: Context) : LivingEntityRenderer<NPCEntity, PosableEnt
model.blue = 1F
model.resetLayerContext()
if (entity.deathTime < 1){
//Render Held Item
heldItemRenderer.renderOnEntity(
entity,
entity.mainHandItem,
model,
clientDelegate,
poseMatrix,
buffer,
packedLight,
Vec3(-90.0,0.0,0.0)
)
}
// if (this.shouldRenderLabel(entity)) {
// this.renderLabelIfPresent(entity, entity.displayName, poseMatrix, buffer, packedLight)
// }
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.client.render.player
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.Rideable
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
@@ -20,6 +20,7 @@ import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.player.AbstractClientPlayer
import net.minecraft.util.Mth
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Matrix4f
import org.joml.Vector3f
@@ -48,10 +49,6 @@ object MountedPlayerRenderer {
val locatorOffsetToCenter = locatorOffset.sub(center, Vector3f())
val transformationMatrix = Matrix4f()
if (player is Rollable && player.shouldRoll()){
//transformationMatrix.rotate(Axis.YP.rotationDegrees(-yBodyRot))
//transformationMatrix.mulLocal(player.orientation)
}
val rotatedOffset = transformationMatrix.transformPosition(locatorOffsetToCenter, Vector3f()).add(center).sub(Vector3f(0f, player.bbHeight/2, 0f))
matrix.translate(rotatedOffset)
@@ -73,12 +70,14 @@ object MountedPlayerRenderer {
}
//Rotates player
if (player is Rollable && player.shouldRoll() && !disableRollableRenderDebug) {
val controller = (player as? OrientationControllable)?.orientationController ?: return
if (controller.active && !disableRollableRenderDebug) {
val center = Vector3f(0f, player.bbHeight / 2, 0f)
val transformationMatrix = Matrix4f()
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f(player.orientation))
val orientation = controller.orientation ?: Matrix3f()
transformationMatrix.mul(Matrix4f(orientation))
transformationMatrix.translate(center.negate(Vector3f()))
//Pre-Undo Yaw
@@ -131,4 +130,4 @@ object MountedPlayerRenderer {
)
}
}
}
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.client.render.pokemon
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.ClientBallDisplay
@@ -101,7 +101,7 @@ class PokemonRenderer(
packedLight: Int
) {
val clientDelegate = entity.delegate as PokemonClientDelegate
shadowRadius = min((entity.boundingBox.maxX - entity.boundingBox.minX), (entity.boundingBox.maxZ) - (entity.boundingBox.minZ)).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).entityScaleModifier
shadowRadius = (min((entity.boundingBox.maxX - entity.boundingBox.minX), (entity.boundingBox.maxZ) - (entity.boundingBox.minZ)).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).entityScaleModifier)/entity.scale
model.posableModel = VaryingModelRepository.getPoser(entity.pokemon.species.resourceIdentifier, clientDelegate)
model.posableModel.context = model.context
model.setupEntityTypeContext(entity)
@@ -160,11 +160,13 @@ class PokemonRenderer(
//Render Held Item
heldItemRenderer.renderOnEntity(
entity,
clientDelegate,
entity.shownItem,
model.posableModel,
clientDelegate,
poseMatrix,
buffer,
packedLight
packedLight,
Vec3(0.0,-90.0,90.0)
)
}
@@ -176,18 +178,20 @@ class PokemonRenderer(
packedLight: Int
) {
val driver = entity.firstPassenger ?: return
val rollable = driver as? Rollable ?: return
val rollable = driver as? OrientationControllable ?: return
val controller = rollable.orientationController
poseMatrix.pushPose()
if(!DISABLE_ROLLING_DEBUG && rollable.shouldRoll()){
if(!DISABLE_ROLLING_DEBUG && controller.active){
val matrix = poseMatrix.last().pose()
val yaw = Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot)
val center = Vector3f(0f, entity.bbHeight/2, 0f)
val transformationMatrix = Matrix4f()
//Move origin to center of Pokemon
transformationMatrix.translate(center)
transformationMatrix.mul(Matrix4f(rollable.orientation))
val orientation = controller.orientation ?: Matrix3f()
transformationMatrix.mul(Matrix4f(orientation))
//Move origin to base of the entity
transformationMatrix.translate(center.negate(Vector3f()))
@@ -0,0 +1,138 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.storage.pc.PCBox
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.util.*
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.Commands.literal
import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.commands.arguments.selector.EntitySelector
import net.minecraft.server.level.ServerPlayer
object ChangePCBoxesCommand {
private const val NAME = "boxcount"
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
dispatcher.register(literal(NAME)
.permission(CobblemonPermissions.CHANGE_BOX_COUNT)
.then(literal("query").then(
argument("player", EntityArgument.player()).executes(::executeQuery))
)
.then(literal("add").then(
argument("player", EntityArgument.player()).then(
argument("amount", IntegerArgumentType.integer(1, 1000)).executes(::executeAdd)))
)
.then(literal("remove").then(
argument("player", EntityArgument.player()).then(
argument("amount", IntegerArgumentType.integer(1, 1000)).executes(::executeRemove)))
)
.then(literal("set").then(
argument("player", EntityArgument.player()).then(
argument("amount", IntegerArgumentType.integer(1, 1000)).executes(::executeSet)))
)
)
}
private fun executeQuery(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
context.source.sendSystemMessage(lang("command.boxcount", player.name, playerPc.boxes.size))
return Command.SINGLE_SUCCESS
}
private fun executeAdd(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
val amount = context.getArgument("amount", Int::class.java)
playerPc.resize(playerPc.boxes.size + amount, true)
playerPc.sendTo(player)
context.source.sendSystemMessage(lang("command.changeboxcount", player.name, playerPc.boxes.size).green())
return Command.SINGLE_SUCCESS
}
private fun executeRemove(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
val amount = context.getArgument("amount", Int::class.java)
if (amount < playerPc.boxes.size) {
val emptyBoxes = playerPc.boxes.filter { it.getNonEmptySlots().isEmpty() }
if (emptyBoxes.size >= amount) {
remove(context,player, playerPc, amount, emptyBoxes)
return Command.SINGLE_SUCCESS
}
else {
context.source.sendSystemMessage(lang("command.changeboxcount.removing_not_empty_box").red().append("\n").append(
lang("command.changeboxcount.force_remove").gray().onClick(true){remove(context,player, playerPc, amount, emptyBoxes)}
))
return 0
}
}
else {
context.source.sendSystemMessage(lang("command.changeboxcount.removing_too_many_boxes", player.name, playerPc.boxes.size).red())
return 0
}
}
private fun executeSet(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
val amount = context.getArgument("amount", Int::class.java)
if (amount < playerPc.boxes.size) {
val emptyBoxes = playerPc.boxes.filter { it.getNonEmptySlots().isEmpty() }
val deleteAmount = playerPc.boxes.size - amount
if (emptyBoxes.size <= deleteAmount) {
context.source.sendSystemMessage(lang("command.changeboxcount.removing_not_empty_box").red().append("\n").append(
lang("command.changeboxcount.force_remove").gray().onClick(true){ remove(context,player, playerPc, deleteAmount, emptyBoxes) }
))
return 0
}
else {
remove(context,player, playerPc, deleteAmount, emptyBoxes)
return Command.SINGLE_SUCCESS
}
}
set(context,player, playerPc, amount)
return Command.SINGLE_SUCCESS
}
private fun set(context: CommandContext<CommandSourceStack>,player: ServerPlayer, playerPc: PCStore, amount: Int){
playerPc.resize(amount, true)
playerPc.sendTo(player)
context.source.sendSystemMessage(lang("command.changeboxcount", player.name, playerPc.boxes.size).green())
}
private fun remove(context: CommandContext<CommandSourceStack>,player: ServerPlayer, playerPc: PCStore, amount: Int,emptyBoxes: List<PCBox>){
if (amount <= emptyBoxes.size) {
playerPc.removeListOfBoxes(emptyBoxes.takeLast(amount),true)
}
else {
val slicedBoxes = emptyBoxes + playerPc.boxes.takeLast(amount - emptyBoxes.size)
playerPc.removeListOfBoxes(slicedBoxes,true)
}
playerPc.initialize()
playerPc.sendTo(player)
context.source.sendSystemMessage(lang("command.changeboxcount", player.name, playerPc.boxes.size).green())
}
}
@@ -0,0 +1,41 @@
/*
* 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.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.Commands.literal
import net.minecraft.commands.arguments.EntityArgument
object SpectateBattleCommand {
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
val command = literal("spectateBattle")
.permission(CobblemonPermissions.SPECTATE_BATTLE)
.then(argument("player", EntityArgument.player())
.executes(::execute))
dispatcher.register(command)
}
private fun execute(context: CommandContext<CommandSourceStack>): Int {
val player = context.source.playerOrException
val target = EntityArgument.getPlayer(context, "player")
SpectateBattleHandler.spectateBattle(target, player)
return Command.SINGLE_SUCCESS
}
}
@@ -60,7 +60,6 @@ import java.util.*
object CobblemonDataProvider : DataProvider {
// Both Forge n Fabric keep insertion order so if a registry depends on another simply register it after
internal var canReload = true
// Both Forge n Fabric keep insertion order so if a registry depends on another simply register it after
private val registries = linkedSetOf<DataRegistry>()
@@ -557,10 +557,10 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
private fun pokemonExpressions(pokemon: Pokemon): List<String> {
return listOf(
"v.actioning_pokemon_name=\"${pokemon.species.name}\";",
"v.actioning_pokemon_name=\'${pokemon.species.name}\';",
"v.actioning_pokemon_level=${pokemon.level};",
"v.actioning_pokemon_ball=\"${pokemon.caughtBall.name}\";",
"v.actioning_pokemon_shiny=\"${pokemon.shiny}\";",
"v.actioning_pokemon_ball=\'${pokemon.caughtBall.name}\';",
"v.actioning_pokemon_shiny=\'${pokemon.shiny}\';",
)
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.entity.pokemon
import com.cobblemon.mod.common.*
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.entity.Despawner
import com.cobblemon.mod.common.api.entity.PokemonSender
@@ -305,6 +304,11 @@ open class PokemonEntity(
.addPokemonEntityFunctions(this)
}
var flyDistO = 0F
var isPokemonWalking = false
var isPokemonFlying = false
init {
delegate.initialize(this)
delegate.changePokemon(pokemon)
@@ -394,6 +398,8 @@ open class PokemonEntity(
// return super.canWalkOnFluid(state)
}
override fun canSprint() = true
override fun handleEntityEvent(status: Byte) {
delegate.handleStatus(status)
super.handleEntityEvent(status)
@@ -418,7 +424,13 @@ open class PokemonEntity(
yHeadRotO = Mth.wrapDegrees(yHeadRotO)
/* I'm sure it's not even us but something altering the logic of the loops in LivingEntity */
isPokemonFlying = flyDist - flyDistO > 0.005F
isPokemonWalking = walkDist - walkDistO > 0.005F
val isMoving = entityData.get(MOVING)
super.tick()
flyDistO = flyDist
if (isBattling) {
// Deploy a platform if a non-wild Pokemon is touching water but not underwater.
@@ -959,6 +971,7 @@ open class PokemonEntity(
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.scaleModifier)
var result = this.exposedForm.hitbox.scale(scale)
result = result.withEyeHeight(this.exposedForm.eyeHeight(this) * result.height)
result = result.scale(this.scale)
return result
}
@@ -1310,7 +1323,7 @@ open class PokemonEntity(
}
} else if (form.behaviour.moving.swim.canBreatheUnderwater && !form.behaviour.moving.swim.canWalkOnWater) {
// Use half hitbox height for swimmers
val halfHeight = form.hitbox.height * form.baseScale / 2.0
val halfHeight = getDimensions(this.pose).height / 2.0
for (i in 1..halfHeight.toInt()) {
blockPos = blockPos.below()
if (!this.level().isWaterAt(blockPos) || !this.level().getBlockState(blockPos).getCollisionShape(this.level(), blockPos).isEmpty) {
@@ -1770,7 +1783,7 @@ open class PokemonEntity(
val center = Vector3f(0f, this.bbHeight/2, 0f)
val seatToCenter = center.sub(seatOffset, Vector3f())
val matrix = (this.passengers.first() as Rollable).orientation ?: Matrix3f().rotate((180f-passenger.yRot).toRadians(), Vector3f(0f, 1f, 0f))
val matrix = (this.passengers.first() as? OrientationControllable)?.orientationController?.orientation ?: Matrix3f().rotate((180f-passenger.yRot).toRadians(), Vector3f(0f, 1f, 0f))
val rotatedOffset = matrix.transform(seatToCenter, Vector3f()).add(center).sub(Vector3f(0f, passenger.bbHeight/2, 0f))
positionUpdater.accept(passenger, this.x + rotatedOffset.x, this.y + rotatedOffset.y, this.z + rotatedOffset.z)
@@ -1812,6 +1825,11 @@ open class PokemonEntity(
return this.riding.speed(this, controller)
}
fun useRidingAltPose(): Boolean {
val driver = this.controllingPassenger as? Player ?: return false
return this.riding.useRidingAltPose(this, driver)
}
var jumpInputStrength: Int = 0 // move this
override fun onPlayerJump(strength: Int) {
// See if this controls the hot bar element
@@ -1926,4 +1944,4 @@ open class PokemonEntity(
override fun resolveEntityScan(): LivingEntity {
return this
}
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.entity.pokemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.entity.PokemonSender
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
@@ -23,23 +22,19 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ActivePokemonState
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
import com.cobblemon.mod.common.util.getIsSubmerged
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.update
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.util.Optional
import net.minecraft.world.entity.Entity
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.level.ServerLevel
import net.minecraft.network.chat.Component
import net.minecraft.network.syncher.EntityDataAccessor
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.pathfinder.PathType
import org.joml.Matrix3f
import org.joml.Vector3f
import java.util.*
/** Handles purely server logic for a Pokémon */
class PokemonServerDelegate : PokemonSideDelegate {
@@ -119,7 +114,14 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
entity.entityData.set(PokemonEntity.ASPECTS, trackedAspects)
entity.entityData.set(PokemonEntity.LABEL_LEVEL, entity.pokemon.level)
entity.entityData.set(PokemonEntity.MOVING, entity.platform == PlatformType.NONE && entity.deltaMovement.multiply(1.0, if (entity.onGround()) 0.0 else 1.0, 1.0).length() > 0.005F)
if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.entityData.set(PokemonEntity.MOVING, entity.isPokemonFlying)
}
else {
entity.entityData.set(PokemonEntity.MOVING, entity.isPokemonWalking)
}
entity.entityData.set(PokemonEntity.FRIENDSHIP, entity.pokemon.friendship)
entity.entityData.set(PokemonEntity.CAUGHT_BALL, trackedBall)
@@ -288,4 +290,4 @@ class PokemonServerDelegate : PokemonSideDelegate {
entity.remove(Entity.RemovalReason.KILLED)
}
}
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.orientation
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readMatrix3f
import com.cobblemon.mod.common.util.writeMatrix3f
import net.minecraft.network.RegistryFriendlyByteBuf
import org.joml.Matrix3f
/**
* Packet sent from the server to update the current orientation of the
* given entity for the remote entity receiving this packet.
*
* @author Jackowes
* @since March 30th, 2025
*/
class S2CUpdateOrientationPacket internal constructor(val orientation: Matrix3f?, val active: Boolean?, val entityId: Int) : NetworkPacket<S2CUpdateOrientationPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVarInt(entityId)
buffer.writeBoolean(orientation != null)
if (orientation != null && active != null) {
buffer.writeMatrix3f(orientation)
buffer.writeBoolean(active)
}
}
companion object {
val ID = cobblemonResource("s2c_update_orientation")
fun decode(buffer: RegistryFriendlyByteBuf): S2CUpdateOrientationPacket {
val entityId = buffer.readVarInt()
val valid = buffer.readBoolean()
val orientation = if (valid) buffer.readMatrix3f() else null
val active = if (valid) buffer.readBoolean() else null
return S2CUpdateOrientationPacket(orientation, active, entityId)
}
}
}
@@ -26,7 +26,7 @@ abstract class SpawnExtraDataEntityPacket<T: NetworkPacket<T>, E : Entity>(var v
abstract fun encodeEntityData(buffer: RegistryFriendlyByteBuf)
abstract fun applyData(entity: E)
abstract fun applyData(entity: E, level: ClientLevel)
abstract fun checkType(entity: Entity): Boolean
@@ -48,7 +48,7 @@ abstract class SpawnExtraDataEntityPacket<T: NetworkPacket<T>, E : Entity>(var v
)
// Cobblemon start
if (this.checkType(entity)) {
this.applyData(entity as E)
this.applyData(entity as E, world)
}
// Cobblemon end
world.addEntity(entity)
@@ -59,4 +59,4 @@ abstract class SpawnExtraDataEntityPacket<T: NetworkPacket<T>, E : Entity>(var v
companion object {
fun decodeVanillaPacket(buffer: RegistryFriendlyByteBuf) = ClientboundAddEntityPacket(buffer)
}
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.*
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
import net.minecraft.resources.ResourceLocation
@@ -36,7 +37,7 @@ class SpawnGenericBedrockPacket(
) : SpawnExtraDataEntityPacket<SpawnGenericBedrockPacket, GenericBedrockEntity>(vanillaSpawnPacket) {
override val id: ResourceLocation = ID
override fun applyData(entity: GenericBedrockEntity) {
override fun applyData(entity: GenericBedrockEntity, level: ClientLevel) {
entity.category = this.category
entity.aspects = this.aspects
entity.entityData.set(GenericBedrockEntity.POSE_TYPE, this.poseType)
@@ -74,4 +75,4 @@ class SpawnGenericBedrockPacket(
return SpawnGenericBedrockPacket(category, aspects, poseType, scale, width, height, startAge, vanillaPacket)
}
}
}
}
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.util.writeEnumConstant
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeString
import com.cobblemon.mod.common.util.writeText
import net.minecraft.client.multiplayer.ClientLevel
import java.util.UUID
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
@@ -69,7 +70,7 @@ class SpawnNPCPacket(
buffer.writeBoolean(this.hideNameTag)
}
override fun applyData(entity: NPCEntity) {
override fun applyData(entity: NPCEntity, level: ClientLevel) {
entity.npc = NPCClasses.getByIdentifier(this.npcClass) ?: error("received unknown NPCClass: $npcClass")
entity.customName = name
entity.entityData.set(NPCEntity.LEVEL, this.level)
@@ -104,4 +105,4 @@ class SpawnNPCPacket(
}
}
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.*
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket
import net.minecraft.resources.ResourceLocation
@@ -30,7 +31,7 @@ class SpawnPokeballPacket(
buffer.writeCollection(aspects) { _, aspect -> buffer.writeString(aspect)}
}
override fun applyData(entity: EmptyPokeBallEntity) {
override fun applyData(entity: EmptyPokeBallEntity, level: ClientLevel) {
entity.pokeBall = this.pokeBall
entity.aspects = this.aspects
}
@@ -47,4 +48,4 @@ class SpawnPokeballPacket(
return SpawnPokeballPacket(pokeBall, aspects, vanillaPacket)
}
}
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.*
import net.minecraft.client.multiplayer.ClientLevel
import java.util.UUID
import net.minecraft.world.entity.Entity
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -44,7 +45,8 @@ class SpawnPokemonPacket(
var spawnYaw: Float,
var friendship: Int,
var freezeFrame: Float,
vanillaSpawnPacket: ClientboundAddEntityPacket
var passengers: IntArray,
vanillaSpawnPacket: ClientboundAddEntityPacket,
) : SpawnExtraDataEntityPacket<SpawnPokemonPacket, PokemonEntity>(vanillaSpawnPacket) {
override val id: ResourceLocation = ID
@@ -70,6 +72,7 @@ class SpawnPokemonPacket(
entity.entityData.get(PokemonEntity.SPAWN_DIRECTION),
entity.entityData.get(PokemonEntity.FRIENDSHIP),
entity.entityData.get(PokemonEntity.FREEZE_FRAME),
entity.passengers.map { it.id }.toIntArray(),
vanillaSpawnPacket
)
@@ -94,9 +97,10 @@ class SpawnPokemonPacket(
buffer.writeFloat(this.spawnYaw)
buffer.writeInt(this.friendship)
buffer.writeFloat(this.freezeFrame)
buffer.writeVarIntArray(this.passengers)
}
override fun applyData(entity: PokemonEntity) {
override fun applyData(entity: PokemonEntity, level: ClientLevel) {
entity.ownerUUID = ownerId
entity.pokemon.apply {
scaleModifier = this@SpawnPokemonPacket.scaleModifier
@@ -121,6 +125,12 @@ class SpawnPokemonPacket(
entity.entityData.set(PokemonEntity.SPAWN_DIRECTION, spawnYaw)
entity.entityData.set(PokemonEntity.FRIENDSHIP, friendship)
entity.entityData.set(PokemonEntity.FREEZE_FRAME, freezeFrame)
entity.ejectPassengers()
passengers.forEach {
val passenger = level.getEntity(it) ?: return@forEach
passenger.startRiding(entity)
}
}
override fun checkType(entity: Entity): Boolean = entity is PokemonEntity
@@ -148,10 +158,11 @@ class SpawnPokemonPacket(
val spawnAngle = buffer.readFloat()
val friendship = buffer.readInt()
val freezeFrame = buffer.readFloat()
val passengers = buffer.readVarIntArray()
val vanillaPacket = decodeVanillaPacket(buffer)
return SpawnPokemonPacket(ownerId, scaleModifier, speciesId, gender, shiny, formName, aspects, battleId, phasingTargetId, beamModeEmitter, platform, nickname, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, friendship, freezeFrame, vanillaPacket)
return SpawnPokemonPacket(ownerId, scaleModifier, speciesId, gender, shiny, formName, aspects, battleId, phasingTargetId, beamModeEmitter, platform, nickname, labelLevel, poseType, unbattlable, hideLabel, caughtBall, spawnAngle, friendship, freezeFrame, passengers, vanillaPacket)
}
}
}
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.net.serverhandling.battle
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
@@ -18,9 +19,7 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleInitializePacke
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.traceFirstEntityCollision
import com.cobblemon.mod.common.util.*
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
@@ -48,9 +47,10 @@ object SpectateBattleHandler : ServerNetworkPacketHandler<SpectateBattlePacket>
player.sendSystemMessage(lang("ui.interact.failed").yellow())
return
}
battle.spectators.add(player.uuid)
player.sendPacket(BattleInitializePacket(battle, null))
player.sendPacket(BattleMessagePacket(battle.chatLog))
this.spectateBattle(targetedPlayerEntity, player)
// Handle music
target?.battleTheme?.let { player.sendPacket(BattleMusicPacket(it)) }
}
else {
@@ -58,4 +58,27 @@ object SpectateBattleHandler : ServerNetworkPacketHandler<SpectateBattlePacket>
}
}
fun spectateBattle(target: ServerPlayer, player: ServerPlayer) {
if (player == target) {
player.sendSystemMessage(lang("command.spectatebattle.self_spectate_disallowed").red())
return
}
if (!target.isInBattle()) {
player.sendSystemMessage(lang("command.spectatebattle.player_not_in_battle").red())
return
}
if (player.isInBattle()) {
player.sendSystemMessage(lang("command.spectatebattle.while_battling_disallowed").red())
return
}
val battle = BattleRegistry.getBattleByParticipatingPlayer(target) ?: return
battle.spectators.add(player.uuid)
player.sendPacket(BattleInitializePacket(battle, null))
player.sendPacket(BattleMessagePacket(battle.chatLog))
}
}
@@ -8,16 +8,25 @@
package com.cobblemon.mod.common.net.serverhandling.orientation
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.server.orientation.C2SUpdateOrientationPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import kotlin.math.atan2
object OrientationPacketHandler : ServerNetworkPacketHandler<C2SUpdateOrientationPacket> {
override fun handle(packet: C2SUpdateOrientationPacket, server: MinecraftServer, player: ServerPlayer) {
if (player is Rollable) {
player.updateOrientation { _ -> packet.orientation }
if (player is OrientationControllable) {
player.orientationController.updateOrientation { _ -> packet.orientation }
println("Packet Received! Roll ${"%.2f".format(player.orientationController.roll)} degrees")
// val orientation = packet.orientation
// val roll = orientation?.let { atan2(it.m10, it.m00) } ?: 0f
// val rollDegrees = Math.toDegrees(roll.toDouble())
// println("Packet Received! Roll: ${"%.2f".format(rollDegrees)} degrees")
}
}
}
@@ -9,9 +9,9 @@
package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
@@ -30,83 +30,37 @@ import kotlin.math.*
class BirdAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) {
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
it.deltaMovement.length() > 0.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 attempting to move forward
})
*/
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
private set
var gravity: Expression = "0".asExpression()
var topSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
private set
var horizontalAcceleration: Expression = "0.1".asExpression()
var glideTopSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
private set
var verticalAcceleration: Expression = "0.1".asExpression()
// Max accel is a whole 1.0 in 3 seconds. The conversion in the function below is to convert seconds to ticks
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
private set
//Seconds self propelled flight (glide is not self propelled in this case)
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
private set
//max y level for the ride
var altitudeExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
private set
// Where 90 is the width of the range and 45 is the offset.
// So the handling interpolates between 135 and 45
var handlingExpr: Expression = "(q.get_ride_stats('SKILL', 'AIR') / 100) * 90 + 45".asExpression()
var infiniteStamina: Expression = "false".asExpression()
private set
var topSpeedExpr: Expression = "(q.get_ride_stats('SPEED', 'AIR') / 100) * (1.0 - 0.35) + 0.35".asExpression()
private set
//Reaches max top speed from a standstill at minimum 8 seconds and max 3 seconds.
//This is a bit of a confusing expression and likely should be cleaned up to be more readable
var accelExpr: Expression = "(q.get_ride_stats('ACCELERATION', 'AIR') / 100) * (0.01666666666 - 0.00625) + 0.00625".asExpression()
var infiniteAltitude: Expression = "false".asExpression()
private set
override fun speed(entity: PokemonEntity, driver: Player): Float {
//Retrieve the current bird controller state
//val state = entity.riding.getState(KEY) { e -> BirdAirControllerState(e) }
//Retrieve the current bird controller state and stats
val state = getState(entity, ::BirdAirState)
//retrieve stats
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val accel = getRuntime(entity).resolveDouble(accelExpr)
val maxGlideSpeed = topSpeed * 2
var glideSpeedChange = 0.00
//speed up and slow down based on input
if (driver.zza > 0.0 && state.currSpeed < topSpeed) {
state.currSpeed = min(state.currSpeed + accel , topSpeed)
} else if (driver.zza < 0.0 && state.currSpeed > 0.0) {
//Decelerate is currently always a constant half of max acceleration.
state.currSpeed = max(state.currSpeed - (0.0166 / 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?
state.currSpeed = max(state.currSpeed + (0.0166) * 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.
state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
}
}
//air resistance
state.currSpeed = max(state.currSpeed - 0.005, 0.0)
return state.currSpeed.toFloat()
return state.rideVel.length().toFloat()
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
@@ -118,62 +72,60 @@ class BirdAirController : RideController {
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
val rollable = driver as? Rollable
if (rollable != null) {
//Need to deadzone this when straight up or down
// test with and without strafing added. May be wanted while moving
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)
}
*/
//Retrieve the current composite controller state
val state = getState(entity, ::BirdAirState)
val velocity = Vec3(0.0 , upForce * state.currSpeed, forwardForce * state.currSpeed)
//Perform ride velocity update
calculateRideSpaceVel(entity, driver, state)
//Translate ride space velocity to world space velocity.
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
//Need to deadzone this when straight up or down
upForce += -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
forwardForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.y
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.y
}
//Bring the ride out of the sky when stamina is depleted.
if (state.stamina <= 0.0) {
upForce -= 0.3
}
val altitudeLimit = getRuntime(entity).resolveDouble(altitudeExpr)
//Only limit altitude if altitude is not infinite
if (!getRuntime(entity).resolveBoolean(infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (entity.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(state.rideVel.x , upForce, forwardForce)
return velocity
}
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
val state = getState(entity, ::BirdAirState)
//TODO: Tie in handling
val handling = getRuntime(entity).resolveDouble(handlingExpr)
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
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())))
if (controller != null) {
var yawForce = rotationChangeRate * sin(Math.toRadians(controller.roll.toDouble()))
//for a bit of correction on the rolls influence pitch as well
var pitchForce = -0.35 * rotationChangeRate * abs(sin(Math.toRadians(controller.roll.toDouble())))
//limit rotation modulation when pitched up heavily or pitched down heavily
yawForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble())))
pitchForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble()))) * 1.5
yawForce *= abs(cos(Math.toRadians(controller.pitch.toDouble())))
pitchForce *= abs(cos(Math.toRadians(controller.pitch.toDouble()))) * 1.5
//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)
}
@@ -190,43 +142,57 @@ class BirdAirController : RideController {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is Rollable) return Vec3.ZERO
val rollable = driver as Rollable
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val state = getState(entity, ::BirdAirState)
val handling = getRuntime(entity).resolveDouble(handlingExpr)
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
//Smooth out mouse input.
val smoothingSpeed = 4
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
var xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val YInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1 * invertPitch, deltaTime * smoothingSpeed);
val xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1 * invertPitch, deltaTime * smoothingSpeed);
//limit rolling based on healing
val rotLimit = handling
//limit rolling based on handling and current speed.
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
val rotMin = 15.0
var rollForce = xInput
val rotLimit = max(handling * normalizeVal(state.rideVel.length(), 0.0, topSpeed).pow(3), rotMin)
//Limit roll by non linearly decreasing inputs towards
// a rotation limit based on the current distance from
// that rotation limit
if (abs(rollable.roll + xInput) < rotLimit) {
if (sign(xInput) == sign(rollable.roll).toDouble()) {
val d = abs(abs(rollable.roll) - rotLimit)
xInput *= (d.pow(2)) / (rotLimit.pow(2))
if (abs(controller.roll + rollForce) < rotLimit) {
if (sign(rollForce) == sign(controller.roll).toDouble()) {
val d = abs(abs(controller.roll) - rotLimit)
rollForce *= (d.pow(2)) / (rotLimit.pow(2))
}
} else if (sign(xInput) == sign(rollable.roll).toDouble()) {
xInput = 0.0
} else if (sign(rollForce) == sign(controller.roll).toDouble()) {
rollForce = 0.0
}
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * ( 1.0 - normalizeVal(state.rideVel.length(), 0.0, topSpeed).pow(3))
//yaw, pitch, roll
return Vec3(0.0, YInput, xInput)
return Vec3(yawForce, yInput, rollForce)
}
override fun canJump(
entity: PokemonEntity,
driver: Player
): Boolean {
return true
return false
}
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
//Retrieve stamina from state
val state = getState(entity, ::BirdAirState)
return (state.stamina / 1.0f)
}
override fun jumpForce(
@@ -243,29 +209,188 @@ class BirdAirController : RideController {
override fun shouldRoll(entity: PokemonEntity) = true
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val glideTopSpeed = getRuntime(entity).resolveDouble(glideTopSpeedExpr)
val state = getState(entity, ::BirdAirState)
//Must I ensure that topspeed is greater than minimum?
val normalizedGlideSpeed = normalizeVal(state.rideVel.length(), topSpeed, glideTopSpeed)
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
}
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
override fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean {
val state = getState(entity, ::BirdAirState)
return state.gliding
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(gravity.toString())
buffer.writeString(horizontalAcceleration.toString())
buffer.writeString(verticalAcceleration.toString())
buffer.writeString(topSpeedExpr.toString())
buffer.writeString(accelExpr.toString())
buffer.writeString(handlingExpr.toString())
buffer.writeExpression(topSpeedExpr)
buffer.writeExpression(glideTopSpeedExpr)
buffer.writeExpression(accelExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(altitudeExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(infiniteAltitude)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readString().asExpression()
horizontalAcceleration = buffer.readString().asExpression()
verticalAcceleration = buffer.readString().asExpression()
topSpeedExpr = buffer.readString().asExpression()
accelExpr = buffer.readString().asExpression()
handlingExpr = buffer.readString().asExpression()
topSpeedExpr = buffer.readExpression()
glideTopSpeedExpr = buffer.readExpression()
accelExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
altitudeExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
infiniteAltitude = buffer.readExpression()
}
companion object {
val KEY = cobblemonResource("air/bird")
}
}
/*
* Normalizes the given value between a min and a max.
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
* and 1.0 represents x is at or above it.
*/
fun normalizeVal(x: Double, min: Double, max: Double): Double {
require(max > min) { "max must be greater than min" }
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
}
/*
* Calculates the change in the ride space vector due to player input and ride state
*/
fun calculateRideSpaceVel(entity: PokemonEntity, driver: Player, state: BirdAirState) {
//retrieve stats
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val glideTopSpeed = getRuntime(entity).resolveDouble(glideTopSpeedExpr)
val accel = getRuntime(entity).resolveDouble(accelExpr)
val staminaStat = getRuntime(entity).resolveDouble(staminaExpr)
var glideSpeedChange = 0.0
val currSpeed = state.rideVel.length()
//Flag for determining if player is actively inputting
var activeInput = false
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina > 0.0) {
//make sure it can't exceed top speed
val forwardInput = when {
driver.zza > 0 && state.rideVel.z > topSpeed -> 0.0
driver.zza < 0 && state.rideVel.z < (-topSpeed / 3.0) -> 0.0
else -> driver.zza.sign
}
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
(state.rideVel.z + (accel * forwardInput.toDouble())))
activeInput = true
}
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
//Base glide speed change on current pitch of the ride.
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
glideSpeedChange = glideSpeedChange.pow(3) * 0.5
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
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?
//state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
lerp( state.rideVel.z, 0.0,glideSpeedChange * -0.0166 * 2 ))
}
} else {
// only add to the speed if it hasn't exceeded the current
//glide angles maximum amount of speed that it can give.
//state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
min(state.rideVel.z + ((0.0166 * 2) * glideSpeedChange), glideTopSpeed))
}
}
//Lateral movement based on driver input.
val latTopSpeed = topSpeed / 2.0
if (driver.xxa != 0.0f && state.stamina > 0.0) {
state.rideVel = Vec3(
(state.rideVel.x + (accel * driver.xxa)).coerceIn(-latTopSpeed, latTopSpeed),
state.rideVel.y,
state.rideVel.z)
activeInput = true
}
else {
state.rideVel = Vec3(
lerp(state.rideVel.x, 0.0, latTopSpeed / 20.0),
state.rideVel.y,
state.rideVel.z)
}
//Vertical movement based on driver input.
val vertTopSpeed = topSpeed / 2.0
val vertInput = when {
driver.jumping -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
}
if (vertInput != 0.0 && state.stamina > 0.0) {
state.rideVel = Vec3(
state.rideVel.x,
(state.rideVel.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
state.rideVel.z)
activeInput = true
}
else {
state.rideVel = Vec3(
state.rideVel.x,
lerp(state.rideVel.y, 0.0, vertTopSpeed / 20.0),
state.rideVel.z)
}
//Check if the ride should be gliding
if (activeInput && state.stamina > 0.0) {
state.gliding = false
}else {
state.gliding = true
}
//Only perform stamina logic if the ride does not have infinite stamina
if (!getRuntime(entity).resolveBoolean(infiniteStamina)) {
if (activeInput) {
state.stamina -= (0.05 / staminaStat).toFloat()
}
//Lose a base amount of stamina just for being airborne
state.stamina -= (0.01 / staminaStat).toFloat()
}
else
{
state.stamina = 1.0f
}
//air resistance
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
lerp( state.rideVel.z,0.0, topSpeed / ( 20.0 * 30.0)))
}
}
@@ -9,13 +9,12 @@
package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
@@ -28,7 +27,6 @@ import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
class DolphinController : RideController {
companion object {
@@ -54,7 +52,8 @@ class DolphinController : RideController {
override val key: ResourceLocation = KEY
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { it.deltaMovement.length() >= 0.05 })
.with(PoseOption(PoseType.SWIM) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { entity ->
//If there are only fluid blocks or air block below the ride
@@ -110,12 +109,12 @@ class DolphinController : RideController {
//Not sure if this is desired? Will need to mess around with this
//a bit more
var zComp = 0.0
val rollable = driver as? Rollable
if (rollable != null)
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null)
{
//Can be used again if I better figure out how to deadzone it near the tops?
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(rollable.roll.toDouble()))
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(rollable.pitch.toDouble()))
zComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.roll.toDouble()))
yComp = -1.0 * g.toDouble() * sin(Math.toRadians(controller.pitch.toDouble()))
}
val currVelocity = Vec3(0.0, yComp , g.toDouble())
@@ -130,17 +129,17 @@ class DolphinController : RideController {
return Vec3(0.0, 0.0, 0.0)
}
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//this should be changed to be speed maybe?
val movingForce = driver.zza
if (rollable != null) {
var yawAngVel = 3 * sin(Math.toRadians(rollable.roll.toDouble())).toFloat()
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(rollable.roll.toDouble())).toFloat())
if (controller != null) {
var yawAngVel = 3 * sin(Math.toRadians(controller.roll.toDouble())).toFloat()
var pitchAngVel = -1 * Math.abs(sin(Math.toRadians(controller.roll.toDouble())).toFloat())
//limit rotation modulation when pitched up heavily or pitched down heavily
yawAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
pitchAngVel *= (abs(cos(Math.toRadians(rollable.pitch.toDouble())))).toFloat()
yawAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
pitchAngVel *= (abs(cos(Math.toRadians(controller.pitch.toDouble())))).toFloat()
//if you are not pressing forward then don't turn
//yawAngVel *= movingForce
//Ignore x,y,z its angular velocity:
@@ -199,4 +198,4 @@ class DolphinController : RideController {
val z = buffer.readDouble()
this.lastVelocity = Vec3(x, y, z)
}
}
}
@@ -45,6 +45,18 @@ class FallToFlightCompositeController : RideController {
var flightController: RideController = GliderAirController()
private set
override fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val state = getState(entity, ::CompositeState)
val shouldBeFlying = checkShouldBeFlying(entity, state.activeController == flightController)
if (state.activeController == flightController && !shouldBeFlying) { // && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
} else if (state.activeController == landController && shouldBeFlying) {
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
}
}
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
@@ -77,20 +89,7 @@ class FallToFlightCompositeController : RideController {
}
}
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::CompositeState)
val shouldBeFlying = checkShouldBeFlying(entity, state.activeController == flightController)
if (state.activeController == flightController && !shouldBeFlying) { // && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
} else if (state.activeController == landController && shouldBeFlying) {
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).speed(entity, driver)
}
@@ -149,4 +148,4 @@ class FallToFlightCompositeController : RideController {
companion object {
val KEY = cobblemonResource("composite/fall_to_flight")
}
}
}
@@ -10,16 +10,13 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.states.GenericLandState
import com.cobblemon.mod.common.pokemon.riding.states.JetAirState
import com.cobblemon.mod.common.util.*
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -28,7 +25,9 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
class GenericLandController : RideController {
companion object {
@@ -66,8 +65,11 @@ class GenericLandController : RideController {
@Transient
override val key: ResourceLocation = KEY
@Transient
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.deltaMovement.horizontalDistance() > 0.1 })
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
@Transient
override val condition: (PokemonEntity) -> Boolean = { entity ->
// Are there any blocks under the mon that aren't air or fluid
@@ -115,7 +117,7 @@ class GenericLandController : RideController {
calculateRideSpaceVel(entity, driver, state)
//Jump the thang!
if (Minecraft.getInstance().options.keyJump.isDown() && entity.onGround()) {
if (driver.jumping && entity.onGround()) {
state.rideVel = Vec3(state.rideVel.x, 2.0, state.rideVel.z)
}
@@ -245,4 +247,4 @@ class GenericLandController : RideController {
}
}
}
}
@@ -51,8 +51,10 @@ class GenericLiquidController : RideController {
private set
override val key: ResourceLocation = KEY
override val poseProvider: PoseProvider = PoseProvider(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { it.isSwimming && it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { entity ->
//This could be kinda weird... what if the top of the mon is in a fluid but the bottom isnt?
Shapes.create(entity.boundingBox).blockPositionsAsListRounded().any {
@@ -120,4 +122,4 @@ class GenericLiquidController : RideController {
this.reverseDriveFactor = buffer.readString().asExpression()
this.strafeFactor = buffer.readString().asExpression()
}
}
}
@@ -16,14 +16,7 @@ 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 com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.LivingEntity
@@ -34,7 +27,7 @@ import net.minecraft.world.phys.Vec3
class GliderAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.horizontalDistance() > 0.1 })
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
var glideSpeed: Expression = "0.1".asExpression()
@@ -72,16 +65,16 @@ class GliderAirController : RideController {
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())
buffer.writeExpression(glideSpeed)
buffer.writeExpression(speed)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
glideSpeed = buffer.readString().asExpression()
speed = buffer.readString().asExpression()
glideSpeed = buffer.readExpression()
speed = buffer.readExpression()
}
companion object {
val KEY: ResourceLocation = cobblemonResource("air/glider")
}
}
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
@@ -38,7 +38,7 @@ import kotlin.math.sin
class HelicopterAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.horizontalDistance() > 0.1 })
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
//If there are only fluid blocks or air block below the ride
//then activate the controller. If it is in water the ride will
@@ -87,12 +87,12 @@ class HelicopterAirController : RideController {
var xVel = 0.0
var zVel = 0.0
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//Horizontal velocity is based on pitch and roll
if (rollable != null) {
xVel = -1.0 * sin(Math.toRadians(rollable.roll.toDouble()))
zVel = sin(Math.toRadians(rollable.pitch.toDouble()))
if (controller != null) {
xVel = -1.0 * sin(Math.toRadians(controller.roll.toDouble()))
zVel = sin(Math.toRadians(controller.pitch.toDouble()))
}
return Vec3(xVel, yVel, zVel)
@@ -101,7 +101,7 @@ class HelicopterAirController : RideController {
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//In degrees per second? It was supposed to be I think but
//I have messed something up
@@ -113,7 +113,7 @@ class HelicopterAirController : RideController {
//TODO: Fix accumulated movement causing pitch and roll out of set bounds
if (rollable != null) {
if (controller != null) {
//If the roll or pitch have exceeded the limit then do not modulate that
//rotation in that direction further
@@ -124,20 +124,20 @@ class HelicopterAirController : RideController {
//The three denotes that the force will be 1/3 what it would have been at
//the rotation limit
rollForce *= 2.0.pow(-1.0 * (abs(rollable.roll.toDouble()) / ROTATION_LIMIT))
rollForce *= 2.0.pow(-1.0 * (abs(controller.roll.toDouble()) / ROTATION_LIMIT))
pitchForce *= 2.0.pow(-1.0 * (abs(rollable.pitch.toDouble()) / ROTATION_LIMIT))
pitchForce *= 2.0.pow(-1.0 * (abs(controller.pitch.toDouble()) / ROTATION_LIMIT))
if (rollable.roll >= ROTATION_LIMIT && rollForce > 0.0) {
if (controller.roll >= ROTATION_LIMIT && rollForce > 0.0) {
rollForce = 0.0;
}
if (rollable.roll <= -ROTATION_LIMIT && rollForce < 0.0) {
if (controller.roll <= -ROTATION_LIMIT && rollForce < 0.0) {
rollForce = 0.0;
}
if (rollable.pitch >= ROTATION_LIMIT && pitchForce > 0.0) {
if (controller.pitch >= ROTATION_LIMIT && pitchForce > 0.0) {
pitchForce = 0.0;
}
if (rollable.pitch <= -ROTATION_LIMIT && pitchForce < 0.0) {
if (controller.pitch <= -ROTATION_LIMIT && pitchForce < 0.0) {
pitchForce = 0.0;
}
@@ -181,9 +181,9 @@ class HelicopterAirController : RideController {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is Rollable) return Vec3.ZERO
if (driver !is OrientationControllable) return Vec3.ZERO
val rollable = driver as Rollable
val controller = (driver as OrientationControllable).orientationController
val invertYaw = if (Cobblemon.config.invertYaw) -1 else 1
//yaw, pitch, roll
return Vec3(xMouse * invertYaw, 0.0, 0.0)
@@ -198,17 +198,17 @@ class HelicopterAirController : RideController {
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(gravity.toString())
buffer.writeString(horizontalAcceleration.toString())
buffer.writeString(verticalVelocity.toString())
buffer.writeString(speed.toString())
buffer.writeExpression(gravity)
buffer.writeExpression(horizontalAcceleration)
buffer.writeExpression(verticalVelocity)
buffer.writeExpression(speed)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readString().asExpression()
horizontalAcceleration = buffer.readString().asExpression()
verticalVelocity = buffer.readString().asExpression()
speed = buffer.readString().asExpression()
gravity = buffer.readExpression()
horizontalAcceleration = buffer.readExpression()
verticalVelocity = buffer.readExpression()
speed = buffer.readExpression()
}
@@ -216,4 +216,4 @@ class HelicopterAirController : RideController {
val KEY = cobblemonResource("air/helicopter")
val ROTATION_LIMIT = 30.0f
}
}
}
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.controller.RideController
import com.cobblemon.mod.common.api.riding.controller.posing.PoseOption
import com.cobblemon.mod.common.api.riding.controller.posing.PoseProvider
@@ -30,7 +30,7 @@ import kotlin.math.*
class JetAirController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { it.deltaMovement.length() > 0 })
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
override val condition: (PokemonEntity) -> Boolean = { true }
@@ -49,9 +49,9 @@ class JetAirController : RideController {
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
// Between 60 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 10.0)".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 300.0, 128.0)".asExpression()
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
// Make configurable by json
@@ -105,17 +105,17 @@ class JetAirController : RideController {
//Gather state information
val state = getState(entity, ::JetAirState)
val rollable = driver as? Rollable
val controller = (driver as? OrientationControllable)?.orientationController
//Calculate ride space velocity
calculateRideSpaceVel(entity, driver, state)
//Translate ride space velocity to world space velocity.
if( rollable != null ) {
if(controller != null) {
upForce = -1.0 * sin(Math.toRadians( rollable.pitch.toDouble() )) * state.rideVel.z
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
forwardForce = cos(Math.toRadians( rollable.pitch.toDouble() )) * state.rideVel.z
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.z
}
@@ -123,14 +123,12 @@ class JetAirController : RideController {
upForce = if(state.stamina > 0.0) upForce else -0.7
val altitudeLimit = getRuntime(entity).resolveDouble(jumpExpr)
val pushingHeightLimit = if(getRuntime(entity).resolveBoolean(infiniteAltitude)) false
else (entity.y >= 128 && entity.xRot <= 0)
//Incorporate the altitude limiter.
//Apply downwards force if limit is reached. Resist upwards force on a buffer of 30 blocks past the limit.
upForce = if(pushingHeightLimit) upForce - upForce * ((entity.y - altitudeLimit) / 30) - 0.5
else upForce
//Only limit altitude if altitude is not infinite
if (!getRuntime(entity).resolveBoolean(infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (entity.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(0.0 , upForce, forwardForce )
@@ -171,7 +169,7 @@ class JetAirController : RideController {
sensitivity: Double,
deltaTime: Double ): Vec3
{
if(driver !is Rollable) return Vec3.ZERO
if(driver !is OrientationControllable) return Vec3.ZERO
//TODO: figure out a cleaner solution to this issue of large jumps when skipping frames or lagging
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
val cappedDeltaTime = min( deltaTime, 0.2)
@@ -256,29 +254,29 @@ class JetAirController : RideController {
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(gravity.toString())
buffer.writeString(minSpeed.toString())
buffer.writeString(handlingExpr.toString())
buffer.writeString(handlingYawExpr.toString())
buffer.writeString(topSpeedExpr.toString())
buffer.writeString(accelExpr.toString())
buffer.writeString(jumpExpr.toString())
buffer.writeString(staminaExpr.toString())
buffer.writeString(infiniteStamina.toString())
buffer.writeString(infiniteAltitude.toString())
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(topSpeedExpr)
buffer.writeExpression(accelExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(infiniteAltitude)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readString().asExpression()
minSpeed = buffer.readString().asExpression()
handlingExpr = buffer.readString().asExpression()
handlingYawExpr = buffer.readString().asExpression()
topSpeedExpr = buffer.readString().asExpression()
accelExpr = buffer.readString().asExpression()
jumpExpr = buffer.readString().asExpression()
staminaExpr = buffer.readString().asExpression()
infiniteStamina = buffer.readString().asExpression()
infiniteAltitude = buffer.readString().asExpression()
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
handlingExpr = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
topSpeedExpr = buffer.readExpression()
accelExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
infiniteAltitude = buffer.readExpression()
}
companion object {
@@ -46,42 +46,67 @@ class RunUpToFlightCompositeController : RideController {
var flightController: RideController = BirdAirController()
private set
private fun shouldTransitionToGround(state: RunUpToFlightCompositeState, entity: PokemonEntity): Boolean {
return state.activeController == flightController
&& entity.onGround()
&& state.timeTransitioned + 20 < entity.level().gameTime
}
private fun shouldTransitionToAir(state: RunUpToFlightCompositeState, entity: PokemonEntity, driver: Player): Boolean {
return state.activeController == landController
&& driver.jumping
&& state.timeTransitioned + 20 < entity.level().gameTime
}
override fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val state = getState(entity, ::RunUpToFlightCompositeState)
val flightState = flightController.getState(entity, ::BirdAirState)
val groundState = landController.getState(entity, ::GenericLandState)
if (shouldTransitionToGround(state, entity)) {
//Pass the speed to the next state
groundState.currSpeed = flightState.currSpeed
groundState.rideVel = flightState.rideVel
groundState.stamina = flightState.stamina
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
state.timeTransitioned = entity.level().gameTime
}
else if (shouldTransitionToAir(state, entity, driver)) {
//Pass the speed to the next state
flightState.currSpeed = groundState.currSpeed
flightState.rideVel = groundState.rideVel
flightState.stamina = groundState.stamina
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
state.timeTransitioned = entity.level().gameTime
}
}
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
fun getActiveController(entity: PokemonEntity): RideController {
val state = getState(entity, ::RunUpToFlightCompositeState)
return state.activeController ?: let {
val controller = if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
flightController
} else {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
landController
}
state.activeController = controller
if (state.activeController != null) return state.activeController!!
if (entity.getCurrentPoseType() in PoseType.FLYING_POSES) {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
state.activeController = flightController
state.timeTransitioned = entity.level().gameTime
controller
return flightController
}
else {
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
state.activeController = landController
state.timeTransitioned = entity.level().gameTime
return landController
}
}
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::RunUpToFlightCompositeState)
if (state.activeController == flightController && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
//Pass the speed to the next state
val flightState = flightController.getState(entity, ::BirdAirState)
val groundState = landController.getState(entity, ::GenericLandState)
groundState.currSpeed = flightState.currSpeed
getState(entity, ::RunUpToFlightCompositeState).activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).speed(entity, driver)
}
@@ -101,6 +126,14 @@ class RunUpToFlightCompositeController : RideController {
return getActiveController(entity).angRollVel(entity, driver, deltaTime)
}
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).rideFovMult(entity, driver)
}
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).setRideBar(entity, driver)
}
override fun canJump(entity: PokemonEntity, driver: Player): Boolean {
return getActiveController(entity).canJump(entity, driver)
}
@@ -111,19 +144,6 @@ class RunUpToFlightCompositeController : RideController {
jumpStrength: Int
): Vec3 {
val controller = getActiveController(entity)
if (controller == landController && jumpStrength >= getRuntime(entity).resolveInt(minimumJump)) {
//Pass the speed to the next state
val flightState = flightController.getState(entity, ::BirdAirState )
val groundState = landController.getState(entity, ::GenericLandState)
flightState.currSpeed = groundState.currSpeed
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
return controller.jumpForce(entity, driver, jumpStrength)
}
@@ -139,6 +159,10 @@ class RunUpToFlightCompositeController : RideController {
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = getActiveController(entity).useAngVelSmoothing(entity)
override fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean {
return getActiveController(entity).useRidingAltPose(entity, driver)
}
override fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
@@ -187,4 +211,4 @@ class RunUpToFlightCompositeController : RideController {
companion object {
val KEY = cobblemonResource("composite/run_up_to_flight")
}
}
}
@@ -21,9 +21,7 @@ import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.writeString
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
@@ -34,7 +32,9 @@ import net.minecraft.world.phys.Vec3
class RunUpToJetFlightCompositeController : RideController {
override val key = KEY
override val poseProvider = PoseProvider(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
.with(PoseOption(PoseType.WALK) {
it.entityData.get(PokemonEntity.MOVING)
})
override val condition: (PokemonEntity) -> Boolean = { true }
var minimumSpeed: Expression = "0.5".asExpression()
@@ -47,6 +47,44 @@ class RunUpToJetFlightCompositeController : RideController {
var flightController: RideController = JetAirController()
private set
fun shouldTransitionToGround(state: RunUpToFlightCompositeState, entity: PokemonEntity, driver: Player): Boolean {
if (state.activeController != flightController) return false
if (state.timeTransitioned + 20 >= entity.level().gameTime) return false
return driver.isSprinting || entity.onGround()
}
fun shouldTransitionToAir(state: RunUpToFlightCompositeState, entity: PokemonEntity, driver: Player, input: Vec3): Boolean {
if (state.activeController != landController) return false
if (state.timeTransitioned + 20 >= entity.level().gameTime) return false
return driver.isSprinting && input.z > 0.5
}
//TODO: This composite controller needs to have the jet lift off at a certain speed.
//maybe just pressing shift or space? Unsure, will need to test to see what works best.
override fun tick(entity: PokemonEntity, driver: Player, input: Vec3) {
val state = getState(entity, ::RunUpToFlightCompositeState)
val flightState = flightController.getState(entity, ::JetAirState)
val groundState = landController.getState(entity, ::GenericLandState)
if (shouldTransitionToGround(state, entity, driver)) {
groundState.currSpeed = flightState.currSpeed
groundState.stamina = flightState.stamina
groundState.rideVel = flightState.rideVel
state.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
state.timeTransitioned = entity.level().gameTime
}
else if (shouldTransitionToAir(state, entity, driver, input)) {
flightState.currSpeed = groundState.currSpeed
flightState.stamina = groundState.stamina
flightState.rideVel = groundState.rideVel
state.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
state.timeTransitioned = entity.level().gameTime
}
}
override fun pose(entity: PokemonEntity): PoseType {
return getActiveController(entity).poseProvider.select(entity)
}
@@ -67,55 +105,7 @@ class RunUpToJetFlightCompositeController : RideController {
}
}
//TODO: This composite controller needs to have the jet lift off at a certain speed.
//maybe just pressing shift or space? Unsure, will need to test to see what works best.
override fun speed(
entity: PokemonEntity,
driver: Player
): Float {
val state = getState(entity, ::RunUpToFlightCompositeState)
if ((state.activeController == flightController && entity.onGround() &&
state.timeTransitioned + 20 < entity.level().gameTime) ||
(Minecraft.getInstance().options.keySprint.isDown && state.activeController == flightController &&
state.timeTransitioned + 20 < entity.level().gameTime)) {
//Pass the data to the next state
val flightState = flightController.getState(entity, ::JetAirState)
val groundState = landController.getState(entity, ::GenericLandState)
groundState.currSpeed = flightState.currSpeed
groundState.stamina = flightState.stamina
//Clear accumulated rotation input when transferring to ground input
flightState.currMouseXForce = 0.0
flightState.currMouseYForce = 0.0
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
//These conditions should also be more graceful. As it stands though it feels better than jump and then
//having to hit the ground
else if(
state.activeController == landController && entity.speed > 0.5 &&
Minecraft.getInstance().options.keySprint.isDown() &&
state.timeTransitioned + 20 < entity.level().gameTime
)
{
//Pass the data to the next state
val flightState = flightController.getState(entity, ::JetAirState)
val groundState = landController.getState(entity, ::GenericLandState)
flightState.currSpeed = groundState.currSpeed
flightState.stamina = groundState.stamina
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
override fun speed(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).speed(entity, driver)
}
@@ -82,7 +82,7 @@ class VehicleLandController : RideController {
@Transient
override val key: ResourceLocation = KEY
@Transient
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.deltaMovement.horizontalDistance() > 0.1 })
override val poseProvider: PoseProvider = PoseProvider(PoseType.STAND).with(PoseOption(PoseType.WALK) { it.entityData.get(PokemonEntity.MOVING) })
@Transient
override val condition: (PokemonEntity) -> Boolean = { entity ->
// Are there any blocks under the mon that aren't air or fluid
@@ -191,4 +191,4 @@ class VehicleLandController : RideController {
this.lookYawLimit = buffer.readString().asExpression()
}
}
}
@@ -14,5 +14,7 @@ import net.minecraft.world.phys.Vec3
data class BirdAirState(val entity: PokemonEntity) : RidingState {
var currSpeed: Double = 0.0
var lastVelocity: Vec3 = Vec3(0.0, 0.0, 0.0)
var stamina: Float = 1.0f
var rideVel: Vec3 = Vec3.ZERO
var gliding: Boolean = false
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.util
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.net.IntSize
@@ -95,6 +96,14 @@ fun ByteBuf.readString(): String {
return Utf8String.read(this, 32767)
}
fun ByteBuf.writeExpression(expression: Expression): ByteBuf {
return this.writeString(expression.getString())
}
fun ByteBuf.readExpression(): Expression {
return this.readString().asExpression()
}
fun <T> ByteBuf.readNullable(reader: (ByteBuf) -> T): T? {
val isPresent = this.readBoolean()
if (isPresent) {
@@ -124,8 +124,14 @@
},
"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]
}
},
@@ -556,105 +562,6 @@
}
}
},
"animation.trainer_generic.send_out": {
"animation_length": 3.625,
"bones": {
"torso": {
"rotation": {
"0.0": [1, 0, 0],
"0.9375": [1, 0, 0],
"1.375": [1, -7.5, 0],
"2.875": [1, -7.5, 0],
"3.375": [1, 0, 0],
"3.625": [1, 0, 0]
}
},
"head": {
"rotation": {
"0.0": [-1, 0, 0],
"0.9375": [-1, 0, 0],
"1.375": [-1, 7.5, 0],
"2.875": [-1, 7.5, 0],
"3.375": [-1, 0, 0],
"3.625": [-1, 0, 0]
}
},
"arm_ball": {
"rotation": {
"0.0": [3, 0, 3.5],
"0.5": [3, 0, 3.5],
"0.9375": {
"pre": [14.5, 2.5, -17.5],
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"1.125": {
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"1.375": [-91, 7.5, 0],
"2.875": [-91, 7.5, 0],
"3.1875": {
"pre": [5, 2.5, -0.5],
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"3.375": {
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"3.625": [3, 0, 3.5]
},
"position": {
"0.0": [0, 0.1, 0],
"3.625": [0, 0.1, 0]
}
},
"arm_left": {
"rotation": {
"0.0": [3, 0, -3.5],
"0.9375": [3, 0, -3.5],
"1.375": [13, 0, -6],
"2.875": [13, 0, -6],
"3.375": [3, 0, -3.5]
},
"position": [0, 0.1, 0]
},
"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]},
"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]}
}
},
"leg_right": {
"rotation": {
"0.9375": [0, 8, 0.5],
"1.375": [1, 10, 0.5],
"2.875": [1, 10, 0.5],
"3.375": [0, 8, 0.5]
},
"position": {
"0.9375": [-0.2, 0, 0],
"1.375": [-0.2, 0, -0.2],
"2.875": [-0.2, 0, -0.2],
"3.375": [-0.2, 0, 0]
}
},
"leg_left": {
"rotation": {
"0.9375": [0, -8, -0.5],
"1.375": [0, -10, -0.5],
"2.875": [0, -10, -0.5],
"3.375": [0, -8, -0.5]
},
"position": [0.2, 0, 0]
}
}
},
"animation.trainer_generic.command": {
"animation_length": 2,
"bones": {
@@ -996,6 +903,276 @@
"scale": 0
}
}
},
"animation.trainer_generic.return": {
"animation_length": 3.625,
"bones": {
"torso": {
"rotation": {
"0.0": [1, 0, 0],
"0.9375": [1, 0, 0],
"1.375": [1, -7.5, 0],
"2.875": [1, -7.5, 0],
"3.375": [1, 0, 0],
"3.625": [1, 0, 0]
}
},
"head": {
"rotation": {
"0.0": [-1, 0, 0],
"0.9375": [-1, 0, 0],
"1.375": [-1, 7.5, 0],
"2.875": [-1, 7.5, 0],
"3.375": [-1, 0, 0],
"3.625": [-1, 0, 0]
}
},
"arm_ball": {
"rotation": {
"0.0": [3, 0, 3.5],
"0.5": [3, 0, 3.5],
"0.9375": {
"pre": [14.5, 2.5, -17.5],
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"1.125": {
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"1.375": [-91, 7.5, 0],
"2.875": [-91, 7.5, 0],
"3.1875": {
"pre": [5, 2.5, -0.5],
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"3.375": {
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"3.625": [3, 0, 3.5]
},
"position": {
"0.0": [0, 0.1, 0],
"3.625": [0, 0.1, 0]
}
},
"arm_left": {
"rotation": {
"0.0": [3, 0, -3.5],
"0.9375": [3, 0, -3.5],
"1.375": [13, 0, -6],
"2.875": [13, 0, -6],
"3.375": [3, 0, -3.5]
},
"position": [0, 0.1, 0]
},
"pokeball": {
"scale": {
"0.9375": {
"pre": [0, 0, 0],
"post": [0.3, 0.3, 0.3]
},
"1.0": [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]
}
}
},
"leg_right": {
"rotation": {
"0.9375": [0, 8, 0.5],
"1.375": [1, 10, 0.5],
"2.875": [1, 10, 0.5],
"3.375": [0, 8, 0.5]
},
"position": {
"0.9375": [-0.2, 0, 0],
"1.375": [-0.2, 0, -0.2],
"2.875": [-0.2, 0, -0.2],
"3.375": [-0.2, 0, 0]
}
},
"leg_left": {
"rotation": {
"0.9375": [0, -8, -0.5],
"1.375": [0, -10, -0.5],
"2.875": [0, -10, -0.5],
"3.375": [0, -8, -0.5]
},
"position": [0.2, 0, 0]
}
},
"timeline": {
"1.0": "q.render_item(v.actioning_pokemon_ball, 'item_right');",
"3.3125": "q.clear_items();"
}
},
"animation.trainer_generic.send_out": {
"animation_length": 2.875,
"bones": {
"torso_crouch": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"arms": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"torso_item_right": {
"rotation": [0.5, 0, 0]
},
"arms_item_right": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"arm_item_right": {
"rotation": {
"0.9167": [1, 0, 2.5],
"1.4167": {
"pre": [-85, 10, -10],
"post": [-85, 10, -10],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-72.5, -30, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, 2.5]
},
"position": [0, 0.1, 0]
},
"torso_item_left": {
"rotation": [0.5, 0, 0]
},
"arms_item_left": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"arm_item_left": {
"rotation": {
"0.9167": [1, 0, -2.5],
"1.4167": {
"pre": [4, 0, 0],
"post": [4, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-8, 0, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, -2.5]
},
"position": [0, 0.1, 0]
},
"torso": {
"rotation": [0.5, 0, 0]
},
"head": {
"rotation": [-0.5, 0, 0]
},
"arm_right": {
"rotation": {
"0.9167": [1, 0, 2.5],
"1.4167": {
"pre": [-85, 10, -10],
"post": [-85, 10, -10],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-72.5, -30, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, 2.5]
},
"position": [0, 0.1, 0]
},
"arm_left": {
"rotation": {
"0.9167": [1, 0, -2.5],
"1.4167": {
"pre": [4, 0, 0],
"post": [4, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-8, 0, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, -2.5]
},
"position": [0, 0.1, 0]
},
"leg_right": {
"rotation": [0, 2, 0.5],
"position": [-0.05, 0, 0]
},
"leg_left": {
"rotation": [0, -2, -0.5],
"position": [0.05, 0, 0]
}
},
"timeline": {
"0.0": "q.render_item(v.actioning_pokemon_ball, 'item_right');",
"1.5833": "q.render_item('air', 'item_right');"
}
}
},
"geometry.animations": {
"texturewidth": 64,
"textureheight": 64
},
"geometry.unknown": {
"texturewidth": 16,
"textureheight": 16
}
}
@@ -81,6 +81,14 @@
{"origin": [-7, 12, -2], "size": [3, 12, 4], "inflate": 0.25, "uv": [40, 32]}
]
},
{
"name": "item_right",
"parent": "arm_right",
"pivot": [-5.5, 12, -2],
"locators": {
"item_right": [-5.5, 12, -2]
}
},
{
"name": "pokeball",
"parent": "arm_ball",
@@ -99,6 +107,14 @@
{"origin": [4, 12, -2], "size": [3, 12, 4], "inflate": 0.25, "uv": [48, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [5.5, 12, -2],
"locators": {
"item_left": [5.5, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -97,6 +97,14 @@
{"origin": [-7, 12, -2], "size": [3, 11, 4], "inflate": 0.25, "uv": [40, 31]}
]
},
{
"name": "item_right",
"parent": "arm_right",
"pivot": [-5.5, 12, -2],
"locators": {
"item_right": [-5.5, 12, -2]
}
},
{
"name": "arm_left",
"parent": "torso",
@@ -106,6 +114,14 @@
{"origin": [4, 12, -2], "size": [3, 11, 4], "inflate": 0.25, "uv": [46, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [5.5, 12, -2],
"locators": {
"item_left": [5.5, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -81,6 +81,14 @@
{"origin": [-8, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [40, 32]}
]
},
{
"name": "item_right",
"parent": "arm_right",
"pivot": [-6, 12, -2],
"locators": {
"item_right": [-6, 12, -2]
}
},
{
"name": "pokeball",
"parent": "arm_ball",
@@ -99,6 +107,14 @@
{"origin": [4, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [48, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [6, 12, -2],
"locators": {
"item_left": [6, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -79,29 +79,18 @@
"cubes": [
{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]},
{"origin": [-8, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [40, 32]}
]
},
{
"name": "pokeball",
"parent": "arm_ball",
"pivot": [-6, 12, 0],
"cubes": [
{"origin": [-8, 8, -2], "size": [4, 2, 4], "pivot": [-6, 10, 0], "rotation": [90, 0, 0], "uv": [0, 122]}
],
"locators": {
"beam": [-6, 8, 0]
}
},
{
"name": "pokeball_lid",
"parent": "pokeball",
"pivot": [-6, 10, 0],
"rotation": [90, 0, 0],
"cubes": [
{"origin": [-6.5, 9.5, -2.02], "size": [1, 1, 0], "uv": [0, 123]},
{"origin": [-7, 9, -2.01], "size": [2, 2, 0], "uv": [0, 124]},
{"origin": [-8, 10, -2], "size": [4, 2, 4], "uv": [0, 116]}
]
"name": "item_right",
"parent": "arm_right",
"pivot": [-6, 12, -2],
"locators": {
"item_right": [-6, 12, -2]
}
},
{
"name": "arm_left",
@@ -112,6 +101,14 @@
{"origin": [4, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [48, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [6, 12, -2],
"locators": {
"item_left": [6, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -3,11 +3,16 @@
"portraitTranslation": [0.0, 1.1, 0.0],
"profileScale": 1.0,
"profileTranslation": [0.0, -0.5, 0.0],
"properties": {
"item_display_context": "THIRD_PERSON_RIGHT_HAND",
"item_right_display_context": "THIRD_PERSON_RIGHT_HAND",
"item_left_display_context": "THIRD_PERSON_LEFT_HAND"
},
"animations": {
"win": "q.bedrock_primary('trainer_generic', 'win', q.curve('symmetrical_wide'))",
"lose": "q.bedrock_primary('trainer_generic', 'lose', q.curve('symmetrical_wide'))",
"send_out": "q.bedrock_primary('trainer_generic', 'send_out', q.curve('symmetrical'), q.exclude_labels('look'))",
"recall": "q.bedrock_primary('trainer_generic', 'send_out', q.curve('symmetrical_wide'), q.exclude_labels('look'))",
"recall": "q.bedrock_primary('trainer_generic', 'return', q.curve('symmetrical_wide'), q.exclude_labels('look'))",
"punch_left": "q.primary_animation(q.punch('head', 'body', 'arm_left', 'arm_right', false), q.curve('symmetrical_wide'), q.exclude_labels('look'))",
"punch_right": "q.primary_animation(q.punch('head', 'body', 'arm_right', 'arm_left', true), q.curve('symmetrical_wide'), q.exclude_labels('look'))"
},
@@ -14543,7 +14543,7 @@
"rotation": [0, -6, -15]
},
"wing_right_elbow": {
"rotation": [0, -6, -15],
"rotation": [0, "-6", -15],
"position": [0.45, 0.1, 0]
},
"wing_right_membrane": {
@@ -20,7 +20,7 @@
"parent": "charizard",
"pivot": [0, 0, -1],
"locators": {
"root": [0, 0, -1]
"root": [0, 0, 0]
}
},
{
@@ -28,7 +28,7 @@
"parent": "charizard",
"pivot": [0, 41, -1],
"locators": {
"top": [0, 41, -1]
"top": [0, 41, 0]
}
},
{
@@ -116,8 +116,7 @@
{
"name": "head_correction",
"parent": "head",
"pivot": [0, 43.5, -0.5],
"rotation": [-95, 0, 0]
"pivot": [0, 43.5, -0.5]
},
{
"name": "head_ai",
@@ -128,15 +127,20 @@
"name": "head_angle",
"parent": "head_ai",
"pivot": [0, 43.5, -0.5],
"rotation": [95, 0, 0],
"cubes": [
{"origin": [-3.5, 41.5, -2], "size": [7, 7, 5], "uv": [48, 0]},
{"origin": [-3.5, 41.5, -4], "size": [7, 4, 2], "uv": [95, 64]},
{"origin": [-2.5, 48, -2], "size": [5, 5, 2], "inflate": 0.02, "uv": [37, 31]},
{"origin": [-2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [-2, 52.51777, -2], "rotation": [45, 0, 15], "uv": [43, 42]},
{"origin": [2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [2, 52.51777, -2], "rotation": [45, 0, -15], "uv": [41, 42]}
{"origin": [-3.5, 41.5, -4], "size": [7, 4, 2], "uv": [95, 64]}
]
},
{
"name": "item_hat",
"parent": "head_angle",
"pivot": [0, 45, 3],
"rotation": [-90, 0, 0],
"locators": {
"item_hat": [0, 45, 3]
}
},
{
"name": "locator_head",
"parent": "head_angle",
@@ -146,59 +150,51 @@
"head": [0, 46.5, 3]
}
},
{
"name": "locator_mouth",
"parent": "head_angle",
"pivot": [0, 48.75, -3],
"rotation": [-90, 0, 0],
"locators": {
"mouth": [0, 48.75, -3]
}
},
{
"name": "locator_special_attack",
"parent": "head_angle",
"pivot": [0, 48.75, -3],
"rotation": [-90, 0, 0],
"locators": {
"special_attack": [0, 48.75, -3]
}
},
{
"name": "locator_jaw_upper",
"parent": "head_angle",
"pivot": [0, 51.75, -2.25],
"rotation": [-90, 0, 0],
"locators": {
"jaw1": [0, 51.75, -2.25]
}
},
{
"name": "brow",
"parent": "head_angle",
"pivot": [0, 48.275, 1.765],
"cubes": [
{"origin": [-2.5, 48.55, 1], "size": [5, 0, 1], "uv": [0, 14]},
{"origin": [-3.5, 47.5, 1.53], "size": [7, 1, 1], "inflate": 0.04, "uv": [0, 12]}
]
},
{
"name": "eye",
"parent": "head_angle",
"pivot": [0, -3.5, 1.5]
},
{
"name": "locator_face",
"parent": "eye",
"parent": "head_angle",
"pivot": [0, 53.75, -0.25],
"rotation": [-90, 0, 0],
"locators": {
"face": [0, 53.75, -0.25]
}
},
{
"name": "locator_item_face",
"parent": "head_angle",
"pivot": [0, 48.5, 0.5],
"rotation": [-90, 0, 0],
"locators": {
"item_face": [0, 48.5, 0.5]
}
},
{
"name": "locator_mouth",
"parent": "head_angle",
"pivot": [0, 45.75, -2],
"rotation": [-90, 0, 0],
"locators": {
"mouth": [0, 45.75, -2],
"special": [0, 45.75, -2]
}
},
{
"name": "eyes",
"parent": "head_angle",
"pivot": [0, -3.5, 1.5]
},
{
"name": "brow",
"parent": "eyes",
"pivot": [0, 48.275, 1.765],
"cubes": [
{"origin": [-2.5, 48.55, 1], "size": [5, 0, 1], "uv": [0, 14]},
{"origin": [-3.5, 47.5, 1.53], "size": [7, 1, 1], "inflate": 0.04, "uv": [0, 12]}
]
},
{
"name": "eye_right",
"parent": "eye",
"parent": "eyes",
"pivot": [-2.75, 47.75, 0.875],
"cubes": [
{"origin": [-3.5, 47.5, 0], "size": [2, 1, 2], "inflate": 0.01, "uv": [106, 36]},
@@ -226,7 +222,7 @@
},
{
"name": "eye_left",
"parent": "eye",
"parent": "eyes",
"pivot": [2.75, 47.75, 0.875],
"cubes": [
{"origin": [1.5, 47.5, 0], "size": [2, 1, 2], "inflate": 0.01, "uv": [44, 82]},
@@ -255,6 +251,91 @@
{"origin": [2.41, 46.4, 0.5], "size": [1, 1, 1], "inflate": 0.03, "uv": [39, 86]}
]
},
{
"name": "muzzle",
"parent": "head_angle",
"pivot": [-3, 48.5, -2],
"cubes": [
{"origin": [-2.5, 48, -2], "size": [5, 5, 2], "inflate": 0.02, "uv": [37, 31]},
{"origin": [-2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [-2, 52.51777, -2], "rotation": [45, 0, 15], "uv": [43, 42]},
{"origin": [2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [2, 52.51777, -2], "rotation": [45, 0, -15], "uv": [41, 42]}
]
},
{
"name": "locator_jaw_upper",
"parent": "muzzle",
"pivot": [0, 51.75, -2.25],
"rotation": [-90, 0, 0],
"locators": {
"jaw1": [0, 51.75, -2.25]
}
},
{
"name": "nose",
"parent": "muzzle",
"pivot": [0, 53, 0],
"rotation": [-40, 0, 0],
"cubes": [
{"origin": [-2.5, 51.36698, -1.67861], "size": [5, 2, 2], "inflate": 0.03, "uv": [48, 12]},
{"origin": [-2, 53.40698, -1.17861], "size": [4, 0, 1], "uv": [0, 15]}
]
},
{
"name": "jaw",
"parent": "head_angle",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-3.5, 45.5, -4], "size": [7, 3, 2], "inflate": 0.01, "uv": [76, 20]}
]
},
{
"name": "jaw2",
"parent": "jaw",
"pivot": [0, 48.5, -4],
"rotation": [-7.5, 0, 0],
"cubes": [
{"origin": [-2, 48.49, -4], "size": [4, 4, 2], "uv": [0, 51]},
{"origin": [-1.5, 48.5, -2], "size": [3, 4, 0], "uv": [90, 0]},
{"origin": [1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [1.5, 52.52782, -2], "rotation": [45, 0, -7.5], "uv": [37, 39]},
{"origin": [-1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [-1.5, 52.52782, -2], "rotation": [45, 0, 7.5], "uv": [37, 38]}
]
},
{
"name": "locator_jaw_lower",
"parent": "jaw2",
"pivot": [0, 51.75, -1.75],
"locators": {
"jaw2": {
"offset": [0, 51.75, -1.75],
"rotation": [-90, 0, 0]
}
}
},
{
"name": "tongue",
"parent": "jaw",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-1.5, 45.5, -2], "size": [3, 3, 0], "uv": [87, 29]}
]
},
{
"name": "tongue2",
"parent": "tongue",
"pivot": [0, 48.5, -2],
"cubes": [
{"origin": [-1, 48.5, -2], "size": [2, 2, 0], "inflate": 0.01, "uv": [46, 38]}
]
},
{
"name": "tongue3",
"parent": "tongue2",
"pivot": [0, 50.5, -2],
"cubes": [
{"origin": [-1, 50.5, -2], "size": [2, 1, 0], "uv": [37, 43]},
{"origin": [-0.5, 51.5, -2], "size": [1, 1, 0], "uv": [45, 43]}
]
},
{
"name": "horns",
"parent": "head_angle",
@@ -300,72 +381,6 @@
"horn2": [2.5, 39, 2]
}
},
{
"name": "nose",
"parent": "head_angle",
"pivot": [0, 53, 0],
"rotation": [-40, 0, 0],
"cubes": [
{"origin": [-2.5, 51.36698, -1.67861], "size": [5, 2, 2], "inflate": 0.03, "uv": [48, 12]},
{"origin": [-2, 53.40698, -1.17861], "size": [4, 0, 1], "uv": [0, 15]}
]
},
{
"name": "jaw",
"parent": "head_angle",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-3.5, 45.5, -4], "size": [7, 3, 2], "inflate": 0.01, "uv": [76, 20]}
]
},
{
"name": "jaw2",
"parent": "jaw",
"pivot": [0, 48.5, -4],
"rotation": [-7.5, 0, 0],
"cubes": [
{"origin": [-2, 48.49, -4], "size": [4, 4, 2], "uv": [0, 51]},
{"origin": [-1.5, 48.5, -1.99], "size": [3, 4, 0], "uv": [90, 0]},
{"origin": [1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [1.5, 52.52782, -2], "rotation": [45, 0, -7.5], "uv": [37, 39]},
{"origin": [-1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [-1.5, 52.52782, -2], "rotation": [45, 0, 7.5], "uv": [37, 38]}
]
},
{
"name": "locator_jaw_lower",
"parent": "jaw2",
"pivot": [0, 51.75, -1.75],
"locators": {
"jaw2": {
"offset": [0, 51.75, -1.75],
"rotation": [-90, 0, 0]
}
}
},
{
"name": "tongue",
"parent": "jaw",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-1.5, 45.5, -2], "size": [3, 3, 0], "uv": [87, 29]}
]
},
{
"name": "tongue2",
"parent": "tongue",
"pivot": [0, 48.5, -2],
"cubes": [
{"origin": [-1, 48.5, -2], "size": [2, 2, 0], "inflate": 0.01, "uv": [46, 38]}
]
},
{
"name": "tongue3",
"parent": "tongue2",
"pivot": [0, 50.5, -2],
"cubes": [
{"origin": [-1, 50.5, -2], "size": [2, 1, 0], "uv": [37, 43]},
{"origin": [-0.5, 51.5, -2], "size": [1, 1, 0], "uv": [45, 43]}
]
},
{
"name": "arms",
"parent": "torso2",
@@ -403,6 +418,14 @@
{"origin": [-21.5, 22, -1.5], "size": [3, 2, 3], "inflate": 0.1, "uv": [0, 58]}
]
},
{
"name": "locator_item",
"parent": "hand_right",
"pivot": [-21.6, 23, 0],
"locators": {
"item": [-21.6, 23, 0]
}
},
{
"name": "locator_hand_primary",
"parent": "hand_right",
@@ -913,6 +936,14 @@
{"origin": [0, 9.5, 58.5], "size": [0, 13, 7], "pivot": [0, 14.45, 62], "rotation": [0, 90, 0], "uv": [0, 92]}
]
},
{
"name": "locator_fire",
"parent": "fire2",
"pivot": [0, 11, 62],
"locators": {
"fire": [0, 11, 62]
}
},
{
"name": "legs",
"parent": "body",
@@ -945,9 +976,10 @@
{
"name": "locator_foot_primary",
"parent": "foot_right",
"pivot": [-9, 1.5, -7],
"pivot": [-9, 1.5, -5],
"rotation": [90, 0, 0],
"locators": {
"foot_primary": [-9, 1.5, -7]
"foot_primary": [-9, 1.5, -5]
}
},
{
@@ -1009,9 +1041,10 @@
{
"name": "locator_foot_secondary",
"parent": "foot_left",
"pivot": [9, 1.5, -7],
"pivot": [9, 1.5, -5],
"rotation": [90, 0, 0],
"locators": {
"foot_secondary": [9, 1.5, -7]
"foot_secondary": [9, 1.5, -5]
}
},
{
@@ -86,12 +86,22 @@
"fly": {
"poseTypes": ["FLY"],
"isBattle": false,
"condition": "!q.is_gliding",
"animations": ["q.look('head_ai')", "q.bedrock('charizard', 'air_fly')"],
"quirks": [
"q.bedrock_quirk('charizard', 'blink')",
"q.bedrock_quirk('charizard', 'air_fly_quirk')"
]
},
"glide": {
"poseTypes": ["FLY"],
"isBattle": false,
"condition": "q.is_gliding",
"animations": ["q.look('head_ai')", "q.bedrock('charizard', 'air_glide_TEST')"],
"quirks": [
"q.bedrock_quirk('charizard', 'blink')"
]
},
"surface_idle": {
"poseTypes": ["STAND", "FLOAT"],
"isTouchingWater": true,
@@ -1561,12 +1561,20 @@
"cobblemon.command.pokebox.last_pokemon": "Did not send that Player's last Pokémon to the PC.",
"cobblemon.command.pokemonrestart": "Cleared %1$s's party and PC.",
"cobblemon.command.freezepokemon.non_pokemon": "You are not looking at a Pokémon.",
"cobblemon.command.spectatebattle.self_spectate_disallowed": "You can't spectate yourself.",
"cobblemon.command.spectatebattle.while_battling_disallowed": "You can't spectate other people while battling.",
"cobblemon.command.spectatebattle.player_not_in_battle": "This player is not in a battle.",
"cobblemon.command.npcedit.non_npc": "You are not looking at a NPC.",
"cobblemon.command.cobblemon_config.reload": "Reloading Cobblemon settings.",
"cobblemon.command.unlockboxwallpaper.success": "%1$s can now use the %2$s wallpaper.",
"cobblemon.command.unlockboxwallpaper.already": "%1$s already has the %2$s wallpaper.",
"cobblemon.wallpaper_unlocked": "Wallpaper Unlocked",
"cobblemon.unknown_wallpaper": "???",
"cobblemon.command.changeboxcount": "%1$s now has %2$s PC boxes",
"cobblemon.command.changeboxcount.removing_not_empty_box": "A box you are trying to remove is not empty",
"cobblemon.command.boxcount": "%1$s currently has %2$s PC boxes",
"cobblemon.command.changeboxcount.removing_too_many_boxes": "You can't remove all the boxes. %1$s currently has %2$s PC boxes",
"cobblemon.command.changeboxcount.force_remove": "Click here to remove anyway",
"cobblemon.config.ui.search_configurations": "Search Configurations",
"cobblemon.config.ui.reset": "Reset",
@@ -97,12 +97,12 @@
"party": {
"type": "simple",
"pokemon": [
"spiritomb level=100 held_item=cobblemon:sitrus_berry ability=pressure nature=jolly moves=darkpulse,shadowball,willowisp,suckerpunch hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 special_attack_ev=252 speed_ev=6",
"porygonz level=100 held_item=cobblemon:life_orb ability=adaptability nature=timid moves=hyperbeam,shadowball,icebeam,thunderbolt hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"togekiss level=100 held_item=cobblemon:leftovers ability=serenegrace nature=timid moves=airslash,dazzlinggleam,aurasphere,thunderwave hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"lucario level=100 held_item=cobblemon:focus_sash ability=innerfocus nature=jolly moves=closecombat,meteormash,earthquake,extremespeed hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252",
"milotic level=100 held_item=cobblemon:flame_orb ability=marvelscale nature=bold moves=recover,mirrorcoat,icebeam,scald hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 defence_ev=252 special_attack_ev=6",
"garchomp level=100 held_item=cobblemon:yache_berry ability=roughskin nature=jolly moves=dragonclaw,earthquake,swordsdance,poisonjab hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252"
"spiritomb level=100 pokeball=master_ball held_item=cobblemon:sitrus_berry ability=pressure nature=jolly moves=darkpulse,shadowball,willowisp,suckerpunch hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 special_attack_ev=252 speed_ev=6",
"porygonz level=100 pokeball=great_ball held_item=cobblemon:life_orb ability=adaptability nature=timid moves=hyperbeam,shadowball,icebeam,thunderbolt hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"togekiss level=100 pokeball=ultra_ball held_item=cobblemon:leftovers ability=serenegrace nature=timid moves=airslash,dazzlinggleam,aurasphere,thunderwave hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"lucario level=100 pokeball=dive_ball held_item=cobblemon:focus_sash ability=innerfocus nature=jolly moves=closecombat,meteormash,earthquake,extremespeed hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252",
"milotic level=100 pokeball=love_ball held_item=cobblemon:flame_orb ability=marvelscale nature=bold moves=recover,mirrorcoat,icebeam,scald hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 defence_ev=252 special_attack_ev=6",
"garchomp level=100 pokeball=quick_ball held_item=cobblemon:yache_berry ability=roughskin nature=jolly moves=dragonclaw,earthquake,swordsdance,poisonjab hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252"
]
}
}
@@ -48,6 +48,8 @@
"PlayerAdvancementsMixin",
"PlayerMixin",
"PointedDripstoneBlockMixin",
"ServerPlayerMixin",
"ServerEntityMixin",
"ShearsDispenserBehaviorMixin",
"StructurePoolAccessor",
"StructurePoolGeneratorMixin",
@@ -56,8 +58,10 @@
"accessor.AccessorEntity",
"accessor.BiomeAccessor",
"accessor.BoatEntityAccessor",
"accessor.ChunkMapAccessor",
"accessor.EntryListWidgetAccessor",
"accessor.StatusEffectInstanceAccessor",
"accessor.TrackedEntityAccessor",
"entity.EntityMixin",
"entity.TameableEntityMixin",
"invoker.AdornRegisterInvoker",