forked from mirrors/cobblemon
Compare commits
44
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
679920b786 | ||
|
|
c8ec4e6f1f | ||
|
|
cbcc6abe40 | ||
|
|
64c9d47b0f | ||
|
|
c0d2803f61 | ||
|
|
e249d5238f | ||
|
|
e32e505e95 | ||
|
|
7642a84108 | ||
|
|
b6e20d69cc | ||
|
|
9d04e20a30 | ||
|
|
d2624813f7 | ||
|
|
a2789e70ed | ||
|
|
f6f607904e | ||
|
|
22ea0296c0 | ||
|
|
24ae2f5324 | ||
|
|
9284497cec | ||
|
|
36cb12952a | ||
|
|
2700dc7f39 | ||
|
|
c9d27d46b6 | ||
|
|
b68756378c | ||
|
|
1ff1880951 | ||
|
|
fea2d32de8 | ||
|
|
e3e6d4452c | ||
|
|
f687e8e658 | ||
|
|
a6d17659f8 | ||
|
|
997a6de23b | ||
|
|
c948b6d885 | ||
|
|
5508980384 | ||
|
|
e39dee01e5 | ||
|
|
a81412e176 | ||
|
|
09f9dd91aa | ||
|
|
2efc3c72ac | ||
|
|
a92fb03093 | ||
|
|
ad5847983f | ||
|
|
bb25300513 | ||
|
|
de33804eb4 | ||
|
|
de042f71dd | ||
|
|
5a1e85bbf4 | ||
|
|
3489e0df74 | ||
|
|
afe8eec343 | ||
|
|
8913d3df11 | ||
|
|
3a55965704 | ||
|
|
7112c74bf2 | ||
|
|
faa1906290 |
@@ -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`.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
+21
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ object CobblemonCommands {
|
||||
ChangeWalkSpeed.register(dispatcher)
|
||||
ChangeJointScaleCommand.register(dispatcher)
|
||||
TestCommand.register(dispatcher)
|
||||
ReloadShowdownCommand.register(dispatcher)
|
||||
ClickTextCommand.register(dispatcher)
|
||||
PokemonEditCommand.register(dispatcher)
|
||||
TeachCommand.register(dispatcher)
|
||||
@@ -59,12 +60,14 @@ 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
|
||||
CobblemonInfoCommand.register(dispatcher)
|
||||
CalculateSeatPositionCommand.register(dispatcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
|
||||
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
|
||||
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.debug.showdown.ShowdownRefreshInfoHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
@@ -30,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
|
||||
@@ -49,7 +49,14 @@ import com.cobblemon.mod.common.client.net.storage.party.SetPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.party.SetPartyReferenceHandler
|
||||
import com.cobblemon.mod.common.client.net.storage.pc.*
|
||||
import com.cobblemon.mod.common.client.net.toast.ToastPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.*
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeAcceptanceChangedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeCancelledHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeCompletedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeOfferExpiredHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeOfferNotificationHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
|
||||
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
|
||||
import com.cobblemon.mod.common.net.PacketRegisterInfo
|
||||
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
|
||||
@@ -60,7 +67,6 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPac
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.debug.showdown.ShowdownRefreshInfoPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
@@ -68,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
|
||||
@@ -91,18 +98,42 @@ import com.cobblemon.mod.common.net.messages.client.storage.party.InitializePart
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.MoveClientPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.party.SetPartyReferencePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.OpenPCPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.RenamePCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCBoxPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.ChangePCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.RequestPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.SetPCBoxWallpapersPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.toast.ToastPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeAcceptanceChangedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeCompletedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeProcessStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.PokedexUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.*
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.*
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BattleChallengeResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.BenchMovePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestMoveSwapPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.RequestPlayerInteractionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.SendOutPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleSelectActionsPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamLeavePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.BattleTeamResponsePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.RemoveSpectatorPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.move.MoveSelectedPacket
|
||||
@@ -110,9 +141,6 @@ import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonS
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMoveSelectCancelledPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.RequestShowdownInfoPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.ShowdownActionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.ShowdownInputPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.npc.SaveNPCPacket
|
||||
@@ -132,11 +160,21 @@ import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokem
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.ReleasePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.*
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.AcceptTradeRequestPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.ChangeTradeAcceptancePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.OfferTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.PerformTradePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.ChallengeResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.RequestInteractionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.BattleSelectActionsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.RemoveSpectatorHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamLeaveHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.battle.TeamRequestResponseHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.block.AdjustBlockEntityViewerCountHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.move.MoveSelectedHandler
|
||||
@@ -144,9 +182,6 @@ import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSe
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartySelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveSelectCancelledHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.showdown.RequestShowdownInfoHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.showdown.ShowdownActionHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.debug.showdown.ShowdownInputHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.dialogue.InputToDialogueHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.evolution.AcceptEvolutionHandler
|
||||
@@ -170,7 +205,12 @@ import com.cobblemon.mod.common.net.serverhandling.storage.party.MovePartyPokemo
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.ReleasePCPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.party.SwapPartyPokemonHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.pc.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.*
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.AcceptTradeRequestHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.CancelTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.OfferTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.PerformTradeHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.trade.UpdateTradeOfferHandler
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
@@ -349,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))
|
||||
|
||||
@@ -374,7 +417,6 @@ object CobblemonNetwork {
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(CalculateSeatPositionsPacket.ID, CalculateSeatPositionsPacket::decode, CalculateSeatPositionsHandler))
|
||||
list.add(PacketRegisterInfo(ShowdownRefreshInfoPacket.ID, ShowdownRefreshInfoPacket::decode, ShowdownRefreshInfoHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
@@ -474,11 +516,6 @@ object CobblemonNetwork {
|
||||
// Orientation packet(s)
|
||||
list.add(PacketRegisterInfo(C2SUpdateOrientationPacket.ID, C2SUpdateOrientationPacket::decode, OrientationPacketHandler))
|
||||
|
||||
// Debug / cheats
|
||||
list.add(PacketRegisterInfo(RequestShowdownInfoPacket.ID, RequestShowdownInfoPacket::decode, RequestShowdownInfoHandler))
|
||||
list.add(PacketRegisterInfo(ShowdownInputPacket.ID, ShowdownInputPacket::decode, ShowdownInputHandler))
|
||||
list.add(PacketRegisterInfo(ShowdownActionPacket.ID, ShowdownActionPacket::decode, ShowdownActionHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ object Abilities : DataRegistry {
|
||||
PotentialAbility.types.add(CommonAbilityType)
|
||||
PotentialAbility.types.add(HiddenAbilityType)
|
||||
this.abilityMap.clear()
|
||||
val abilitiesJson = ShowdownService.get().getAbilityIds()
|
||||
val abilitiesJson = ShowdownService.service.getAbilityIds()
|
||||
for (i in 0 until abilitiesJson.size()) {
|
||||
val id = abilitiesJson[i].asString
|
||||
val ability = AbilityTemplate(id)
|
||||
@@ -67,4 +67,4 @@ object Abilities : DataRegistry {
|
||||
abilities.forEach { ability -> this.register(ability) }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,10 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.*
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.EntityBackedBattleActor
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.FleeableBattleActor
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
@@ -71,9 +74,7 @@ import net.minecraft.world.item.ItemStack
|
||||
open class PokemonBattle(
|
||||
val format: BattleFormat,
|
||||
val side1: BattleSide,
|
||||
val side2: BattleSide,
|
||||
val service: ShowdownService = ShowdownService.get(),
|
||||
val battleId: UUID = UUID.randomUUID()
|
||||
val side2: BattleSide
|
||||
) {
|
||||
/** Whether logging will be silenced for this battle. */
|
||||
var mute = true
|
||||
@@ -108,6 +109,8 @@ open class PokemonBattle(
|
||||
val players = playerUUIDs.mapNotNull { it.getPlayer() }
|
||||
val spectators = mutableSetOf<UUID>()
|
||||
|
||||
val battleId = UUID.randomUUID()
|
||||
|
||||
val showdownMessages = mutableListOf<String>()
|
||||
val battleLog = mutableListOf<String>()
|
||||
val chatLog = mutableListOf<Component>()
|
||||
@@ -222,7 +225,7 @@ open class PokemonBattle(
|
||||
|
||||
fun writeShowdownAction(vararg messages: String) {
|
||||
log(messages.joinToString("\n"))
|
||||
service.send(battleId, messages.toList().toTypedArray())
|
||||
ShowdownService.service.send(battleId, messages.toList().toTypedArray())
|
||||
}
|
||||
|
||||
fun turn(newTurnNumber: Int) {
|
||||
@@ -375,6 +378,7 @@ open class PokemonBattle(
|
||||
|
||||
fun dispatch(dispatcher: () -> DispatchResult) {
|
||||
dispatches.add(BattleDispatch { dispatcher() })
|
||||
|
||||
}
|
||||
|
||||
fun dispatchToFront(dispatcher: () -> DispatchResult) {
|
||||
@@ -498,7 +502,6 @@ open class PokemonBattle(
|
||||
}
|
||||
|
||||
fun checkForInputDispatch() {
|
||||
actors.filter { it is UnionBattleActor }.forEach { it.mustChoose = false }
|
||||
if (checkForfeit()) return // ignore actors that are still choosing, their choices don't matter anymore
|
||||
val readyToInput = (actors.any { !it.mustChoose && it.responses.isNotEmpty() } && actors.none { it.mustChoose })
|
||||
if (readyToInput && captureActions.isEmpty()) {
|
||||
@@ -555,4 +558,4 @@ open class PokemonBattle(
|
||||
queryStruct.addFunction("has_rule") { params -> DoubleValue(params.getString(0) in format.ruleSet) }
|
||||
return queryStruct
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -42,4 +42,4 @@ abstract class AIBattleActor(
|
||||
setActionResponses(request!!.iterate(this.activePokemon) { _, _, _ -> PassActionResponse })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -73,7 +73,7 @@ abstract class BattleActor(
|
||||
open fun isForPlayer(serverPlayerEntity: ServerPlayer) = serverPlayerEntity.uuid in getPlayerUUIDs()
|
||||
open fun isForPokemon(pokemonEntity: PokemonEntity) = activePokemon.any { it.battlePokemon?.effectedPokemon?.entity == pokemonEntity }
|
||||
|
||||
open fun turn() {
|
||||
fun turn() {
|
||||
val request = request ?: return
|
||||
responses.clear()
|
||||
if(activePokemon.any { it.isAlive() }) {
|
||||
@@ -90,7 +90,7 @@ abstract class BattleActor(
|
||||
}
|
||||
}
|
||||
|
||||
open fun upkeep() {
|
||||
fun upkeep() {
|
||||
val request = request ?: return
|
||||
val forceSwitchPokemon = request.forceSwitch.mapIndexedNotNull { index, b -> if (b) activePokemon[index] else null }
|
||||
if (forceSwitchPokemon.isEmpty()) {
|
||||
@@ -125,7 +125,7 @@ abstract class BattleActor(
|
||||
battle.checkForInputDispatch()
|
||||
}
|
||||
|
||||
open fun writeShowdownResponse() {
|
||||
fun writeShowdownResponse() {
|
||||
val showdownMessages = mutableListOf<String>()
|
||||
var index = 0
|
||||
request!!.iterate(activePokemon) { activeBattlePokemon, showdownMoveSet, forceSwitch ->
|
||||
@@ -159,4 +159,4 @@ abstract class BattleActor(
|
||||
|
||||
open fun win(otherWinners: List<BattleActor>, losers: List<BattleActor>) {}
|
||||
open fun lose(winners: List<BattleActor>, otherLosers: List<BattleActor>) {}
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package com.cobblemon.mod.common.api.battles.model.actor
|
||||
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
|
||||
class UnionBattleActor(npc: NPCEntity, pokemonList: List<BattlePokemon>) : NPCBattleActor(npc, pokemonList.toMutableList(), 1) {
|
||||
|
||||
override fun writeShowdownResponse() {}
|
||||
|
||||
override fun onChoiceRequested() {
|
||||
this.mustChoose = false
|
||||
}
|
||||
|
||||
override fun turn() {
|
||||
super.turn()
|
||||
this.mustChoose = false
|
||||
}
|
||||
|
||||
override fun upkeep() {
|
||||
super.upkeep()
|
||||
this.mustChoose = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ object Moves : DataRegistry {
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.allMoves.clear()
|
||||
this.idMapping.clear()
|
||||
val movesJson = ShowdownService.get().getMoves()
|
||||
val movesJson = ShowdownService.service.getMoves()
|
||||
for (i in 0 until movesJson.size()) {
|
||||
val jsMove = movesJson[i].asJsonObject
|
||||
val id = jsMove.get("id").asString
|
||||
@@ -116,4 +116,4 @@ object Moves : DataRegistry {
|
||||
this.idMapping[move.num] = move
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+71
@@ -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())
|
||||
|
||||
}
|
||||
+5
-1
@@ -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 {
|
||||
@@ -217,4 +207,4 @@ data class RidingManager(val entity: PokemonEntity) {
|
||||
val controller = getController(entity) ?: return false
|
||||
return controller.dismountOnShift(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-8
@@ -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
|
||||
@@ -113,7 +113,7 @@ interface RideController : Encodable, Decodable {
|
||||
|
||||
fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean = false
|
||||
|
||||
//If function is not overwritten by controllers then just perform the defualt
|
||||
//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,
|
||||
@@ -125,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)
|
||||
@@ -150,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()
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
@@ -60,9 +59,7 @@ object BattleBuilder {
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
partyAccessor: (ServerPlayer) -> PartyStore = { it.party() },
|
||||
service: ShowdownService = ShowdownService.get(),
|
||||
battleId: UUID = UUID.randomUUID()
|
||||
partyAccessor: (ServerPlayer) -> PartyStore = { it.party() }
|
||||
): BattleStartResult {
|
||||
|
||||
val adjustLevel = battleFormat.adjustLevel
|
||||
@@ -119,9 +116,7 @@ object BattleBuilder {
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = BattleSide(player1Actor),
|
||||
side2 = BattleSide(player2Actor),
|
||||
service = service,
|
||||
battleId = battleId
|
||||
side2 = BattleSide(player2Actor)
|
||||
).ifSuccessful {
|
||||
it.battlePartyStores.addAll(battlePartyStores)
|
||||
}
|
||||
@@ -137,9 +132,7 @@ object BattleBuilder {
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_MULTI,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
partyAccessor: (ServerPlayer) -> PartyStore = { it.party() },
|
||||
service: ShowdownService = ShowdownService.get(),
|
||||
battleId: UUID = UUID.randomUUID()
|
||||
partyAccessor: (ServerPlayer) -> PartyStore = { it.party() }
|
||||
): BattleStartResult {
|
||||
val adjustLevel = battleFormat.adjustLevel
|
||||
val teams = players.mapIndexed { index, it ->
|
||||
@@ -220,9 +213,7 @@ object BattleBuilder {
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = BattleSide(playerActors[0], playerActors[1]),
|
||||
side2 = BattleSide(playerActors[2], playerActors[3]),
|
||||
service = service,
|
||||
battleId = battleId
|
||||
side2 = BattleSide(playerActors[2], playerActors[3])
|
||||
).ifSuccessful {
|
||||
it.battlePartyStores.addAll(battlePartyStores)
|
||||
}
|
||||
@@ -254,9 +245,7 @@ object BattleBuilder {
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
fleeDistance: Float = Cobblemon.config.defaultFleeDistance,
|
||||
party: PartyStore = player.party(),
|
||||
service: ShowdownService = ShowdownService.get(),
|
||||
battleId: UUID = UUID.randomUUID()
|
||||
party: PartyStore = player.party()
|
||||
): BattleStartResult {
|
||||
val playerTeam = party.toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemon = leadingPokemon).sortedBy { it.health <= 0 }
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
@@ -297,9 +286,7 @@ object BattleBuilder {
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = BattleSide(playerActor),
|
||||
side2 = BattleSide(wildActor),
|
||||
service = service,
|
||||
battleId = battleId
|
||||
side2 = BattleSide(wildActor)
|
||||
).ifSuccessful {
|
||||
if (!cloneParties) {
|
||||
pokemonEntity.battleId = it.battleId
|
||||
@@ -329,9 +316,7 @@ object BattleBuilder {
|
||||
battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES,
|
||||
cloneParties: Boolean = false,
|
||||
healFirst: Boolean = false,
|
||||
party: PartyStore = player.party(),
|
||||
service: ShowdownService = ShowdownService.get(),
|
||||
battleId: UUID = UUID.randomUUID()
|
||||
party: PartyStore = player.party()
|
||||
): BattleStartResult {
|
||||
val playerTeam = party.toBattleTeam(clone = cloneParties, healPokemon = healFirst, leadingPokemon = leadingPokemon)
|
||||
val playerActor = PlayerBattleActor(player.uuid, playerTeam)
|
||||
@@ -378,9 +363,7 @@ object BattleBuilder {
|
||||
BattleRegistry.startBattle(
|
||||
battleFormat = battleFormat,
|
||||
side1 = BattleSide(playerActor),
|
||||
side2 = BattleSide(npcActor),
|
||||
service = service,
|
||||
battleId = battleId
|
||||
side2 = BattleSide(npcActor)
|
||||
).ifSuccessful { battle ->
|
||||
npcEntity.entityData.update(NPCEntity.BATTLE_IDS) { it + battle.battleId }
|
||||
}
|
||||
@@ -554,4 +537,4 @@ open class ErroredBattleStart(
|
||||
|
||||
val errors: Iterable<BattleStartError>
|
||||
get() = generalErrors + participantErrors.flatMap { it.value }
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ object BattleRegistry {
|
||||
return team.joinToString("]")
|
||||
}
|
||||
|
||||
private fun startShowdown(battle: PokemonBattle, sendMessages: Boolean = true) {
|
||||
private fun startShowdown(battle: PokemonBattle) {
|
||||
|
||||
// Build request message
|
||||
val messages = mutableListOf<String>()
|
||||
@@ -176,9 +176,7 @@ object BattleRegistry {
|
||||
}
|
||||
|
||||
// Compiles the request and sends it off
|
||||
if (sendMessages) {
|
||||
battle.service.startBattle(battle, messages.toTypedArray())
|
||||
}
|
||||
ShowdownService.service.startBattle(battle, messages.toTypedArray())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +186,6 @@ object BattleRegistry {
|
||||
* @param side1 The first side for the battle.
|
||||
* @param side2 The second side for the battle.
|
||||
* @param canPreempt Whether the battle can be blocked by subscribers to [CobblemonEvents.BATTLE_STARTED_PRE].
|
||||
* @param service The showdown service to use for the battle.
|
||||
*
|
||||
* NOTE: this does NOT validate the [battleFormat] or sides before launching Showdown.
|
||||
* This creates the battle and hands it off to Showdown - YOU WILL GET ISSUES IF THE BATTLE IS INVALID!
|
||||
@@ -198,15 +195,12 @@ object BattleRegistry {
|
||||
battleFormat: BattleFormat,
|
||||
side1: BattleSide,
|
||||
side2: BattleSide,
|
||||
canPreempt: Boolean = true,
|
||||
service: ShowdownService = ShowdownService.get(),
|
||||
battleId: UUID = UUID.randomUUID(),
|
||||
sendMessages: Boolean = true
|
||||
canPreempt: Boolean = true
|
||||
): BattleStartResult {
|
||||
val battle = PokemonBattle(battleFormat, side1, side2, service, battleId)
|
||||
val battle = PokemonBattle(battleFormat, side1, side2)
|
||||
val start: () -> Unit = {
|
||||
battleMap[battle.battleId] = battle
|
||||
startShowdown(battle, sendMessages)
|
||||
startShowdown(battle)
|
||||
}
|
||||
|
||||
if (!canPreempt) start().also { return SuccessfulBattleStart(battle) }
|
||||
@@ -240,4 +234,4 @@ object BattleRegistry {
|
||||
fun tick() {
|
||||
battleMap.forEachValue(Long.MAX_VALUE) { it.tick() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,13 @@ class ShowdownThread : Thread("Cobblemon Showdown") {
|
||||
this.start()
|
||||
this.latch.await()
|
||||
for (action in whenReady) {
|
||||
action(ShowdownService.get())
|
||||
action(ShowdownService.service)
|
||||
}
|
||||
}
|
||||
|
||||
fun queue(action: (ShowdownService) -> Unit) {
|
||||
if (this.latch.count == 0L) {
|
||||
action(ShowdownService.get())
|
||||
action(ShowdownService.service)
|
||||
} else {
|
||||
this.whenReady.add(action)
|
||||
}
|
||||
@@ -37,8 +37,8 @@ class ShowdownThread : Thread("Cobblemon Showdown") {
|
||||
|
||||
override fun run() {
|
||||
LOGGER.info("Starting showdown service...")
|
||||
ShowdownService.get().openConnection()
|
||||
ShowdownService.service.openConnection()
|
||||
LOGGER.info("Showdown has been started!")
|
||||
this.latch.countDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -49,4 +49,4 @@ class RequestInstruction(val battleActor: BattleActor, val message: BattleMessag
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.cobblemon.mod.common.battles.runner
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Class responsible for logging debug messages for the showdown service.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
class ShowdownDebugLog {
|
||||
val log = mutableListOf<LogMessage>()
|
||||
|
||||
/**
|
||||
* Adds a log message to the debug log.
|
||||
*
|
||||
* @param source The source of the log message.
|
||||
* @param message The log message.
|
||||
*/
|
||||
fun add(source: String, message: String) {
|
||||
if (!Cobblemon.config.enableDebugKeys) return
|
||||
val now = Date()
|
||||
val timestamp = SimpleDateFormat("HH:mm:ss").format(now)
|
||||
log.add(LogMessage("[$source][${timestamp}]> $message", now))
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all log messages from the debug log.
|
||||
*/
|
||||
fun clear() {
|
||||
log.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves log messages after a specified date.
|
||||
*
|
||||
* @param after The date after which to retrieve log messages. If null, retrieves the last 250 messages.
|
||||
* @return A list of log messages.
|
||||
*/
|
||||
fun get(after: Date?): List<String> {
|
||||
if (after == null) return log.take(250).map { it.message }
|
||||
return log.filter { it.time.after(after) }.map { it.message }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided messages are the start of the log.
|
||||
*
|
||||
* @param messages The messages to check.
|
||||
* @return True if the messages are the start of the log, false otherwise.
|
||||
*/
|
||||
fun isStartOfLog(messages: List<String>): Boolean {
|
||||
if (messages.isEmpty() && log.isEmpty()) return true
|
||||
return messages.size == log.size && messages.first() == log.first().message
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class representing a log message.
|
||||
*
|
||||
* @property message The log message.
|
||||
* @property time The time the log message was created.
|
||||
*/
|
||||
data class LogMessage(val message: String, val time: Date)
|
||||
}
|
||||
@@ -10,154 +10,32 @@ package com.cobblemon.mod.common.battles.runner
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
|
||||
import com.cobblemon.mod.common.battles.runner.remote.UnionShowdownService
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.JsonArray
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service.
|
||||
*
|
||||
* All outgoing calls to showdown should be done through this layer to decouple the mod from a specific implementation.
|
||||
* To get an instance of the service, use the singleton provided via `ShowdownService.get()`.
|
||||
* To get an instance of the service, use the singleton provided via `ShowdownService.get()`
|
||||
*
|
||||
* @since February 27th, 2023
|
||||
* @author landonjw
|
||||
*/
|
||||
interface ShowdownService {
|
||||
/**
|
||||
* The unique identifier for this showdown service.
|
||||
*/
|
||||
val id: ResourceLocation
|
||||
|
||||
/**
|
||||
* The current status of the showdown service.
|
||||
*/
|
||||
val status: ShowdownStatus
|
||||
|
||||
/**
|
||||
* The log of messages from the showdown service.
|
||||
* This is accessed for use in [ShowdownDebugGUI]. It is not intended for general use
|
||||
* and will be empty in production environments.
|
||||
*/
|
||||
val log: ShowdownDebugLog
|
||||
|
||||
/**
|
||||
* Opens a connection to the showdown service.
|
||||
*/
|
||||
fun openConnection()
|
||||
|
||||
/**
|
||||
* Closes the connection to the showdown service.
|
||||
*/
|
||||
fun closeConnection()
|
||||
|
||||
/**
|
||||
* Restarts the connection to the showdown service.
|
||||
*/
|
||||
fun restartConnection()
|
||||
|
||||
/**
|
||||
* Starts a battle in the showdown service.
|
||||
*
|
||||
* @param battle The battle to start.
|
||||
* @param messages The initial messages for the battle.
|
||||
*/
|
||||
fun startBattle(battle: PokemonBattle, messages: Array<String>)
|
||||
|
||||
/**
|
||||
* Sends messages to a specific battle in the showdown service.
|
||||
*
|
||||
* @param battleId The unique identifier of the battle.
|
||||
* @param messages The messages to send.
|
||||
*/
|
||||
fun send(battleId: UUID, messages: Array<String>)
|
||||
|
||||
/**
|
||||
* Sends a message to the showdown service.
|
||||
*
|
||||
* @param message The message to send.
|
||||
*/
|
||||
fun send(message: String)
|
||||
|
||||
/**
|
||||
* Retrieves the ability IDs from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of ability IDs.
|
||||
*/
|
||||
fun getAbilityIds(): JsonArray
|
||||
|
||||
/**
|
||||
* Retrieves the moves from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of moves.
|
||||
*/
|
||||
fun getMoves(): JsonArray
|
||||
|
||||
/**
|
||||
* Retrieves the item IDs from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of item IDs.
|
||||
*/
|
||||
fun getItemIds(): JsonArray
|
||||
|
||||
/**
|
||||
* Registers species in the showdown service.
|
||||
*/
|
||||
fun registerSpecies()
|
||||
|
||||
/**
|
||||
* Registers bag items in the showdown service.
|
||||
*/
|
||||
fun registerBagItems()
|
||||
|
||||
/**
|
||||
* Indicates that species have been initialized in the showdown service.
|
||||
*/
|
||||
fun indicateSpeciesInitialized() {}
|
||||
|
||||
|
||||
companion object {
|
||||
private val services = mutableMapOf<ResourceLocation, ShowdownService>(
|
||||
cobblemonResource("embedded") to GraalShowdownService(cobblemonResource("embedded")),
|
||||
cobblemonResource("union") to UnionShowdownService(cobblemonResource("union"))
|
||||
)
|
||||
|
||||
/**
|
||||
* Retrieves the showdown service for the specified path.
|
||||
*
|
||||
* @param path The resource location of the showdown service.
|
||||
* @return The showdown service.
|
||||
* @throws IllegalStateException if no showdown service is found for the specified path.
|
||||
*/
|
||||
fun get(path: ResourceLocation = cobblemonResource("embedded")): ShowdownService {
|
||||
return services[path] ?: error("No showdown service found for path $path")
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a showdown service exists for the specified path.
|
||||
*
|
||||
* @param path The resource location of the showdown service.
|
||||
* @return `true` if the showdown service exists, `false` otherwise.
|
||||
*/
|
||||
fun contains(path: ResourceLocation): Boolean {
|
||||
return services.containsKey(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all available showdown services.
|
||||
*
|
||||
* @return A list of all showdown services.
|
||||
*/
|
||||
fun all(): List<ShowdownService> {
|
||||
return services.values.toList()
|
||||
}
|
||||
val service: ShowdownService by lazy { GraalShowdownService() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing the status of the showdown service.
|
||||
*/
|
||||
enum class ShowdownStatus {
|
||||
CONNECTED, DISCONNECTED, RESTARTING
|
||||
}
|
||||
}
|
||||
+4
-152
@@ -13,12 +13,9 @@ import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownDebugLog
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownStatus
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
@@ -41,20 +38,12 @@ import org.graalvm.polyglot.io.FileSystem
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service via
|
||||
* GraalVM. This directly invokes JavaScript functions provided within the showdown service.
|
||||
*
|
||||
* @property id The unique identifier for this showdown service.
|
||||
* @property context The GraalVM context for executing JavaScript.
|
||||
* @property sendBattleMessageFunction The JavaScript function for sending battle messages.
|
||||
* @property unbundler The unbundler for extracting necessary showdown files.
|
||||
* @property gson The Gson instance for JSON operations.
|
||||
* @property log The debug log for the showdown service.
|
||||
* @property status The current status of the showdown service.
|
||||
*
|
||||
* @see {@code cobbled-exports/cobbled-index.js} within cobbledmon-showdown repository
|
||||
* @see <a href="https://www.graalvm.org/">
|
||||
* @since February 27, 2023
|
||||
* @author Hiroku, landonjw
|
||||
*/
|
||||
class GraalShowdownService(override val id: ResourceLocation) : ShowdownService {
|
||||
class GraalShowdownService : ShowdownService {
|
||||
|
||||
@Transient
|
||||
lateinit var context: Context
|
||||
@@ -65,35 +54,12 @@ class GraalShowdownService(override val id: ResourceLocation) : ShowdownService
|
||||
@Transient
|
||||
val gson = Gson()
|
||||
|
||||
override val log = ShowdownDebugLog()
|
||||
|
||||
override var status: ShowdownStatus = ShowdownStatus.DISCONNECTED
|
||||
private set
|
||||
|
||||
/**
|
||||
* Opens a connection to the showdown service.
|
||||
*/
|
||||
override fun openConnection() = openConnection(false)
|
||||
|
||||
/**
|
||||
* Opens a connection to the showdown service.
|
||||
*
|
||||
* @param silent If true, suppresses changing the status of the connection process.
|
||||
*/
|
||||
private fun openConnection(silent: Boolean) {
|
||||
log.clear()
|
||||
log.add(source = "Minecraft", message = "Opening showdown connection")
|
||||
override fun openConnection() {
|
||||
unbundler.attemptUnbundle()
|
||||
createContext()
|
||||
boot()
|
||||
if (!silent) {
|
||||
status = ShowdownStatus.CONNECTED
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the GraalVM context for executing JavaScript.
|
||||
*/
|
||||
private fun createContext() {
|
||||
val wd = Paths.get("./showdown")
|
||||
val access = HostAccess.newBuilder(HostAccess.EXPLICIT)
|
||||
@@ -150,126 +116,42 @@ class GraalShowdownService(override val id: ResourceLocation) : ShowdownService
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to the showdown service.
|
||||
*/
|
||||
override fun closeConnection() = closeConnection(false)
|
||||
|
||||
/**
|
||||
* Closes the connection to the showdown service.
|
||||
*
|
||||
* @param silent If true, suppresses changing the status of the connection process.
|
||||
*/
|
||||
private fun closeConnection(silent: Boolean) {
|
||||
log.add(source = "Minecraft", message = "Closing showdown connection")
|
||||
override fun closeConnection() {
|
||||
context.close()
|
||||
if (!silent) {
|
||||
this.status = ShowdownStatus.DISCONNECTED
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts the connection to the showdown service.
|
||||
*/
|
||||
override fun restartConnection() {
|
||||
this.status = ShowdownStatus.RESTARTING
|
||||
try {
|
||||
closeConnection(true)
|
||||
openConnection(true)
|
||||
this.status = ShowdownStatus.CONNECTED
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Failed to restart showdown connection", e)
|
||||
this.status = ShowdownStatus.DISCONNECTED
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the showdown service by evaluating the JavaScript entry point.
|
||||
*/
|
||||
private fun boot() {
|
||||
log.add(source = "Minecraft", message = "Booting showdown")
|
||||
context.eval("js", File("showdown/index.js").readText())
|
||||
sendBattleMessageFunction = context.getBindings("js").getMember("sendBattleMessage")
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a battle in the showdown service.
|
||||
*
|
||||
* @param battle The battle to start.
|
||||
* @param messages The initial messages for the battle.
|
||||
*/
|
||||
override fun startBattle(battle: PokemonBattle, messages: Array<String>) {
|
||||
val startBattleFunction = context.getBindings("js").getMember("startBattle")
|
||||
log.add(source = "Minecraft", message = "Starting battle ${battle.battleId}")
|
||||
messages.forEach { message ->
|
||||
log.add(source = "Minecraft", message = "(${battle.battleId}) $message")
|
||||
}
|
||||
startBattleFunction.execute(this, battle.battleId.toString(), messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends messages to a specific battle in the showdown service.
|
||||
*
|
||||
* @param battleId The unique identifier of the battle.
|
||||
* @param messages The messages to send.
|
||||
*/
|
||||
override fun send(battleId: UUID, messages: Array<String>) {
|
||||
sendToShowdown(battleId, messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the showdown service.
|
||||
*
|
||||
* @param message The message to send.
|
||||
*/
|
||||
override fun send(message: String) {
|
||||
log.add("Minecraft", message)
|
||||
val regex = Regex("^\\(([^()]*)\\)")
|
||||
val match = regex.find(message)
|
||||
val battleId = match?.groups?.get(1)?.value ?: return
|
||||
val endIndex = match.groups[1]!!.range.last + 1
|
||||
sendToShowdown(UUID.fromString(battleId), arrayOf(message.substring(endIndex)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ability IDs from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of ability IDs.
|
||||
*/
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
val getCobbledAbilityIdsFn = context.getBindings("js").getMember("getCobbledAbilityIds")
|
||||
val arrayResult = getCobbledAbilityIdsFn.execute().asString()
|
||||
log.add(source = "Minecraft", message = "Getting ability ids")
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the moves from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of moves.
|
||||
*/
|
||||
override fun getMoves(): JsonArray {
|
||||
val getCobbledMovesFn = context.getBindings("js").getMember("getCobbledMoves")
|
||||
log.add(source = "Minecraft", message = "Getting moves")
|
||||
val arrayResult = getCobbledMovesFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the item IDs from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of item IDs.
|
||||
*/
|
||||
override fun getItemIds(): JsonArray {
|
||||
val getCobbledItemIdsFn = context.getBindings("js").getMember("getCobbledItemIds")
|
||||
log.add(source = "Minecraft", message = "Getting item ids")
|
||||
val arrayResult = getCobbledItemIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers species in the showdown service.
|
||||
*/
|
||||
override fun registerSpecies() {
|
||||
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
|
||||
val jsArray = this.context.eval("js", "new Array();")
|
||||
@@ -282,62 +164,32 @@ class GraalShowdownService(override val id: ResourceLocation) : ShowdownService
|
||||
}
|
||||
}
|
||||
}
|
||||
log.add(source = "Minecraft", message = "Registering species")
|
||||
receiveSpeciesDataFn.execute(jsArray)
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that species have been initialized to the showdown service.
|
||||
*/
|
||||
override fun indicateSpeciesInitialized() {
|
||||
val afterCobbledSpeciesInitFn = this.context.getBindings("js").getMember("afterCobbledSpeciesInit")
|
||||
log.add(source = "Minecraft", message = "Indicating species initialized")
|
||||
afterCobbledSpeciesInitFn.execute()
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers bag items in the showdown service.
|
||||
*/
|
||||
override fun registerBagItems() {
|
||||
val receiveBagItemDataFn = this.context.getBindings("js").getMember("receiveBagItemData")
|
||||
for ((itemId, js) in BagItems.bagItemsScripts) {
|
||||
log.add(source = "Minecraft", message = "Registering bag item $itemId")
|
||||
receiveBagItemDataFn.execute(itemId, js.replace("\n", " "))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends messages to the showdown service.
|
||||
*
|
||||
* @param battleId The unique identifier of the battle.
|
||||
* @param messages The messages to send.
|
||||
*/
|
||||
private fun sendToShowdown(battleId: UUID, messages: Array<String>) {
|
||||
messages.forEach { message ->
|
||||
log.add(source = "Minecraft", message = "($battleId) $message")
|
||||
}
|
||||
sendBattleMessageFunction.execute(battleId.toString(), messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives messages from the showdown service.
|
||||
*
|
||||
* @param battleId The unique identifier of the battle.
|
||||
* @param message The message received.
|
||||
*/
|
||||
@Export
|
||||
fun sendFromShowdown(battleId: String, message: String) {
|
||||
log.add(source = "Showdown", message = "($battleId) $message")
|
||||
ShowdownInterpreter.interpretMessage(UUID.fromString(battleId), message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs messages from the showdown service.
|
||||
*
|
||||
* @param message The message to log.
|
||||
*/
|
||||
@Export
|
||||
fun log(message: String) {
|
||||
Cobblemon.LOGGER.info(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
-381
@@ -1,381 +0,0 @@
|
||||
package com.cobblemon.mod.common.battles.runner.remote
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.UnionBattleActor
|
||||
import com.cobblemon.mod.common.api.npc.NPCClasses
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownDebugLog
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownStatus
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.HttpService
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonParser
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.schedule.Activity
|
||||
import java.net.URI
|
||||
import java.net.http.HttpClient
|
||||
import java.net.http.WebSocket
|
||||
import java.util.*
|
||||
import java.util.concurrent.CompletionStage
|
||||
|
||||
/**
|
||||
* Implementation of the ShowdownService interface for use with the Union Block.
|
||||
*
|
||||
* This wraps around a WebSocket connection to a remote showdown service.
|
||||
*
|
||||
* @property id The unique identifier for this showdown service.
|
||||
* @property host The host address of the showdown service.
|
||||
* @property httpPort The port for HTTP communication with the showdown service.
|
||||
* @property socketPort The port for WebSocket communication with the showdown service.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
class UnionShowdownService(
|
||||
override val id: ResourceLocation,
|
||||
val host: String = "localhost",
|
||||
val httpPort: Int = 8003,
|
||||
val socketPort: Int = 8002
|
||||
) : ShowdownService {
|
||||
|
||||
override var status: ShowdownStatus = ShowdownStatus.DISCONNECTED
|
||||
private set
|
||||
|
||||
override val log = ShowdownDebugLog()
|
||||
|
||||
private lateinit var webSocket: WebSocket
|
||||
|
||||
/**
|
||||
* Opens a connection to the showdown service.
|
||||
*/
|
||||
override fun openConnection() = openConnection(false)
|
||||
|
||||
/**
|
||||
* Opens a connection to the showdown service.
|
||||
*
|
||||
* @param silent If true, suppresses changing the status of the connection process.
|
||||
*/
|
||||
private fun openConnection(silent: Boolean) {
|
||||
log.clear()
|
||||
log.add(source = "Minecraft", message = "Opening showdown connection")
|
||||
try {
|
||||
webSocket = HttpClient.newHttpClient()
|
||||
.newWebSocketBuilder()
|
||||
.buildAsync(URI.create("ws://$host:$socketPort"), WebSocketListener(this))
|
||||
.join()
|
||||
// TODO: See if this is needed
|
||||
registerSpecies()
|
||||
indicateSpeciesInitialized()
|
||||
registerBagItems()
|
||||
if (!silent) {
|
||||
this.status = ShowdownStatus.CONNECTED
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.warn("Failed to open showdown connection to $host:$socketPort", e)
|
||||
log.add(source = "Minecraft", message = "Failed to open showdown connection with error: ${e.message}")
|
||||
if (!silent) {
|
||||
this.status = ShowdownStatus.DISCONNECTED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to the showdown service.
|
||||
*/
|
||||
override fun closeConnection() = closeConnection(false)
|
||||
|
||||
/**
|
||||
* Closes the connection to the showdown service.
|
||||
*
|
||||
* @param silent If true, suppresses changing the status of the disconnection process.
|
||||
*/
|
||||
private fun closeConnection(silent: Boolean) {
|
||||
log.add(source = "Minecraft", message = "Closing showdown connection")
|
||||
try {
|
||||
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "Goodbye!").join()
|
||||
} catch (e: Exception) {
|
||||
Cobblemon.LOGGER.warn("Failed to close showdown connection to $host:$socketPort", e)
|
||||
log.add(source = "Minecraft", message = "Failed to close showdown connection to $host:$socketPort with error: ${e.message}")
|
||||
}
|
||||
finally {
|
||||
if (!silent) {
|
||||
this.status = ShowdownStatus.DISCONNECTED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts the connection to the showdown service.
|
||||
*/
|
||||
override fun restartConnection() {
|
||||
this.status = ShowdownStatus.RESTARTING
|
||||
closeConnection(true)
|
||||
openConnection(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a battle in the showdown service.
|
||||
*
|
||||
* @param battle The battle to start.
|
||||
* @param messages The initial messages for the battle.
|
||||
*/
|
||||
override fun startBattle(battle: PokemonBattle, messages: Array<String>) {
|
||||
send("[${battle.battleId}]>start-battle")
|
||||
send(battle.battleId, messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends messages to a specific battle in the showdown service.
|
||||
*
|
||||
* @param battleId The unique identifier of the battle.
|
||||
* @param messages The messages to send.
|
||||
*/
|
||||
override fun send(battleId: UUID, messages: Array<String>) {
|
||||
messages.forEach { send("[$battleId]$it") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the showdown service.
|
||||
*
|
||||
* @param message The message to send.
|
||||
*/
|
||||
override fun send(message: String) {
|
||||
webSocket.sendText(message, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ability IDs from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of ability IDs.
|
||||
*/
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the moves from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of moves.
|
||||
*/
|
||||
override fun getMoves(): JsonArray {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the item IDs from the showdown service.
|
||||
*
|
||||
* @return A `JsonArray` of item IDs.
|
||||
*/
|
||||
override fun getItemIds(): JsonArray {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers species in the showdown service.
|
||||
*/
|
||||
override fun registerSpecies() {
|
||||
HttpService.post<Unit>(
|
||||
url = "http://$host:$httpPort/Species",
|
||||
body = PokemonSpecies.species.map { PokemonSpecies.ShowdownSpecies(it, null) }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers bag items in the showdown service.
|
||||
*/
|
||||
override fun registerBagItems() {
|
||||
HttpService.post<Unit>(
|
||||
url = "http://$host:$httpPort/BagItems",
|
||||
body = BagItems.bagItemsScripts.map { BagItem(it.key, it.value) }.toList()
|
||||
)
|
||||
}
|
||||
|
||||
override fun indicateSpeciesInitialized() {
|
||||
HttpService.post<Unit>(url = "http://$host:$httpPort/InitializeDex")
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket listener for handling events from the showdown service.
|
||||
*
|
||||
* @property service The UnionShowdownService instance.
|
||||
*/
|
||||
class WebSocketListener(val service: UnionShowdownService) : WebSocket.Listener {
|
||||
val builder = StringBuilder()
|
||||
|
||||
override fun onOpen(webSocket: WebSocket) {
|
||||
service.log.add(source = "Showdown", message = "Connected to server at ${service.host}:${service.socketPort}")
|
||||
super.onOpen(webSocket)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a battle based on the received instruction.
|
||||
*
|
||||
* @param instruction The instruction to start the battle.
|
||||
*/
|
||||
fun startBattle(instruction: String) {
|
||||
val arguments = instruction.replace("|start-battle|", "").split("|")
|
||||
if (arguments.size != 7) return
|
||||
val battleId = UUID.fromString(arguments[0])
|
||||
val playerIdA = UUID.fromString(arguments[1])
|
||||
val playerNameA = arguments[2]
|
||||
val playerIdB = UUID.fromString(arguments[3])
|
||||
val playerNameB = arguments[4]
|
||||
val teamA = JsonParser.parseString(arguments[5]).asJsonObject
|
||||
val teamB = JsonParser.parseString(arguments[6]).asJsonObject
|
||||
val playerA = server()?.playerList?.getPlayer(playerIdA)
|
||||
val playerB = server()?.playerList?.getPlayer(playerIdB)
|
||||
|
||||
println(arguments[5])
|
||||
println(arguments[6])
|
||||
|
||||
if (playerA == null && playerB == null) {
|
||||
Cobblemon.LOGGER.warn("Tried to start battle with no players present on server")
|
||||
return
|
||||
}
|
||||
|
||||
val registryAccess = server()?.registryAccess() ?: return
|
||||
|
||||
val playerAParty = PartyStore(UUID.randomUUID()).loadFromJSON(teamA, registryAccess)
|
||||
val playerBParty = PartyStore(UUID.randomUUID()).loadFromJSON(teamB, registryAccess)
|
||||
val playerATeam = toShowdownBattleTeam(playerAParty.toList()).onEach {
|
||||
it.effectedPokemon.level = 100
|
||||
it.effectedPokemon.heal()
|
||||
}
|
||||
val playerBTeam = toShowdownBattleTeam(playerBParty.toList()).onEach {
|
||||
it.effectedPokemon.level = 100
|
||||
it.effectedPokemon.heal()
|
||||
}
|
||||
|
||||
if (playerA != null && playerB != null) {
|
||||
val playerActorA = PlayerBattleActor(playerIdA, playerATeam)
|
||||
val playerActorB = PlayerBattleActor(playerIdB, playerBTeam)
|
||||
BattleRegistry.startBattle(
|
||||
battleId = battleId,
|
||||
battleFormat = BattleFormat.GEN_9_SINGLES,
|
||||
side1 = BattleSide(playerActorA),
|
||||
side2 = BattleSide(playerActorB),
|
||||
sendMessages = false,
|
||||
service = service
|
||||
)
|
||||
}
|
||||
else if (playerA != null && playerB == null) {
|
||||
val playerActorA = PlayerBattleActor(playerIdA, playerATeam)
|
||||
val npc = NPCEntity(playerA.level())
|
||||
npc.moveTo(playerA.x + 5, playerA.y, playerA.z, playerA.yRot, (playerA.xRot + 180f) % 360f)
|
||||
npc.npc = NPCClasses.getByIdentifier(cobblemonResource("standard"))!!
|
||||
npc.loadTextureFromGameProfileName(playerNameB)
|
||||
npc.initialize(1)
|
||||
npc.config.setDirectly("yaw", DoubleValue((playerA.xRot + 90f) % 360f))
|
||||
npc.uuid = playerIdB
|
||||
npc.customName = playerNameB.text()
|
||||
npc.isInvulnerable = true
|
||||
npc.isMovable = false
|
||||
playerA.level().addFreshEntity(npc)
|
||||
val playerActorB = UnionBattleActor(npc, playerBTeam)
|
||||
BattleRegistry.startBattle(
|
||||
battleId = battleId,
|
||||
battleFormat = BattleFormat.GEN_9_SINGLES,
|
||||
side1 = BattleSide(playerActorA),
|
||||
side2 = BattleSide(playerActorB),
|
||||
sendMessages = false,
|
||||
service = service
|
||||
)
|
||||
}
|
||||
else {
|
||||
val npc = NPCEntity(playerB!!.level())
|
||||
npc.moveTo(playerB.x + 5, playerB.y, playerB.z, playerB.yRot, (playerB.xRot + 180f) % 360f)
|
||||
npc.npc = NPCClasses.getByIdentifier(cobblemonResource("standard"))!!
|
||||
npc.loadTextureFromGameProfileName(playerNameA)
|
||||
npc.initialize(1)
|
||||
npc.uuid = playerIdA
|
||||
npc.customName = playerNameA.text()
|
||||
npc.isInvulnerable = true
|
||||
npc.isMovable = false
|
||||
npc.config.setDirectly("yaw", DoubleValue((playerB.xRot + 90f) % 360f))
|
||||
playerB.level().addFreshEntity(npc)
|
||||
val playerActorA = UnionBattleActor(npc, playerATeam)
|
||||
val playerActorB = PlayerBattleActor(playerIdB, playerBTeam)
|
||||
BattleRegistry.startBattle(
|
||||
battleId = battleId,
|
||||
battleFormat = BattleFormat.GEN_9_SINGLES,
|
||||
side1 = BattleSide(playerActorA),
|
||||
side2 = BattleSide(playerActorB),
|
||||
sendMessages = false,
|
||||
service = service
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of Pokemon to a list of BattlePokemon for the showdown service.
|
||||
*
|
||||
* @param team The list of Pokemon to convert.
|
||||
* @return A list of BattlePokemon.
|
||||
*/
|
||||
fun toShowdownBattleTeam(team: List<Pokemon>): List<BattlePokemon> {
|
||||
return team.map { pokemon ->
|
||||
BattlePokemon(
|
||||
originalPokemon = pokemon,
|
||||
effectedPokemon = pokemon.clone(newUUID = false),
|
||||
postBattleEntityOperation = { entity -> entity.discard() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onText(webSocket: WebSocket, data: CharSequence, last: Boolean): CompletionStage<*>? {
|
||||
if (last) {
|
||||
builder.append(data)
|
||||
val message = builder.toString()
|
||||
service.log.add(source = "Showdown", message = message)
|
||||
|
||||
if (data.startsWith("|start-battle|")) {
|
||||
startBattle(data.toString())
|
||||
}
|
||||
else {
|
||||
val battleIdRegex = Regex("^\\[([^]]+)]")
|
||||
val match = battleIdRegex.find(message) ?: return null
|
||||
|
||||
val battleId = match.groupValues[1]
|
||||
val instruction = message.replace("[$battleId]", "")
|
||||
ShowdownInterpreter.interpretMessage(UUID.fromString(battleId), instruction)
|
||||
}
|
||||
|
||||
builder.clear()
|
||||
}
|
||||
else {
|
||||
builder.append(data)
|
||||
}
|
||||
return super.onText(webSocket, data, last)
|
||||
}
|
||||
|
||||
override fun onError(webSocket: WebSocket, error: Throwable) {
|
||||
service.log.add(source = "Showdown", message = "Error: ${error.message}")
|
||||
Cobblemon.LOGGER.warn("Error in showdown service ${service.id} websocket", error)
|
||||
super.onError(webSocket, error)
|
||||
}
|
||||
|
||||
override fun onClose(webSocket: WebSocket, statusCode: Int, reason: String): CompletionStage<*>? {
|
||||
service.log.add(source = "Showdown", message = "Closed connection with status $statusCode and reason $reason")
|
||||
return super.onClose(webSocket, statusCode, reason)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class representing a bag item. We use this for serialization to the showdown service.
|
||||
*
|
||||
* @property id The unique identifier of the bag item.
|
||||
* @property script The script associated with the bag item.
|
||||
*/
|
||||
private data class BagItem(val id: String, val script: String)
|
||||
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.battles.runner.socket
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
import java.net.InetAddress
|
||||
import java.net.Socket
|
||||
import java.nio.charset.Charset
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service via
|
||||
* a socket client.
|
||||
*
|
||||
* This is primarily used for debugging purposes, but could be extended in the future to provide
|
||||
* a means of connecting to a remote Showdown server. This does not provide any fault handling in the
|
||||
* event that the server goes down.
|
||||
*
|
||||
* When messages are sent to showdown, this will await a response from showdown.
|
||||
* The protocol for messages sent from showdown is <length of characters in payload><payload>,
|
||||
* and a payload length of 0 indicates that there is no response.
|
||||
*
|
||||
* @see {@code sim/cobbled-debug-server.ts} within cobblemon-showdown repository
|
||||
* @since February 27, 2023
|
||||
* @author landonjw
|
||||
*/
|
||||
class SocketShowdownService(val host: String = "localhost", val port: Int = 18468, val localPort: Int = 0) : ShowdownService {
|
||||
|
||||
private lateinit var socket: Socket
|
||||
private lateinit var writer: OutputStreamWriter
|
||||
private lateinit var reader: BufferedReader
|
||||
val gson = Gson()
|
||||
|
||||
override fun openConnection() {
|
||||
socket = Socket(InetAddress.getLocalHost(), port, InetAddress.getLocalHost(), localPort)
|
||||
writer = socket.getOutputStream().writer(charset = Charset.forName("ascii"))
|
||||
reader = BufferedReader(InputStreamReader(socket.getInputStream()))
|
||||
}
|
||||
|
||||
override fun closeConnection() {
|
||||
socket.close()
|
||||
}
|
||||
|
||||
override fun startBattle(battle: PokemonBattle, messages: Array<String>) {
|
||||
writer.write(">startbattle ${battle.battleId}\n")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to start battle!") }
|
||||
send(battle.battleId, messages)
|
||||
}
|
||||
|
||||
override fun send(battleId: UUID, messages: Array<String>) {
|
||||
for (message in messages) {
|
||||
writer.write("$battleId~$message\n")
|
||||
writer.flush()
|
||||
readBattleInput().forEach { interpretMessage(battleId, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun read(reader: BufferedReader, size: Int): String {
|
||||
val buffer = CharArray(size)
|
||||
while (true) {
|
||||
if(reader.read(buffer) == 0) continue
|
||||
return String(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readMessage(): String {
|
||||
val payloadSize = read(reader, 8).toInt()
|
||||
val payload = read(reader, payloadSize)
|
||||
return payload
|
||||
}
|
||||
|
||||
private fun readBattleInput(): List<String> {
|
||||
val lines = mutableListOf<String>()
|
||||
val numLines = read(reader, 8).toInt()
|
||||
if (numLines != 0) {
|
||||
for (i in 0 until numLines) {
|
||||
lines.add(readMessage())
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
private fun interpretMessage(battleId: UUID, message: String) {
|
||||
ShowdownInterpreter.interpretMessage(battleId, message)
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
writer.write(">getCobbledAbilityIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getMoves(): JsonArray {
|
||||
writer.write(">getCobbledMoves\n")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getItemIds(): JsonArray {
|
||||
writer.write(">getCobbledItemIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
private fun sendSpeciesData(species: Species, form: FormData?) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
private fun sendBagItem(itemId: String, js: String) {
|
||||
writer.write(">receiveBagItemData $itemId $js")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send bag item to Showdown: $itemId") }
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
writer.write(">resetSpeciesData\n")
|
||||
acknowledge()
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
sendSpeciesData(species, null)
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
sendSpeciesData(species, form)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun acknowledge(ifFails: () -> Unit = {}) {
|
||||
writer.flush()
|
||||
val ack = CharArray(3)
|
||||
reader.read(ack)
|
||||
if (String(ack) != "ACK") {
|
||||
ifFails()
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerBagItems() {
|
||||
for ((itemId, js) in BagItems.bagItemsScripts) {
|
||||
sendBagItem(itemId, js.replace("\n", " "))
|
||||
}
|
||||
}
|
||||
|
||||
override fun indicateSpeciesInitialized() {
|
||||
writer.write(">afterCobbledSpeciesInit")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
}
|
||||
+1
-5
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
package com.cobblemon.mod.common.client.gui.debug
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownStatus
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.debug.ShowdownDebugGUI.Companion.activeShowdownService
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.client.debug.showdown.ShowdownRefreshInfoPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.RequestShowdownInfoPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.ShowdownActionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.ShowdownInputPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.mojang.blaze3d.platform.ClipboardManager
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.gui.components.EditBox
|
||||
import net.minecraft.client.gui.components.ObjectSelectionList
|
||||
import net.minecraft.client.gui.components.ObjectSelectionList.Entry
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
import net.minecraft.locale.Language
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.util.FormattedCharSequence
|
||||
import java.awt.Color
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* GUI screen for debugging the Showdown service in the Cobblemon mod.
|
||||
* This screen allows users to view and interact with the Showdown service logs and status.
|
||||
*
|
||||
* @property status The current status of the showdown service.
|
||||
* @property service The resource location of the showdown service.
|
||||
* @property messages A list of messages from the active showdown service currently being refreshed.
|
||||
* @property availableServices A list of available showdown services. This allows the client to switch which server they are viewing.
|
||||
* @property requestProcessedAt The date and time when the request was processed.
|
||||
* @property includesStartOfLog Indicates if the messages include the start of the log.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
class ShowdownDebugGUI : Screen(lang("ui.debug.showdown")), CobblemonRenderable {
|
||||
|
||||
companion object {
|
||||
var activeShowdownService = cobblemonResource("embedded")
|
||||
private set
|
||||
}
|
||||
|
||||
private var showdownStatus: ShowdownStatus? = null
|
||||
val log = mutableListOf<String>()
|
||||
var availableServices = listOf(cobblemonResource("embedded"))
|
||||
var lastProcessedLogTime: Date? = null
|
||||
|
||||
val outputWindow = addRenderableWidget(ShowdownOutputList())
|
||||
val serviceButton = addRenderableWidget(
|
||||
Button.builder(Component.literal("Service: $activeShowdownService")) {
|
||||
cycleActiveService()
|
||||
}.bounds(10, 10, 200, 20).build()
|
||||
)
|
||||
|
||||
val startButton = addRenderableWidget(
|
||||
Button.builder(Component.literal("Start")) { button ->
|
||||
CobblemonNetwork.sendToServer(ShowdownActionPacket(
|
||||
service = activeShowdownService,
|
||||
action = ShowdownActionPacket.Action.START
|
||||
))
|
||||
}.bounds(10, 40, 50, 20).build()
|
||||
)
|
||||
|
||||
val stopButton = addRenderableWidget(
|
||||
Button.builder(Component.literal("Stop")) { button ->
|
||||
CobblemonNetwork.sendToServer(ShowdownActionPacket(
|
||||
service = activeShowdownService,
|
||||
action = ShowdownActionPacket.Action.STOP
|
||||
))
|
||||
}.bounds(70, 40, 50, 20).build()
|
||||
)
|
||||
|
||||
val restartButton = addRenderableWidget(
|
||||
Button.builder(Component.literal("Restart")) { button ->
|
||||
this.showdownStatus = ShowdownStatus.RESTARTING
|
||||
CobblemonNetwork.sendToServer(ShowdownActionPacket(
|
||||
service = activeShowdownService,
|
||||
action = ShowdownActionPacket.Action.RESTART
|
||||
))
|
||||
}.bounds(130, 40, 50, 20).build()
|
||||
)
|
||||
|
||||
val startNPCBattleButton = addRenderableWidget(
|
||||
Button.builder(Component.literal("Queue")) { button ->
|
||||
CobblemonNetwork.sendToServer(ShowdownActionPacket(
|
||||
service = activeShowdownService,
|
||||
action = ShowdownActionPacket.Action.QUEUE
|
||||
))
|
||||
}.bounds(10, 70, 100, 20).build()
|
||||
)
|
||||
|
||||
val clipboardManager = ClipboardManager()
|
||||
var timer = Timer()
|
||||
|
||||
init {
|
||||
addRenderableWidget(
|
||||
Button.builder(Component.literal("Copy to Clipboard")) Clipboard@{
|
||||
if (log.isEmpty()) return@Clipboard
|
||||
clipboardManager.setClipboard(Minecraft.getInstance().window.window, log.joinToString("\n"))
|
||||
}.bounds(170, Minecraft.getInstance().window.guiScaledHeight - 40, 100, 30).build()
|
||||
)
|
||||
|
||||
addRenderableWidget(ShowdownInputWidget(this))
|
||||
startTimer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through the available showdown services.
|
||||
*/
|
||||
fun cycleActiveService() {
|
||||
if (availableServices.size == 1) return
|
||||
val currentIndex = availableServices.indexOf(activeShowdownService)
|
||||
activeShowdownService = availableServices[(currentIndex + 1) % availableServices.size]
|
||||
serviceButton.message = Component.literal("Service: $activeShowdownService")
|
||||
outputWindow.reset()
|
||||
log.clear()
|
||||
lastProcessedLogTime = null
|
||||
startTimer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the timer for periodically refreshing the showdown info.
|
||||
*/
|
||||
fun startTimer() {
|
||||
timer.cancel()
|
||||
timer = Timer()
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
CobblemonNetwork.sendToServer(RequestShowdownInfoPacket(activeShowdownService, lastProcessedLogTime))
|
||||
}
|
||||
}, 0, 500)
|
||||
}
|
||||
|
||||
override fun onClose() {
|
||||
timer.cancel()
|
||||
super.onClose()
|
||||
}
|
||||
|
||||
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
guiGraphics.fill(0, 0, width, height, Color(0, 0, 0, 100).rgb)
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTick)
|
||||
if (showdownStatus != null) {
|
||||
guiGraphics.fill(
|
||||
190,
|
||||
45,
|
||||
200,
|
||||
55,
|
||||
when (showdownStatus!!) {
|
||||
ShowdownStatus.DISCONNECTED -> Color(255, 0, 0, 255).rgb
|
||||
ShowdownStatus.RESTARTING -> Color(255, 255, 0, 255).rgb
|
||||
ShowdownStatus.CONNECTED -> Color(0, 255, 0, 255).rgb
|
||||
}
|
||||
)
|
||||
drawScaledText(
|
||||
context = guiGraphics,
|
||||
text = Component.literal(showdownStatus!!.name.replaceFirstChar {
|
||||
if (it.isLowerCase()) it.titlecase(
|
||||
Locale.getDefault()
|
||||
) else it.toString()
|
||||
}),
|
||||
x = 205,
|
||||
y = 47
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the showdown info with the data from the given packet.
|
||||
*
|
||||
* @param packet The packet containing the refreshed showdown info.
|
||||
*/
|
||||
fun refreshShowdownInfo(packet: ShowdownRefreshInfoPacket) {
|
||||
// Prevent false positives from old packets
|
||||
if (activeShowdownService != packet.service) return
|
||||
if (packet.includesStartOfLog) {
|
||||
log.clear()
|
||||
outputWindow.reset()
|
||||
}
|
||||
packet.messages.forEach {
|
||||
log.add(it)
|
||||
outputWindow.addNewEntry(it.text())
|
||||
}
|
||||
lastProcessedLogTime = packet.requestProcessedAt
|
||||
showdownStatus = packet.status
|
||||
when (showdownStatus) {
|
||||
ShowdownStatus.CONNECTED -> {
|
||||
startButton.active = false
|
||||
stopButton.active = true
|
||||
restartButton.active = true
|
||||
startNPCBattleButton.active = true
|
||||
}
|
||||
ShowdownStatus.DISCONNECTED -> {
|
||||
startButton.active = true
|
||||
stopButton.active = false
|
||||
restartButton.active = false
|
||||
startNPCBattleButton.active = false
|
||||
}
|
||||
ShowdownStatus.RESTARTING -> {
|
||||
startButton.active = false
|
||||
stopButton.active = false
|
||||
restartButton.active = false
|
||||
startNPCBattleButton.active = false
|
||||
}
|
||||
else -> {
|
||||
startButton.active = false
|
||||
stopButton.active = false
|
||||
restartButton.active = false
|
||||
startNPCBattleButton.active = false
|
||||
}
|
||||
}
|
||||
this.availableServices = packet.availableServices
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* List widget for displaying the output log of the Showdown service.
|
||||
*/
|
||||
class ShowdownOutputList : ObjectSelectionList<OutputEntry>(
|
||||
Minecraft.getInstance(),
|
||||
Minecraft.getInstance().window.guiScaledWidth - 280 - 10,
|
||||
Minecraft.getInstance().window.guiScaledHeight - 10 - 40,
|
||||
10,
|
||||
14
|
||||
) {
|
||||
/**
|
||||
* Adds a new entry to the output list.
|
||||
*
|
||||
* @param text The text to add as a new entry.
|
||||
*/
|
||||
fun addNewEntry(text: Component) {
|
||||
val line = text.copy()
|
||||
val textRenderer = Minecraft.getInstance().font
|
||||
val lines = Language.getInstance().getVisualOrder(textRenderer.splitter.splitLines(line, rowWidth, line.style))
|
||||
val fullyScrolledDown = maxScroll - scrollAmount < 10
|
||||
lines.forEach { addEntry(OutputEntry(it)) }
|
||||
if (fullyScrolledDown) {
|
||||
scrollAmount = maxScroll.toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelected(): OutputEntry? = null
|
||||
|
||||
override fun getRowWidth(): Int {
|
||||
return Minecraft.getInstance().window.guiScaledWidth - 280 - 10 - 10
|
||||
}
|
||||
|
||||
override fun getScrollbarPosition(): Int {
|
||||
return this.x + rowWidth + 5
|
||||
}
|
||||
|
||||
override fun renderWidget(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
|
||||
setRectangle(
|
||||
Minecraft.getInstance().window.guiScaledWidth - 280 - 10,
|
||||
Minecraft.getInstance().window.guiScaledHeight - 10 - 10 - 40,
|
||||
280,
|
||||
10
|
||||
)
|
||||
this.x = 280
|
||||
super.renderWidget(guiGraphics, mouseX, mouseY, partialTick)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the output list by clearing all entries.
|
||||
*/
|
||||
fun reset() {
|
||||
super.clearEntries()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry for the output list, representing a single line of text.
|
||||
*
|
||||
* @param line The formatted line of text to display.
|
||||
*/
|
||||
class OutputEntry(val line: FormattedCharSequence) : Entry<OutputEntry>() {
|
||||
override fun getNarration() = "".text()
|
||||
override fun render(
|
||||
context: GuiGraphics,
|
||||
index: Int,
|
||||
rowTop: Int,
|
||||
rowLeft: Int,
|
||||
rowWidth: Int,
|
||||
rowHeight: Int,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
isHovered: Boolean,
|
||||
partialTicks: Float
|
||||
) {
|
||||
drawScaledText(
|
||||
context,
|
||||
line,
|
||||
rowLeft,
|
||||
rowTop - 2
|
||||
)
|
||||
}
|
||||
|
||||
override fun isFocused() = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Input widget for entering commands to the Showdown service.
|
||||
*/
|
||||
class ShowdownInputWidget(val gui: ShowdownDebugGUI) : EditBox(
|
||||
Minecraft.getInstance().font,
|
||||
280,
|
||||
Minecraft.getInstance().window.guiScaledHeight - 40,
|
||||
Minecraft.getInstance().window.guiScaledWidth - 280 - 10,
|
||||
30,
|
||||
Component.literal("Enter command")
|
||||
) {
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
val result = super.keyPressed(keyCode, scanCode, modifiers)
|
||||
if (keyCode == 257 && this.value.isNotEmpty()) {
|
||||
CobblemonNetwork.sendToServer(ShowdownInputPacket(
|
||||
service = activeShowdownService,
|
||||
input = this.value
|
||||
))
|
||||
deleteCharsToPos(0)
|
||||
CobblemonNetwork.sendToServer(RequestShowdownInfoPacket(activeShowdownService, gui.lastProcessedLogTime))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
+2
-16
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.keybind.keybinds
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.debug.ShowdownDebugGUI
|
||||
import com.cobblemon.mod.common.client.keybind.CobblemonKeyBinding
|
||||
import com.cobblemon.mod.common.client.keybind.KeybindCategories
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
@@ -27,10 +26,8 @@ object DebugKeybindings {
|
||||
TranslateRightKeybinding(),
|
||||
TranslateUpKeybinding(),
|
||||
TranslateDownKeybinding(),
|
||||
PrintModelSettingsKeybinding(),
|
||||
ToggleShowdownDebugGUIKeybinding()
|
||||
PrintModelSettingsKeybinding()
|
||||
)
|
||||
|
||||
class ScaleUpKeybinding: CobblemonKeyBinding(
|
||||
"key.cobblemon.scaleportraitup",
|
||||
InputConstants.Type.KEYSYM,
|
||||
@@ -148,15 +145,4 @@ object DebugKeybindings {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleShowdownDebugGUIKeybinding : CobblemonKeyBinding(
|
||||
"key.cobblemon.toggleshowdowndebuggui",
|
||||
InputConstants.Type.KEYSYM,
|
||||
InputConstants.KEY_APOSTROPHE,
|
||||
KeybindCategories.COBBLEMON_DEBUG_CATEGORY
|
||||
) {
|
||||
override fun onPress() {
|
||||
Minecraft.getInstance().setScreen(ShowdownDebugGUI())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package com.cobblemon.mod.common.client.net.debug.showdown
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.client.gui.debug.ShowdownDebugGUI
|
||||
import com.cobblemon.mod.common.net.messages.client.debug.showdown.ShowdownRefreshInfoPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
|
||||
/**
|
||||
* Handles the reception of a [ShowdownRefreshInfoPacket] on the client side.
|
||||
* This handler updates the Showdown Debug GUI with the latest showdown information.
|
||||
* @author landonjw
|
||||
*/
|
||||
object ShowdownRefreshInfoHandler : ClientNetworkPacketHandler<ShowdownRefreshInfoPacket> {
|
||||
/**
|
||||
* Handles the received [ShowdownRefreshInfoPacket] by refreshing the showdown information
|
||||
* in the [ShowdownDebugGUI] if it is currently open.
|
||||
*
|
||||
* @param packet The received packet containing the showdown information.
|
||||
* @param client The Minecraft client instance.
|
||||
*/
|
||||
override fun handle(packet: ShowdownRefreshInfoPacket, client: Minecraft) {
|
||||
if (client.screen is ShowdownDebugGUI) {
|
||||
(client.screen as ShowdownDebugGUI).refreshShowdownInfo(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -11,13 +11,12 @@ 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
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.animationFunctions
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.ModelLayer
|
||||
@@ -717,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 {
|
||||
@@ -819,4 +819,4 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
condition = condition,
|
||||
animations = { animations(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-8
@@ -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 {
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -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
|
||||
@@ -178,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.battles.runner.ShowdownService
|
||||
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
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
object ReloadShowdownCommand {
|
||||
|
||||
fun register(dispatcher: CommandDispatcher<CommandSourceStack>) {
|
||||
val command = Commands.literal("reloadshowdown")
|
||||
.requires { it.hasPermission(4) }
|
||||
.executes(::execute)
|
||||
dispatcher.register(command)
|
||||
}
|
||||
|
||||
private fun execute(context: CommandContext<CommandSourceStack>): Int {
|
||||
try {
|
||||
ShowdownService.service.closeConnection()
|
||||
ShowdownService.service.openConnection()
|
||||
ShowdownService.service.registerSpecies()
|
||||
ShowdownService.service.registerBagItems()
|
||||
context.source.sendSystemMessage(Component.literal("Reloaded showdown"))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return Command.SINGLE_SUCCESS
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.command.argument.NPCClassArgumentType
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.alias
|
||||
import com.cobblemon.mod.common.util.commandLang
|
||||
import com.cobblemon.mod.common.util.permission
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import com.mojang.brigadier.Command
|
||||
@@ -83,4 +84,4 @@ object SpawnNPCCommand {
|
||||
}
|
||||
throw FAILED_SPAWN_EXCEPTION.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>()
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.cobblemon.mod.common.util.effectiveName
|
||||
import com.cobblemon.mod.common.util.update
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
open class NPCBattleActor(
|
||||
class NPCBattleActor(
|
||||
val npc: NPCEntity,
|
||||
pokemonList: List<BattlePokemon>,
|
||||
val skill: Int
|
||||
@@ -84,4 +84,4 @@ open class NPCBattleActor(
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -1771,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)
|
||||
@@ -1932,4 +1944,4 @@ open class PokemonEntity(
|
||||
override fun resolveEntityScan(): LivingEntity {
|
||||
return this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-11
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package com.cobblemon.mod.common.net.messages.client.debug.showdown
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownStatus
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.Date
|
||||
|
||||
|
||||
/**
|
||||
* Packet sent to refresh the showdown information with the current status, messages, and available services.
|
||||
* This is typically sent when a user is in the Showdown Debug UI after they send a [RequestShowdownInfoPacket].
|
||||
*
|
||||
* @property status The current status of the showdown service.
|
||||
* @property service The resource location of the showdown service.
|
||||
* @property messages A list of messages from the active showdown service currently being refreshed.
|
||||
* @property availableServices A list of available showdown services. This allows the client to switch which server they are viewing.
|
||||
* @property requestProcessedAt The date and time when the request was processed.
|
||||
* @property includesStartOfLog Indicates if the messages include the start of the log.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
class ShowdownRefreshInfoPacket(
|
||||
val status: ShowdownStatus,
|
||||
val service: ResourceLocation,
|
||||
val messages: List<String>,
|
||||
val availableServices: List<ResourceLocation>,
|
||||
val requestProcessedAt: Date,
|
||||
val includesStartOfLog: Boolean
|
||||
) : NetworkPacket<ShowdownRefreshInfoPacket> {
|
||||
companion object {
|
||||
val ID = cobblemonResource("refresh_showdown_info")
|
||||
|
||||
/**
|
||||
* Decodes a [ShowdownRefreshInfoPacket] from the given [RegistryFriendlyByteBuf].
|
||||
*
|
||||
* @param buffer The buffer to read from.
|
||||
* @return The decoded [ShowdownRefreshInfoPacket].
|
||||
*/
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = ShowdownRefreshInfoPacket(
|
||||
status = ShowdownStatus.valueOf(buffer.readString()),
|
||||
service = buffer.readResourceLocation(),
|
||||
messages = buffer.readList { buffer.readString() },
|
||||
availableServices = buffer.readList { buffer.readResourceLocation() },
|
||||
requestProcessedAt = buffer.readDate(),
|
||||
includesStartOfLog = buffer.readBoolean()
|
||||
)
|
||||
}
|
||||
|
||||
override val id = ID
|
||||
|
||||
/**
|
||||
* Encodes this [ShowdownRefreshInfoPacket] to the given [RegistryFriendlyByteBuf].
|
||||
*
|
||||
* @param buffer The buffer to write to.
|
||||
*/
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeString(status.name)
|
||||
buffer.writeResourceLocation(service)
|
||||
buffer.writeCollection(messages) { pb, value -> pb.writeString(value) }
|
||||
buffer.writeCollection(availableServices) { pb, value -> pb.writeResourceLocation(value) }
|
||||
buffer.writeDate(requestProcessedAt)
|
||||
buffer.writeBoolean(includesStartOfLog)
|
||||
}
|
||||
}
|
||||
+47
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -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(
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-4
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package com.cobblemon.mod.common.net.messages.server.debug.showdown
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Packet sent to request showdown information for a specific server.
|
||||
*
|
||||
* @property service The id of the showdown service to get showdown information for.
|
||||
* @author landonjw
|
||||
*/
|
||||
class RequestShowdownInfoPacket(val service: ResourceLocation, val lastRequest: Date?) : NetworkPacket<RequestShowdownInfoPacket> {
|
||||
override val id = ID
|
||||
|
||||
/**
|
||||
* Encodes this [RequestShowdownInfoPacket] to the given [RegistryFriendlyByteBuf].
|
||||
*
|
||||
* @param buffer The buffer to write to.
|
||||
*/
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(service)
|
||||
buffer.writeNullable(lastRequest) { pb, value -> pb.writeDate(value) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("request_showdown_info")
|
||||
|
||||
/**
|
||||
* Decodes a [RequestShowdownInfoPacket] from the given [RegistryFriendlyByteBuf].
|
||||
*
|
||||
* @param buffer The buffer to read from.
|
||||
* @return The decoded [RequestShowdownInfoPacket].
|
||||
*/
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = RequestShowdownInfoPacket(
|
||||
service = buffer.readResourceLocation(),
|
||||
lastRequest = buffer.readNullable { pb -> pb.readDate() }
|
||||
)
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package com.cobblemon.mod.common.net.messages.server.debug.showdown
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A network packet for sending action commands to the Showdown service.
|
||||
*
|
||||
* @property service The resource location of the Showdown service.
|
||||
* @property action The action command to be sent to the Showdown service.
|
||||
* @author landonjw
|
||||
*/
|
||||
class ShowdownActionPacket(val service: ResourceLocation, val action: Action) : NetworkPacket<ShowdownActionPacket> {
|
||||
override val id = ID
|
||||
|
||||
/**
|
||||
* Encodes the packet data into the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to encode the data into.
|
||||
*/
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(service)
|
||||
buffer.writeEnum(action)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("showdown_action_request")
|
||||
|
||||
/**
|
||||
* Decodes the packet data from the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to decode the data from.
|
||||
* @return The decoded ShowdownActionPacket.
|
||||
*/
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = ShowdownActionPacket(
|
||||
service = buffer.readResourceLocation(),
|
||||
action = buffer.readEnum(Action::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum representing possible actions for the Showdown service.
|
||||
*/
|
||||
enum class Action {
|
||||
START, STOP, RESTART, QUEUE
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package com.cobblemon.mod.common.net.messages.server.debug.showdown
|
||||
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readString
|
||||
import com.cobblemon.mod.common.util.writeString
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* A network packet for sending input commands to the Showdown service.
|
||||
*
|
||||
* @property service The resource location of the Showdown service.
|
||||
* @property input The input command to be sent to the Showdown service.
|
||||
* @author landonjw
|
||||
*/
|
||||
class ShowdownInputPacket(val service: ResourceLocation, val input: String) : NetworkPacket<ShowdownInputPacket> {
|
||||
override val id = ID
|
||||
|
||||
/**
|
||||
* Encodes the packet data into the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to encode the data into.
|
||||
*/
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(service)
|
||||
buffer.writeString(input)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("showdown_info_request")
|
||||
|
||||
/**
|
||||
* Decodes the packet data from the given buffer.
|
||||
*
|
||||
* @param buffer The buffer to decode the data from.
|
||||
* @return The decoded ShowdownInputPacket.
|
||||
*/
|
||||
fun decode(buffer: RegistryFriendlyByteBuf) = ShowdownInputPacket(
|
||||
service = buffer.readResourceLocation(),
|
||||
input = buffer.readString()
|
||||
)
|
||||
}
|
||||
}
|
||||
+29
-6
@@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
package com.cobblemon.mod.common.net.serverhandling.debug.showdown
|
||||
|
||||
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.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.debug.showdown.ShowdownRefreshInfoPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.RequestShowdownInfoPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Handles the reception of a [RequestShowdownInfoPacket] on the server side.
|
||||
* This handler retrieves the showdown information from the specified service
|
||||
* and sends it back to the client.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
object RequestShowdownInfoHandler : ServerNetworkPacketHandler<RequestShowdownInfoPacket> {
|
||||
|
||||
/**
|
||||
* Handles the received [RequestShowdownInfoPacket] by retrieving the showdown information
|
||||
* from the specified service and sending it back to the client in a [ShowdownRefreshInfoPacket].
|
||||
*
|
||||
* @param packet The received packet requesting showdown information.
|
||||
* @param server The Minecraft server instance.
|
||||
* @param player The player who sent the request.
|
||||
*/
|
||||
override fun handle(packet: RequestShowdownInfoPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
if (!Cobblemon.config.enableDebugKeys) return
|
||||
if (!ShowdownService.contains(packet.service)) return
|
||||
|
||||
val service = ShowdownService.get(packet.service)
|
||||
val messages = service.log.get(after = packet.lastRequest)
|
||||
val availableServices = ShowdownService.all().map { it.id }
|
||||
val includesStartOfLog = if (packet.lastRequest == null) true else service.log.isStartOfLog(messages)
|
||||
player.sendPacket(ShowdownRefreshInfoPacket(service.status, packet.service, messages, availableServices, Date(), includesStartOfLog))
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.cobblemon.mod.common.net.serverhandling.debug.showdown
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry.packTeam
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.ShowdownActionPacket
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* Handles network packets for Showdown actions on the server side.
|
||||
*
|
||||
* This handler processes incoming `ShowdownActionPacket` packets and performs the corresponding
|
||||
* actions (ie. restart it) on the specified Showdown service.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
object ShowdownActionHandler : ServerNetworkPacketHandler<ShowdownActionPacket> {
|
||||
|
||||
/**
|
||||
* Handles the received `ShowdownActionPacket`.
|
||||
*
|
||||
* @param packet The received packet containing the action to perform.
|
||||
* @param server The Minecraft server instance.
|
||||
* @param player The player who sent the packet.
|
||||
*/
|
||||
override fun handle(packet: ShowdownActionPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
if (!Cobblemon.config.enableDebugKeys) return
|
||||
if (!ShowdownService.contains(packet.service)) return
|
||||
|
||||
val service = ShowdownService.get(packet.service)
|
||||
when (packet.action) {
|
||||
ShowdownActionPacket.Action.START -> service.openConnection()
|
||||
ShowdownActionPacket.Action.STOP -> service.closeConnection()
|
||||
ShowdownActionPacket.Action.RESTART -> service.restartConnection()
|
||||
ShowdownActionPacket.Action.QUEUE -> {
|
||||
val cobblemonTeam = player.party().saveToJSON(JsonObject(), player.registryAccess())
|
||||
val showdownTeam = player.party().map { BattlePokemon(it, it.clone(false)) { entity -> entity.discard() } }
|
||||
showdownTeam.forEach {
|
||||
it.effectedPokemon.level = 100
|
||||
it.effectedPokemon.heal()
|
||||
}
|
||||
val showdownTeamStr = showdownTeam.packTeam()
|
||||
service.send(">queue-battle ${player.uuid} ${player.name.string} $cobblemonTeam@|@$showdownTeamStr")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package com.cobblemon.mod.common.net.serverhandling.debug.showdown
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.server.debug.showdown.ShowdownInputPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
/**
|
||||
* Handles the processing of `ShowdownInputPacket` on the server side.
|
||||
* This handler sends a command to the given showdown service. This is used for debugging purposes.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
object ShowdownInputHandler : ServerNetworkPacketHandler<ShowdownInputPacket> {
|
||||
|
||||
/**
|
||||
* Handles the received `ShowdownInputPacket`.
|
||||
*
|
||||
* @param packet The received packet containing the input command and service information.
|
||||
* @param server The Minecraft server instance.
|
||||
* @param player The player who sent the packet.
|
||||
*/
|
||||
override fun handle(packet: ShowdownInputPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
if (!Cobblemon.config.enableDebugKeys) return
|
||||
if (!ShowdownService.contains(packet.service)) return
|
||||
|
||||
val service = ShowdownService.get(packet.service)
|
||||
service.send(packet.input)
|
||||
}
|
||||
|
||||
}
|
||||
+12
-3
@@ -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")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@ abstract class BaseCobblemonHeldItemManager : HeldItemManager {
|
||||
|
||||
internal open fun load() {
|
||||
this.itemIds.clear()
|
||||
val itemsJson = ShowdownService.get().getItemIds()
|
||||
val itemsJson = ShowdownService.service.getItemIds()
|
||||
val showdownIds = hashSetOf<String>()
|
||||
for (i in 0 until itemsJson.size()) {
|
||||
showdownIds += itemsJson[i].asString
|
||||
@@ -95,4 +95,4 @@ abstract class BaseCobblemonHeldItemManager : HeldItemManager {
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
-46
@@ -11,7 +11,7 @@ package com.cobblemon.mod.common.pokemon.riding.controllers
|
||||
import com.bedrockk.molang.Expression
|
||||
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
|
||||
@@ -19,9 +19,7 @@ import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.riding.states.BirdAirState
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
@@ -32,9 +30,7 @@ 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.5 })
|
||||
.with(PoseOption(PoseType.FLY) { it.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override val condition: (PokemonEntity) -> Boolean = { true }
|
||||
|
||||
@@ -82,15 +78,15 @@ class BirdAirController : RideController {
|
||||
calculateRideSpaceVel(entity, driver, state)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
val rollable = driver as? Rollable
|
||||
if (rollable != null) {
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Need to deadzone this when straight up or down
|
||||
|
||||
upForce += -1.0 * sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
|
||||
forwardForce += cos(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
|
||||
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(rollable.pitch.toDouble())) * state.rideVel.y
|
||||
forwardForce += sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.y
|
||||
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.y
|
||||
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVel.y
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +109,7 @@ class BirdAirController : RideController {
|
||||
|
||||
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
|
||||
@@ -121,14 +117,14 @@ class BirdAirController : RideController {
|
||||
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
|
||||
val rotationChangeRate = 10.0
|
||||
|
||||
if (rollable != null) {
|
||||
var yawForce = rotationChangeRate * 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(rollable.roll.toDouble())))
|
||||
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
|
||||
|
||||
return Vec3(yawForce, pitchForce, 0.0)
|
||||
}
|
||||
@@ -146,8 +142,8 @@ 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)
|
||||
@@ -169,12 +165,12 @@ class BirdAirController : RideController {
|
||||
//Limit roll by non linearly decreasing inputs towards
|
||||
// a rotation limit based on the current distance from
|
||||
// that rotation limit
|
||||
if (abs(rollable.roll + rollForce) < rotLimit) {
|
||||
if (sign(rollForce) == sign(rollable.roll).toDouble()) {
|
||||
val d = abs(abs(rollable.roll) - rotLimit)
|
||||
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(rollForce) == sign(rollable.roll).toDouble()) {
|
||||
} else if (sign(rollForce) == sign(controller.roll).toDouble()) {
|
||||
rollForce = 0.0
|
||||
}
|
||||
|
||||
@@ -235,23 +231,23 @@ class BirdAirController : RideController {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeString(topSpeedExpr.toString())
|
||||
buffer.writeString(glideTopSpeedExpr.toString())
|
||||
buffer.writeString(accelExpr.toString())
|
||||
buffer.writeString(handlingExpr.toString())
|
||||
buffer.writeString(altitudeExpr.toString())
|
||||
buffer.writeString(infiniteStamina.toString())
|
||||
buffer.writeString(infiniteAltitude.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) {
|
||||
topSpeedExpr = buffer.readString().asExpression()
|
||||
glideTopSpeedExpr = buffer.readString().asExpression()
|
||||
accelExpr = buffer.readString().asExpression()
|
||||
handlingExpr = buffer.readString().asExpression()
|
||||
altitudeExpr = buffer.readString().asExpression()
|
||||
infiniteStamina = buffer.readString().asExpression()
|
||||
infiniteAltitude = buffer.readString().asExpression()
|
||||
topSpeedExpr = buffer.readExpression()
|
||||
glideTopSpeedExpr = buffer.readExpression()
|
||||
accelExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
altitudeExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
}
|
||||
|
||||
|
||||
@@ -273,8 +269,6 @@ class BirdAirController : RideController {
|
||||
* 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)
|
||||
@@ -305,10 +299,10 @@ class BirdAirController : RideController {
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
val rollable = driver as? Rollable
|
||||
if (rollable != null) {
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Base glide speed change on current pitch of the ride.
|
||||
glideSpeedChange = sin(Math.toRadians(rollable.pitch.toDouble()))
|
||||
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?
|
||||
@@ -353,8 +347,8 @@ class BirdAirController : RideController {
|
||||
//Vertical movement based on driver input.
|
||||
val vertTopSpeed = topSpeed / 2.0
|
||||
val vertInput = when {
|
||||
Minecraft.getInstance().options.keyJump.isDown() -> 1.0
|
||||
Minecraft.getInstance().options.keyShift.isDown() -> -1.0
|
||||
driver.jumping -> 1.0
|
||||
driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
@@ -399,4 +393,4 @@ class BirdAirController : RideController {
|
||||
state.rideVel.y,
|
||||
lerp( state.rideVel.z,0.0, topSpeed / ( 20.0 * 30.0)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-15
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-15
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-7
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-14
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-25
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-27
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -169,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)
|
||||
@@ -254,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 {
|
||||
|
||||
+52
-53
@@ -23,7 +23,6 @@ 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
|
||||
@@ -47,64 +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
|
||||
groundState.rideVel = flightState.rideVel
|
||||
groundState.stamina = flightState.stamina
|
||||
|
||||
getState(entity, ::RunUpToFlightCompositeState).let {
|
||||
it.activeController = landController
|
||||
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
|
||||
it.timeTransitioned = entity.level().gameTime
|
||||
}
|
||||
}
|
||||
else if (state.activeController == landController && Minecraft.getInstance().options.keyJump.isDown &&
|
||||
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 )
|
||||
flightState.currSpeed = groundState.currSpeed
|
||||
flightState.rideVel = groundState.rideVel
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -142,9 +144,6 @@ class RunUpToFlightCompositeController : RideController {
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
val controller = getActiveController(entity)
|
||||
val state = getState(entity, ::CompositeState)
|
||||
val isJumping = Minecraft.getInstance().options.keyJump.isDown
|
||||
|
||||
return controller.jumpForce(entity, driver, jumpStrength)
|
||||
}
|
||||
|
||||
@@ -212,4 +211,4 @@ class RunUpToFlightCompositeController : RideController {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("composite/run_up_to_flight")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-54
@@ -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,57 +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
|
||||
groundState.rideVel = flightState.rideVel
|
||||
|
||||
//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
|
||||
flightState.rideVel = groundState.rideVel
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.cobblemon.mod.common.util
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URI
|
||||
|
||||
object HttpService {
|
||||
|
||||
val gson: Gson = GsonBuilder().create()
|
||||
|
||||
inline fun <reified T> get(url: String): HttpResponse<T> {
|
||||
val connection = (URI.create(url).toURL().openConnection() as HttpURLConnection).also {
|
||||
it.requestMethod = "GET"
|
||||
it.connectTimeout = 5000
|
||||
it.readTimeout = 5000
|
||||
it.useCaches = false
|
||||
}
|
||||
return readFromUrlConnection<T>(connection)
|
||||
}
|
||||
|
||||
inline fun <reified T> post(url: String, body: Any? = null): HttpResponse<T> {
|
||||
val bodyJson = gson.toJson(body)
|
||||
val bodyBytes = bodyJson.toByteArray(Charsets.UTF_8)
|
||||
|
||||
val connection = (URI.create(url).toURL().openConnection() as HttpURLConnection).also {
|
||||
it.requestMethod = "POST"
|
||||
it.doOutput = true
|
||||
it.connectTimeout = 5000
|
||||
it.readTimeout = 5000
|
||||
it.useCaches = false
|
||||
it.setRequestProperty("Content-Type", "application/json; charset=UTF-8")
|
||||
it.setRequestProperty("Content-Length", bodyBytes.size.toString())
|
||||
}
|
||||
|
||||
connection.connect()
|
||||
connection.outputStream.use {
|
||||
it.write(bodyBytes)
|
||||
}
|
||||
|
||||
return readFromUrlConnection<T>(connection)
|
||||
}
|
||||
|
||||
inline fun <reified T> readFromUrlConnection(connection: HttpURLConnection): HttpResponse<T> {
|
||||
try {
|
||||
val status = connection.responseCode
|
||||
|
||||
if (status >= 400) {
|
||||
connection.errorStream.use {
|
||||
val error = readFromStream(it)
|
||||
return HttpResponse(error = HttpError(status, error), body = null)
|
||||
}
|
||||
}
|
||||
else {
|
||||
connection.inputStream.use {
|
||||
val bodyStr = readFromStream(it)
|
||||
return when {
|
||||
T::class == Unit::class -> HttpResponse(error = null, body = null)
|
||||
T::class == String::class -> HttpResponse(error = null, body = bodyStr as T)
|
||||
else -> HttpResponse(error = null, body = gson.fromJson(bodyStr, T::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
Cobblemon.LOGGER.error("Failed to read from connection url at ${connection.url}", e)
|
||||
return HttpResponse(error = HttpError(500, null), body = null)
|
||||
}
|
||||
}
|
||||
|
||||
fun readFromStream(stream: InputStream): String {
|
||||
ByteArrayOutputStream().use { result ->
|
||||
val buffer = ByteArray(1024)
|
||||
var length: Int
|
||||
while (stream.read(buffer).also { length = it } != -1) {
|
||||
result.write(buffer, 0, length)
|
||||
}
|
||||
return result.toString("UTF-8")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class HttpResponse<T>(val error: HttpError?, val body: T?)
|
||||
data class HttpError(val statusCode: Int, val details: String?)
|
||||
@@ -1253,7 +1253,6 @@
|
||||
"key.cobblemon.translateportraitleft": "Translate Portrait Left",
|
||||
"key.cobblemon.translateportraitright": "Translate Portrait Right",
|
||||
"key.cobblemon.printmodelsettings": "Print Model Settings",
|
||||
"key.cobblemon.toggleshowdowndebugui": "Toggle Showdown Debugger UI",
|
||||
"key.cobblemon.categories.cobblemon.debug": "Cobblemon Debug",
|
||||
|
||||
"cobblemon.subtitle.poke_ball.capture_succeeded": "Poké Ball capture succeeds",
|
||||
@@ -1562,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",
|
||||
@@ -7212,4 +7219,4 @@
|
||||
"cobblemon.ability.zenmode.desc": "Changes the Pokémon's shape when HP is half or less.",
|
||||
"cobblemon.ability.zerotohero": "Zero to Hero",
|
||||
"cobblemon.ability.zerotohero.desc": "The Pokémon transforms into its Hero Form when it switches out."
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"hitbox": "player",
|
||||
"presets": [
|
||||
"cobblemon:looks_in_one_direction"
|
||||
|
||||
],
|
||||
"variation": {
|
||||
"dirt": [
|
||||
@@ -105,4 +105,4 @@
|
||||
"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",
|
||||
|
||||
@@ -16,9 +16,9 @@ val bundle: Configuration by configurations.creating {
|
||||
loom {
|
||||
val clientConfig = runConfigs.getByName("client")
|
||||
clientConfig.runDir = "runClient"
|
||||
// clientConfig.programArg("--username=GaryOak")
|
||||
clientConfig.programArg("--username=AshKetchum")
|
||||
//This is AshKetchum's UUID so you get an Ash Ketchum skin
|
||||
// clientConfig.programArg("--uuid=93e4e551-589a-41cb-ab2d-435266c8e035")
|
||||
clientConfig.programArg("--uuid=93e4e551-589a-41cb-ab2d-435266c8e035")
|
||||
val serverConfig = runConfigs.getByName("server")
|
||||
serverConfig.runDir = "runServer"
|
||||
}
|
||||
@@ -59,4 +59,4 @@ tasks {
|
||||
dependsOn(copyJar)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user