Compare commits

..
Author SHA1 Message Date
Landon Wills 09a0cd6c30 Start restructure of camera transformations 2025-04-27 07:46:20 -04:00
564 changed files with 2276 additions and 4268 deletions

No files matched your search

-1
View File
@@ -165,7 +165,6 @@
- Fixed certain Pokémon with forms not having appropriate stock Pokédex entries.
- Fixed issue with Pokédex Scanner that caused the open/close overlay to have the wrong opacity values
- Fixed dragon's breath not being usable on the restoration tank when it should be
- Fixed some effects like particles from fishing rods appearing for players in the same coordinates in another world.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.api.riding.Rideable;
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
@@ -21,6 +20,7 @@ import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
@@ -35,30 +35,49 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Camera.class)
public abstract class CameraMixin {
@Shadow private Entity entity;
@Shadow @Final private Quaternionf rotation;
@Shadow @Final private Vector3f forwards;
@Shadow @Final private Vector3f up;
@Shadow @Final private Vector3f left;
@Shadow private float xRot;
@Shadow private float yRot;
@Shadow
private Entity entity;
@Shadow
@Final
private Quaternionf rotation;
@Shadow
@Final
private Vector3f forwards;
@Shadow
@Final
private Vector3f up;
@Shadow
@Final
private Vector3f left;
@Shadow
private float xRot;
@Shadow
private float yRot;
@Shadow private float eyeHeight;
@Shadow private float eyeHeightOld;
@Shadow
private float eyeHeight;
@Shadow
private float eyeHeightOld;
@Shadow protected abstract void setPosition(Vec3 pos);
@Shadow
protected abstract void setPosition(Vec3 pos);
@Unique private float returnTimer = 0;
@Unique private float rollAngleStart = 0;
@Unique Minecraft minecraft = Minecraft.getInstance();
@Unique
private float returnTimer = 0;
@Unique
private float rollAngleStart = 0;
@Unique
Minecraft minecraft = Minecraft.getInstance();
@Unique
boolean disableRollableCameraDebug = false;
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
public void cobblemon$setRotation(float f, float g, CallbackInfo ci) {
if (!(this.entity instanceof OrientationControllable controllable) || Cobblemon.config.getDisableRoll()) return;
if (!(this.entity instanceof OrientationControllable controllable) || disableRollableCameraDebug) return;
var controller = controllable.getOrientationController();
if (!controller.isActive() && controller.getOrientation() != null) {
if(this.returnTimer < 1) {
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.
controller.getOrientation().set(
@@ -66,16 +85,17 @@ public abstract class CameraMixin {
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
);
if(rollAngleStart == 0){
if (rollAngleStart == 0) {
this.returnTimer = 1;
controller.reset();
return;
}
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart * (1 - returnTimer)));
cobblemon$applyRotation();
this.returnTimer += .05F;
ci.cancel();
} else {
}
else {
this.returnTimer = 1;
controller.reset();
}
@@ -89,26 +109,16 @@ public abstract class CameraMixin {
ci.cancel();
}
@Inject(method = "setup", at = @At("HEAD"))
private void cobblemon$tickAnimationPreRenders(BlockGetter level, Entity entity, boolean detached, boolean thirdPersonReverse, float partialTick, CallbackInfo ci) {
MountedPokemonAnimationRenderController.INSTANCE.reset();
}
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
public void cobblemon$positionCamera(Camera instance, double x, double y, double z, Operation<Void> original, @Local(ordinal = 1, argsOnly = true) boolean thirdPersonReverse) {
Entity entity = instance.getEntity();
Entity vehicle = entity.getVehicle();
MountedPokemonAnimationRenderController.INSTANCE.reset();
if(vehicle instanceof Rideable){
Vec3 position = MountedCameraRenderer.INSTANCE.getCameraPosition(
instance,
entity,
vehicle,
x,
y,
z,
thirdPersonReverse,
eyeHeightOld,
eyeHeight
);
if(position != null){
if (cobblemon$isRidingPokemon(entity)) {
Vec3 position = MountedCameraRenderer.INSTANCE.getCameraPosition(instance, entity, entity.getVehicle(), thirdPersonReverse, eyeHeightOld, eyeHeight);
if (position != null) {
setPosition(position);
return;
}
@@ -118,19 +128,25 @@ public abstract class CameraMixin {
}
@Unique
private void cobblemon$applyRotation(){
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null) return;
private boolean cobblemon$isRidingPokemon(Entity entity) {
return entity.getVehicle() instanceof PokemonEntity;
}
@Unique
private void cobblemon$applyRotation() {
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));
newRotation.rotateY((float) Math.toRadians(180));
}
this.rotation.set(newRotation);
this.xRot = controller.getPitch();
this.yRot = controller.getYaw();
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
this.forwards.set(controller.getForwardVector());
this.up.set(controller.getUpVector());
this.left.set(controller.getLeftVector());
}
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V", ordinal = 0))
@@ -138,7 +154,7 @@ public abstract class CameraMixin {
Entity entity = instance.getEntity();
Entity vehicle = entity.getVehicle();
if (!(vehicle instanceof PokemonEntity) || Cobblemon.config.getThirdPersonViewBobbing()) {
if (!(vehicle instanceof PokemonEntity) || Cobblemon.config.getViewBobTranslation()) {
original.call(instance, zoom, dy, dx);
}
}
@@ -9,20 +9,10 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker;
import com.cobblemon.mod.common.stadium.BattleCameraManagerClient;
import com.cobblemon.mod.common.util.CameraExtensionsKt;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -30,17 +20,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public class GameRendererMixin {
@Shadow @Final Minecraft minecraft;
@Shadow float renderDistance;
@Shadow @Final private LightTexture lightTexture;
@Shadow private boolean renderHand;
@Shadow private double getFov(Camera activeRenderInfo, float partialTicks, boolean useFOVSetting) { return 0.0; }
@Shadow public void resetProjectionMatrix(Matrix4f matrix) {}
@Shadow public Matrix4f getProjectionMatrix(double fov) { return new Matrix4f(); }
@Shadow private void renderItemInHand(Camera camera, float partialTick, Matrix4f projectionMatrix) {}
@Shadow public void pick(float partialTicks) {}
@Unique
long lastTime = -1;
@@ -63,44 +42,4 @@ public class GameRendererMixin {
lastTime = newTime;
}
@Inject(method = "renderLevel", at = @At(value = "HEAD"), cancellable = true)
public void renderLevel(DeltaTracker deltaTracker, CallbackInfo ci) {
if (BattleCameraManagerClient.INSTANCE.shouldUseStadiumCamera()) {
float f = deltaTracker.getGameTimeDeltaPartialTick(true);
this.lightTexture.updateLightTexture(f);
if (this.minecraft.getCameraEntity() == null) {
this.minecraft.setCameraEntity(this.minecraft.player);
}
this.minecraft.getProfiler().push("camera");
Camera stadiumCamera = new Camera();
Entity entity = (Entity) this.minecraft.player;
stadiumCamera.setup(this.minecraft.level, entity, true, false, 1.0F);
//adjust camera
BattleCameraManagerClient.INSTANCE.updateCameraData(f);
stadiumCamera.setPosition(BattleCameraManagerClient.INSTANCE.getCameraPosition());
Vector3f rotations = BattleCameraManagerClient.INSTANCE.getCameraRotation();
CameraExtensionsKt.setRotation(stadiumCamera, rotations.x, rotations.y, rotations.z);
this.renderDistance = (float)(this.minecraft.options.getEffectiveRenderDistance() * 16);
double d = this.getFov(stadiumCamera, f, true);
Matrix4f matrix4f = this.getProjectionMatrix(d);
this.resetProjectionMatrix(matrix4f);
Quaternionf quaternionf = stadiumCamera.rotation().conjugate(new Quaternionf());
Matrix4f matrix4f2 = (new Matrix4f()).rotation(quaternionf);
this.minecraft.levelRenderer.prepareCullFrustum(stadiumCamera.getPosition(), matrix4f2, this.getProjectionMatrix(Math.max(d, (double)(Integer)this.minecraft.options.fov().get())));
this.minecraft.levelRenderer.renderLevel(deltaTracker, false, stadiumCamera, (GameRenderer) (Object) this, this.lightTexture, matrix4f2, matrix4f);
this.minecraft.getProfiler().pop();
ci.cancel();
}
}
@Inject(method = "tick", at = @At(value = "TAIL"))
public void tick(CallbackInfo ci) {
BattleCameraManagerClient.INSTANCE.tickCameraManager();
}
}
@@ -1,46 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.render.TextClipping;
import net.minecraft.client.gui.Font;
import net.minecraft.network.chat.Style;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/**
* This mixin facilitates the gradual reveal of text with NPC dialogue. If there is a better way
* to do this, I'd love to hear it.
*
* The idea is that somewhere static in the mod we have a control for how many characters can be drawn
* and how many have been drawn so far, and this mixin will add to the latter and stop drawing characters
* once it reaches the former.
*
* This relies STRONGLY on the code that sets the character cap to reset it to -1 after the text is rendered,
* otherwise all text in Minecraft will stop drawing.
*
* Sites that take advantage of this mixin will do the following:
* 1: Set the character cap, which resets the drawn counter to zero.
* 2: Call whatever Minecraft function draws your text.
* 3: Reset the character cap to -1, which will allow regular text to draw again.
*
* @author Hiroku
* @since April 18th, 2025
*/
@Mixin(Font.StringRenderOutput.class)
public class StringRenderOutputMixin {
@Inject(method = "accept", at = @At(value = "HEAD"), cancellable = true)
public void onAccept(int i, Style style, int j, CallbackInfoReturnable<Boolean> cir) {
if (!TextClipping.canDrawAnotherCharacter()) {
cir.setReturnValue(true);
}
}
}
@@ -37,9 +37,6 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.of(::PCBlockEntity, CobblemonBlocks.PC).build(null))
@JvmField
val STADIUM: BlockEntityType<StadiumBlockEntity> = this.create("stadium_block", BlockEntityType.Builder.of(::StadiumBlockEntity, CobblemonBlocks.STADIUM_BLOCK).build(null))
@JvmField
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@@ -482,9 +482,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }))
@JvmField
val STADIUM_BLOCK = create("stadium_block", StadiumBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F)))
@JvmField
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava()))
@@ -619,8 +619,6 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val PC = blockItem("pc", CobblemonBlocks.PC)
@JvmField
val STADIUM_BLOCK = blockItem("stadium_block", CobblemonBlocks.STADIUM_BLOCK)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@@ -225,10 +225,6 @@ import com.cobblemon.mod.common.net.serverhandling.trade.ChangeTradeAcceptanceHa
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.stadium.BattleCamerasSyncPacket
import com.cobblemon.mod.common.stadium.BattleCameraSetupHandler
import com.cobblemon.mod.common.stadium.BattleCameraPacket
import com.cobblemon.mod.common.stadium.BattleCameraManagersSyncPacket
import com.cobblemon.mod.common.util.server
import net.minecraft.server.level.ServerPlayer
@@ -386,8 +382,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SeasoningRegistrySyncPacket.ID, SeasoningRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(FlowRegistrySyncPacket.ID, FlowRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BattleCamerasSyncPacket.ID, BattleCamerasSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BattleCameraManagersSyncPacket.ID, BattleCameraManagersSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
@@ -448,9 +442,6 @@ object CobblemonNetwork {
// Debug / cheats
list.add(PacketRegisterInfo(CalculateSeatPositionsPacket.ID, CalculateSeatPositionsPacket::decode, CalculateSeatPositionsHandler))
// Stadium packets
list.add(PacketRegisterInfo(BattleCameraPacket.ID, BattleCameraPacket::decode, BattleCameraSetupHandler))
// Riding
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
@@ -39,10 +39,6 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
val INCENSE_SWEET_KEY: ResourceKey<PoiType> = createKey("saccharine_honey_log")
@JvmField
val INCENSE_SWEET = create(INCENSE_SWEET_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.INCENSE_SWEET), 0, 1))
@JvmField
val STADIUM_BLOCK_KEY: ResourceKey<PoiType> = createKey("stadium_block")
@JvmField
val STADIUM_BLOCK = create(STADIUM_BLOCK_KEY.location().path, PoiType(getBlockStates(CobblemonBlocks.STADIUM_BLOCK), 0, 1))
private fun createKey(string: String): ResourceKey<PoiType> = ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string))
@@ -43,18 +43,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val PC_WALLPAPER_UNLOCK = this.create("pc.wallpaper.unlock")
@JvmField
val NPC_GIBBER_GENERIC = this.create("entity.npc.gibber.generic")
@JvmField
val NPC_GIBBER_SPEECH_TYPE_1_1 = this.create("entity.npc.gibber.speech_type_1_1")
@JvmField
val NPC_GIBBER_SPEECH_TYPE_1_2 = this.create("entity.npc.gibber.speech_type_1_2")
@JvmField
val NPC_GIBBER_SPEECH_TYPE_1_3 = this.create("entity.npc.gibber.speech_type_1_3")
@JvmField
val VILLAGER_WORK_NURSE = this.create("entity.villager.work_nurse")
@JvmField
val HEALING_MACHINE_ACTIVE = this.create("block.healing_machine.active")
@@ -470,4 +460,4 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.createVariableRangeEvent(cobblemonResource(name)))
}
}
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.dialogue
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
/**
* Settings for the gibbering sound effect that plays gradually as dialogue appears.
*
* @author Hiroku, landonjw
* @since April 19th, 2025
*/
class DialogueGibber(
/** Whether text will gradually appear while the gibbering occurs. */
val graduallyShowText: Boolean = true,
/** Whether clicking will make the gibber skip to the end. */
val allowSkip: Boolean = true,
/** The space between gibbers in terms of characters. 'Holy cow' would play 2 sounds if step = 4. */
val step: Int = 4,
/** The time (in seconds) between gibbers. The smaller this is, the faster the text will progress. */
val interval: Double = 0.1,
val minPitch: Float = 0.9F,
val maxPitch: Float = 1.1F,
val minVolume: Float = 0.9F,
val maxVolume: Float = 1.0F,
/** The possible sounds to play when gibbering. */
val sounds: List<ResourceLocation> = listOf(
cobblemonResource("entity.npc.gibber.generic")
)
)
@@ -31,7 +31,6 @@ class DialoguePage(
var lines: MutableList<DialogueText> = mutableListOf(),
var input: DialogueInput = DialogueNoInput(),
var background: ResourceLocation? = null,
var gibber: DialogueGibber? = null,
var clientActions: MutableList<Expression> = mutableListOf(),
var escapeAction: DialogueAction? = null,
) {
@@ -43,7 +42,6 @@ class DialoguePage(
speaker: String? = null,
lines: Iterable<MutableComponent>,
input: DialogueInput = DialogueNoInput(),
gibber: DialogueGibber? = null,
background: ResourceLocation? = null,
clientActions: Iterable<Expression> = emptyList(),
/** The thing to do when the player presses ESC while on this page. If null, falls back to the same property on [Dialogue]. */
@@ -54,7 +52,6 @@ class DialoguePage(
speaker = speaker,
lines = lines.map { WrappedDialogueText(it) }.toMutableList(),
input = input,
gibber = gibber,
background = background,
clientActions = clientActions.toMutableList(),
escapeAction = escapeAction?.let { func -> FunctionDialogueAction { activeDialogue, _ -> func(activeDialogue) } }
@@ -19,16 +19,7 @@ import net.minecraft.network.chat.MutableComponent
*/
class DialogueSpeaker(
val name: DialogueText? = null,
val face: DialogueFaceProvider? = null,
val gibber: DialogueGibber? = null
val face: DialogueFaceProvider? = null
) {
fun of(
name: MutableComponent = "".text(),
face: DialogueFaceProvider? = null,
gibber: DialogueGibber? = null
) = DialogueSpeaker(
name = WrappedDialogueText(name),
face = face,
gibber = gibber
)
fun of(name: MutableComponent = "".text(), face: DialogueFaceProvider? = null) = DialogueSpeaker(WrappedDialogueText(name), face)
}
@@ -76,8 +76,6 @@ interface NetworkPacket<T: NetworkPacket<T>> : CustomPacketPayload, Encodable {
server.playerList.players.filter { player ->
if (exclusionCondition.invoke(player))
return@filter false
if (player.level().dimension() != worldKey)
return@filter false
val xDiff = x - player.x
val yDiff = y - player.y
val zDiff = z - player.z
@@ -1,132 +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.block
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.block.entity.StadiumBlockEntity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.Mirror
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.phys.BlockHitResult
class StadiumBlock(properties: Properties): BaseEntityBlock(properties) {
companion object {
val CODEC = simpleCodec(::StadiumBlock)
}
init {
registerDefaultState(stateDefinition.any().setValue(HorizontalDirectionalBlock.FACING, Direction.NORTH))
}
override fun codec() = CODEC
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return StadiumBlockEntity(pos, state)
}
override fun getStateForPlacement(blockPlaceContext: BlockPlaceContext): BlockState {
return this.defaultBlockState().setValue(HorizontalDirectionalBlock.FACING, blockPlaceContext.horizontalDirection)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(HorizontalDirectionalBlock.FACING)
}
override fun rotate(blockState: BlockState, rotation: Rotation): BlockState {
return blockState.setValue(
HorizontalDirectionalBlock.FACING, rotation.rotate(blockState.getValue(
HorizontalDirectionalBlock.FACING)))
}
override fun mirror(blockState: BlockState, mirror: Mirror): BlockState {
return blockState.rotate(mirror.getRotation(blockState.getValue(HorizontalDirectionalBlock.FACING)))
}
override fun onRemove(state: BlockState, world: Level, pos: BlockPos, newState: BlockState, moved: Boolean) {
if (!state.`is`(newState.block)) super.onRemove(state, world, pos, newState, moved)
}
//TODO: override getTicker
override fun getRenderShape(state: BlockState) = RenderShape.MODEL
override fun useWithoutItem(
state: BlockState,
world: Level,
pos: BlockPos,
player: Player,
hitResult: BlockHitResult
): InteractionResult {
if (player !is ServerPlayer) return InteractionResult.SUCCESS
val blockEntity = world.getBlockEntity(pos)
if (blockEntity !is StadiumBlockEntity) return InteractionResult.SUCCESS
player.sendSystemMessage(text("Adding you to Battle").red())
return InteractionResult.SUCCESS
}
/*
override fun useWithoutItem(
blockState: BlockState,
world: Level,
blockPos: BlockPos,
player: Player,
blockHitResult: BlockHitResult
): InteractionResult {
if (player !is ServerPlayer) return InteractionResult.SUCCESS
val blockEntity = world.getBlockEntity(blockPos)
if (blockEntity !is StadiumBlockEntity) return InteractionResult.SUCCESS
// Toggle isInUse state
blockEntity.isInUse = !blockEntity.isInUse
// debug
val activateMessage = if (blockEntity.isInUse) "Stadium Activated!" else "Stadium Deactivated!"
player.sendSystemMessage(text(activateMessage).red())
if (blockEntity.isInUse) {
// Generate flight path when activated
blockEntity.generateFlightPath(world, radiusX = 5.0, radiusZ = 3.0, height = 10.0, steps = 20)
} else {
// Clear path when deactivated
blockEntity.pathPoints.clear()
}
val message = "Stadium Pos: " + blockEntity.blockPos +
"\nPlayer 1 Pos: " + blockEntity.getPlayer1Pos() +
"\nPoke 1 Pos: " + blockEntity.getPoke1Pos() +
"\nPlayer 2 Pos: " + blockEntity.getPlayer2Pos() +
"\nPoke 2 Pos: " + blockEntity.getPoke2Pos()
player.sendSystemMessage(text(message).red())
blockEntity.setChanged()
world.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
return InteractionResult.SUCCESS
}
*/
}
@@ -1,149 +0,0 @@
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneState
import com.cobblemon.mod.common.stadium.StadiumSingleBattle
import net.minecraft.core.BlockPos
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
class StadiumBlockEntity(
pos: BlockPos,
blockState: BlockState
): BlockEntity(
CobblemonBlockEntities.STADIUM,
pos,
blockState
) {
/** Battle area center is bottom center */
var battleArea = Vector3f(10f,10f,10f) // Width, Height, Length
var battleCenter = Vec3.ZERO // Local
var battleRot = 70F
fun positionInBattleArea(targetPos: Vector3f) : Boolean {
val adjustedPos = targetPos.sub(blockPos.bottomCenter.toVector3f()).sub(battleCenter.toVector3f())
if (targetPos.y < 0f || targetPos.y >= battleArea.y) return false
adjustedPos.rotateY(-battleRot)
if (targetPos.x < -battleArea.x || targetPos.x > battleArea.x) return false
if (targetPos.z < -battleArea.z || targetPos.z > battleArea.z) return false
return true
}
/*
var isInUse = false
val droneState = DroneState()
val pathPoints: MutableList<Vec3> = mutableListOf()
fun generateFlightPath(world: Level, radiusX: Double, radiusZ: Double, height: Double, steps: Int) {
pathPoints.clear()
for (i in 0 until steps) {
val angle = (i.toDouble() / steps) * Math.PI * 2
val xOffset = radiusX * Math.cos(angle)
val zOffset = radiusZ * Math.sin(angle)
val basePos = Vec3(blockPos.x + xOffset, blockPos.y + height, blockPos.z + zOffset)
val adjustedPos = getValidPoint(world, basePos)
pathPoints.add(adjustedPos)
}
}
private fun getValidPoint(world: Level, start: Vec3): Vec3 {
val maxHeight = 40 // Max height for drone path
val stepHeight = 1.0 // Step size for raycast
var adjustedHeight = start.y
for (h in 0..maxHeight step stepHeight.toInt()) {
val checkPos = BlockPos(start.x.toInt(), start.y.toInt() - h, start.z.toInt()) // Convert to BlockPos
if (!world.getBlockState(checkPos).isAir) { // Check if block is solid
return Vec3(start.x, start.y - h - 1, start.z) // Adjust position
}
}
return start // No obstruction found
}
fun getPlayer1Pos(): BlockPos {
return blockPos.north(10)
}
fun getPlayer2Pos(): BlockPos {
return blockPos.south(10)
}
fun getPoke1Pos(): BlockPos {
return blockPos.north(5)
}
fun getPoke2Pos(): BlockPos {
return blockPos.south(5)
}
/**
* Saves data to NBT.
*/
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.saveAdditional(tag, registries)
// Save the isInUse state
tag.putBoolean("IsInUse", isInUse)
// Save path points
val pathTag = ListTag()
for (point in pathPoints) {
val pointTag = CompoundTag()
pointTag.putDouble("x", point.x)
pointTag.putDouble("y", point.y)
pointTag.putDouble("z", point.z)
pathTag.add(pointTag)
}
tag.put("PathPoints", pathTag)
}
/**
* Loads data from NBT.
*/
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
// Load the isInUse state
isInUse = tag.getBoolean("IsInUse")
// Load path points
pathPoints.clear()
val pathTag = tag.getList("PathPoints", 10) // 10 is the ID for CompoundTag
for (i in 0 until pathTag.size) {
val pointTag = pathTag.getCompound(i)
val point = Vec3(pointTag.getDouble("x"), pointTag.getDouble("y"), pointTag.getDouble("z"))
pathPoints.add(point)
}
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
//println("getUpdatePacket called for NestBlockEntity at $blockPos with egg: $eggComponent")
return ClientboundBlockEntityDataPacket.create(this)
}
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
val tag = saveWithoutMetadata(registryLookup)
//println("getUpdateTag called for NestBlockEntity at $blockPos with tag: $tag")
return tag
}
*/
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonMenuType.COOKING_POT
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
@@ -53,7 +54,6 @@ import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
import com.cobblemon.mod.common.stadium.BattleCameraManagerClient
import com.cobblemon.mod.common.util.isLookingAt
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
@@ -68,10 +68,14 @@ import net.minecraft.client.renderer.blockentity.SignRenderer
import net.minecraft.client.renderer.entity.EntityRenderer
import net.minecraft.client.renderer.entity.LivingEntityRenderer
import net.minecraft.client.resources.PlayerSkin
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.resources.ResourceManager
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.AABB
import java.util.UUID
import net.minecraft.client.player.AbstractClientPlayer
import net.minecraft.server.level.ServerPlayer
object CobblemonClient {
@@ -107,7 +111,6 @@ object CobblemonClient {
CobblemonDataProvider.canReload = true
DeferredRenderer.clearAll()
ClientPlayerIcon.clear()
BattleCameraManagerClient.onLogout()
}
fun initialize(implementation: CobblemonClientImplementation) {
@@ -257,8 +260,6 @@ object CobblemonClient {
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
CobblemonBlocks.GIMMIGHOUL_CHEST,
CobblemonBlocks.DISPLAY_CASE,
CobblemonBlocks.LECTERN,
CobblemonBlocks.STADIUM_BLOCK,
CobblemonBlocks.SACCHARINE_DOOR,
CobblemonBlocks.SACCHARINE_TRAPDOOR,
CobblemonBlocks.SACCHARINE_SIGN,
@@ -277,6 +278,7 @@ object CobblemonClient {
CobblemonBlocks.RED_CAMPFIRE_POT,
CobblemonBlocks.WHITE_CAMPFIRE_POT,
CobblemonBlocks.YELLOW_CAMPFIRE_POT
)
this.createBoatModelLayers()
@@ -314,7 +316,6 @@ object CobblemonClient {
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.STADIUM, ::StadiumBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LURE_CAKE, ::CakeBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_CAKE, ::CakeBlockEntityRenderer)
@@ -8,11 +8,6 @@
package com.cobblemon.mod.common.client.gui.dialogue
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueGibberDTO
import net.minecraft.network.chat.MutableComponent
class DialogueRenderableSpeaker(
val name: MutableComponent?,
val face: RenderableFace?,
val gibber: DialogueGibberDTO? = null
)
class DialogueRenderableSpeaker(val name: MutableComponent?, val face: RenderableFace?)
@@ -10,71 +10,43 @@ package com.cobblemon.mod.common.client.gui.dialogue
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.dialogue.ArtificialDialogueFaceProvider
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
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.api.net.NetworkPacket
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueBox
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialoguePortraitWidget
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueNameWidget
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueOptionWidget
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialoguePortraitWidget
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueTextInputWidget
import com.cobblemon.mod.common.client.gui.dialogue.widgets.DialogueTimerWidget
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueDTO
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueGibberDTO
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueInputDTO
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.asExpressions
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isInventoryKeyPressed
import com.cobblemon.mod.common.util.nextBetween
import com.cobblemon.mod.common.util.resolve
import kotlin.random.Random
import com.cobblemon.mod.common.util.*
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.sounds.SoundEvent
class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTranslated()), CobblemonRenderable {
val speakers = dialogueDTO.speakers?.mapNotNull { (key, value) ->
val name = value.name
when (val face = value.face) {
is ArtificialDialogueFaceProvider -> key to DialogueRenderableSpeaker(
name = name,
face = ArtificialRenderableFace(face.modelType, face.identifier, face.aspects, face.isLeftSide),
gibber = value.gibber
)
is PlayerDialogueFaceProvider -> key to DialogueRenderableSpeaker(
name = name,
face = PlayerRenderableFace(
playerId = face.playerId,
isLeftSide = face.isLeftSide
),
gibber = value.gibber
)
is ArtificialDialogueFaceProvider -> key to DialogueRenderableSpeaker(name, ArtificialRenderableFace(face.modelType, face.identifier, face.aspects, face.isLeftSide))
is PlayerDialogueFaceProvider -> key to DialogueRenderableSpeaker(name, PlayerRenderableFace(face.playerId, face.isLeftSide))
is ReferenceDialogueFaceProvider -> {
key to DialogueRenderableSpeaker(
name = name,
face = ReferenceRenderableFace(
entity = Minecraft.getInstance().level?.getEntity(face.entityId) as? PosableEntity ?: return@mapNotNull null,
isLeftSide = face.isLeftSide
),
gibber = value.gibber
face = ReferenceRenderableFace(Minecraft.getInstance().level?.getEntity(face.entityId) as? PosableEntity ?: return@mapNotNull null, face.isLeftSide)
)
}
else -> key to DialogueRenderableSpeaker(name = name, face = null, gibber = value.gibber)
else -> key to DialogueRenderableSpeaker(name, null)
}
}?.toMap() ?: emptyMap()
val runtime = MoLangRuntime().setup().setupClient()
@@ -91,18 +63,11 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
lateinit var dialogueNameWidget: DialogueNameWidget
lateinit var dialoguePortraitWidget: DialoguePortraitWidget
var dialogueStartTick = 0
val scaledWidth
get() = this.minecraft!!.window.guiScaledWidth
val scaledHeight
get() = this.minecraft!!.window.guiScaledHeight
var expressionsToRun = listOf<ExpressionLike>()
var gibberDone = false
var gibber: DialogueGibberDTO? = null
var gibberSounds = listOf<SoundEvent>()
companion object {
const val BOX_WIDTH = 196
const val BOX_HEIGHT = 74
@@ -129,15 +94,13 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
private val buttonResource = cobblemonResource("textures/gui/dialogue/dialogue_button.png")
private val buttonFullWidthResource = cobblemonResource("textures/gui/dialogue/dialogue_button_full.png")
val dialogueMoLangFunctions = mutableListOf<(DialogueScreen) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val dialogueMolangFunctions = mutableListOf<(DialogueScreen) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ dialogueScreen ->
return@mutableListOf hashMapOf(
"face" to java.util.function.Function {
val face = dialogueScreen.dialogueDTO.currentPageDTO.speaker?.let { dialogueScreen.speakers[it] }?.face ?: return@Function DoubleValue.ZERO
return@Function face.struct
},
"face" to java.util.function.Function { Unit },
)
}
// idk something maybe, stuff for accessing the current 'face' to modify it?
)
}
@@ -148,15 +111,14 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
override fun init() {
super.init()
runtime.environment.query.addFunctions(
functions = dialogueMoLangFunctions.flatMap { it(this@DialogueScreen).entries }.associate { it.key to it.value }
runtime.environment.query
.addFunctions(
dialogueMolangFunctions
.flatMap { it(this@DialogueScreen).entries }
.associate { it.key to it.value }
)
val currentSpeaker = dialogueDTO.currentPageDTO.speaker
gibber = dialogueDTO.currentPageDTO.gibber ?: currentSpeaker?.let { speakers[it]?.gibber }
gibberSounds = gibber?.sounds?.mapNotNull { BuiltInRegistries.SOUND_EVENT.get(it) } ?: emptyList()
expressionsToRun = dialogueDTO.currentPageDTO.clientActions.map { it.asExpressionLike() }
dialogueDTO.currentPageDTO.clientActions.forEach { runtime.resolve(it.asExpressionLike()) }
val centerX = scaledWidth / 2F
val boxMinY = (scaledHeight / 2F) - (BOX_HEIGHT / 2F) - 30
val boxMaxY = boxMinY + BOX_HEIGHT
@@ -239,34 +201,18 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
setInitialFocus(dialogueTextInputWidget)
}
this.dialogueStartTick = minecraft!!.player!!.tickCount
dialogueDTO.currentPageDTO.clientActions.flatMap(String::asExpressions).resolve(runtime)
}
var gibberIndex = 0
var timeElapsedSinceGibber = 0.0
override fun render(GuiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val gibber = gibber
if (gibber != null) {
playGibberSpeak(
delta = delta,
text = dialogueDTO.currentPageDTO.lines.joinToString { it.string },
gibber = gibber
)
}
remainingSeconds -= delta / 20F
dialogueTimerWidget.ratio = if (remainingSeconds <= 0) -1F else remainingSeconds / dialogueDTO.dialogueInput.deadline
super.render(GuiGraphics, mouseX, mouseY, delta)
expressionsToRun.forEach { runtime.resolve(it) }
expressionsToRun = emptyList()
}
override fun isPauseScreen() = false
fun update(dialogueDTO: DialogueDTO) {
gibberIndex = 0
gibberDone = false
this.dialogueDTO = dialogueDTO
this.remainingSeconds = dialogueDTO.dialogueInput.deadline
waitingForServerUpdate = false
@@ -290,31 +236,4 @@ class DialogueScreen(var dialogueDTO: DialogueDTO) : Screen("gui.dialogue".asTra
return super.keyPressed(keyCode, scanCode, modifiers)
}
fun playGibberSpeak(delta: Float, text: String, gibber: DialogueGibberDTO) {
timeElapsedSinceGibber += delta / 20
if (timeElapsedSinceGibber < gibber.interval) {
return
}
timeElapsedSinceGibber = 0.0
if (gibberIndex >= text.length) {
gibberDone = true
return
}
val gibberChar = text[gibberIndex]
gibberIndex += gibber.step
if (gibberSounds.isNotEmpty()) {
val pitchRange = gibber.maxPitch * 100 - gibber.minPitch * 100
val gibberPitch = (gibberChar.hashCode() % pitchRange + gibber.minPitch * 100) / 100
val volume = Random.nextBetween(gibber.minVolume, gibber.maxVolume)
val gibberSound = gibberSounds[gibberChar.hashCode() % gibberSounds.size]
minecraft?.soundManager?.play(SimpleSoundInstance.forUI(gibberSound, gibberPitch, volume))
}
}
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.client.gui.dialogue
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.drawPosablePortrait
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
@@ -35,12 +34,10 @@ import org.joml.Vector3f
*/
sealed interface RenderableFace {
val isLeftSide: Boolean
val struct: QueryStruct
fun render(GuiGraphics: GuiGraphics, partialTicks: Float)
}
class PlayerRenderableFace(val playerId: UUID, override val isLeftSide: Boolean) : RenderableFace {
override val struct: QueryStruct = QueryStruct(hashMapOf())
override fun render(GuiGraphics: GuiGraphics, partialTicks: Float) {
val entity = Minecraft.getInstance().level?.getPlayerByUUID(playerId) ?: return
// All of the maths below is shamelessly stolen from InventoryScreen.drawEntity.
@@ -77,8 +74,6 @@ class PlayerRenderableFace(val playerId: UUID, override val isLeftSide: Boolean)
class ReferenceRenderableFace(val entity: PosableEntity, override val isLeftSide: Boolean): RenderableFace {
val state = entity.delegate as PosableState
override val struct: QueryStruct
get() = state.runtime.environment.query
override fun render(GuiGraphics: GuiGraphics, partialTicks: Float) {
val state = this.state
if (state is PokemonClientDelegate) {
@@ -117,8 +112,6 @@ class ArtificialRenderableFace(
override val isLeftSide: Boolean
): RenderableFace {
val state = FloatingState()
override val struct: QueryStruct
get() = state.runtime.environment.query
override fun render(GuiGraphics: GuiGraphics, partialTicks: Float) {
val state = this.state
@@ -10,10 +10,8 @@ package com.cobblemon.mod.common.client.gui.dialogue.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.ScrollingWidget
import com.cobblemon.mod.common.client.gui.dialogue.DialogueScreen
import com.cobblemon.mod.common.client.render.TextClipping
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.client.dialogue.dto.DialogueInputDTO
import com.cobblemon.mod.common.net.messages.server.dialogue.InputToDialoguePacket
@@ -129,7 +127,6 @@ class DialogueBox(
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTicks: Float) {
val gibber = dialogueScreen.gibber
correctSize()
blitk(
matrixStack = context.pose(),
@@ -140,9 +137,8 @@ class DialogueBox(
width = frameWidth,
textureWidth = frameWidth + DialoguePortraitWidget.DIALOGUE_ARROW_WIDTH + SCROLL_BAR_WIDTH + SCROLL_TRACK_WIDTH
)
TextClipping.doWithMaxCharacters(if (gibber?.graduallyShowText == true) dialogueScreen.gibberIndex else -1) {
super.renderWidget(context, mouseX, mouseY, partialTicks)
}
super.renderWidget(context, mouseX, mouseY, partialTicks)
}
override fun enableScissor(context: GuiGraphics) {
@@ -162,14 +158,6 @@ class DialogueBox(
mouseY > this.y && mouseY < this.bottom
) {
if (dialogue.dialogueInput.allowSkip && dialogue.dialogueInput.inputType in listOf(DialogueInputDTO.InputType.NONE, DialogueInputDTO.InputType.AUTO_CONTINUE)) {
val gibber = dialogueScreen.gibber
if (gibber != null && gibber.graduallyShowText && !dialogueScreen.gibberDone) {
if (gibber.allowSkip) {
dialogueScreen.gibberDone = true
}
return true
}
dialogueScreen.sendToServer(InputToDialoguePacket(dialogue.dialogueInput.inputId, "skip!"))
return true
}
@@ -63,4 +63,4 @@ class DialogueOptionWidget(
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
return true
}
}
}
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render
/**
* A utility class for limiting the number of characters that can be rendered by whatever draw operation
* is about to occur.
*
* Usage: Run [doWithMaxCharacters] with the maximum number of characters that will be allowed to render,
* or -1 for unlimited, and a mixin will ensure that a string renderer will only visit that many characters
* and cancel all those after.
*
* @author Hiroku
* @since April 19th, 2025
*/
object TextClipping {
private var acceptableRenderCount = -1
private var renderedCount = 0
@JvmStatic
fun doWithMaxCharacters(maxCharacters: Int, action: Runnable) {
acceptableRenderCount = maxCharacters
action.run()
acceptableRenderCount = -1
renderedCount = 0
}
@JvmStatic
fun canDrawAnotherCharacter(): Boolean {
if (acceptableRenderCount == -1) {
return true
}
if (renderedCount >= acceptableRenderCount) {
return false
}
renderedCount++
return true
}
}
@@ -1,238 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.block
import com.cobblemon.mod.common.block.entity.StadiumBlockEntity
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
import net.minecraft.util.Mth
import org.joml.Vector3f
import org.joml.Vector3i
class StadiumBlockRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<StadiumBlockEntity> {
val context = RenderContext().also {
it.put(RenderContext.DO_QUIRKS, false)
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.BLOCK)
}
private var pathIndex = 0
private var animationTime = 0.0
private val droneSpeed = 0.10
override fun render(
blockEntity: StadiumBlockEntity,
partialTick: Float,
poseStack: PoseStack,
bufferSource: MultiBufferSource,
packedLight: Int,
packedOverlay: Int
) {
val vertexConsumer = bufferSource.getBuffer(RenderType.LINES)
val pose = poseStack.last()
val center = Vector3f(0.5f,0f,0.5f).add(blockEntity.battleCenter.toVector3f()) // 0,0,0 is corner of block, we make center be bottom center
val rotation = blockEntity.battleRot / (180f * Mth.PI)
val widthOffset = blockEntity.battleArea.x / 2f
val lengthOffset = blockEntity.battleArea.z / 2f
val height = blockEntity.battleArea.y
fun drawLine(point1: Vector3i, point2: Vector3i) {
val f = if (point1.x == 0) -widthOffset else widthOffset
val g = if (point1.y == 0) 0f else height
val h = if (point1.z == 0) -lengthOffset else lengthOffset
val i = if (point2.x == 0) -widthOffset else widthOffset
val j = if (point2.y == 0) 0f else height
val k = if (point2.z == 0) -lengthOffset else lengthOffset
val position1 = Vector3f(f, g, h).rotateY(rotation).add(center)
val position2 = Vector3f(i, j, k).rotateY(rotation).add(center)
val normal = Vector3f(position2).sub(position1).normalize()
vertexConsumer.addVertex(pose, position1).setColor(0.9f, 0.9f, 0.9f, 1f).setNormal(normal.x, normal.y, normal.z)
vertexConsumer.addVertex(pose, position2).setColor(0.9f, 0.9f, 0.9f, 1f).setNormal(normal.x, normal.y, normal.z)
}
drawLine(Vector3i(0,0,0), Vector3i(1,0,0))
drawLine(Vector3i(0,0,0), Vector3i(0,1,0))
drawLine(Vector3i(0,0,0), Vector3i(0,0,1))
drawLine(Vector3i(1,0,0), Vector3i(1,1,0))
drawLine(Vector3i(1,0,0), Vector3i(1,0,1))
drawLine(Vector3i(0,0,1), Vector3i(1,0,1))
drawLine(Vector3i(0,0,1), Vector3i(0,1,1))
drawLine(Vector3i(0,0,0), Vector3i(0,0,0))
drawLine(Vector3i(0,1,0), Vector3i(1,1,0))
drawLine(Vector3i(0,1,0), Vector3i(0,1,1))
drawLine(Vector3i(1,1,0), Vector3i(1,1,1))
drawLine(Vector3i(0,1,1), Vector3i(1,1,1))
drawLine(Vector3i(1,0,1), Vector3i(1,1,1))
}
/*
override fun render(
blockEntity: StadiumBlockEntity,
tickDelta: Float,
poseStack: PoseStack,
multiBufferSource: MultiBufferSource,
light: Int,
overlay: Int
) {
val blockState = blockEntity.blockState
val yRot = blockState.getValue(HorizontalDirectionalBlock.FACING).toYRot()
val isClientSide = blockEntity.level?.isClientSide == true
if (!isClientSide) return
val stadiumBlockState = blockEntity.level?.getBlockState(blockEntity.blockPos) ?: return
if (stadiumBlockState.block != CobblemonBlocks.STADIUM) {
return
}
val droneState = blockEntity.droneState
poseStack.pushPose()
if (blockEntity.isInUse) {
if (blockEntity.pathPoints.isEmpty()) {
poseStack.popPose()
return
}
spawnDebugParticles(blockEntity) // debug particles
// Center the drone at the block's center
poseStack.translate(0.5, -3.0, 0.5)
//poseStack.mulPose(Axis.XP.rotationDegrees(180F))
//poseStack.mulPose(Axis.YP.rotationDegrees(180F))
val points = blockEntity.pathPoints
val nextIndex = (pathIndex + 1) % points.size
val currentPoint = points[pathIndex]
val nextPoint = points[nextIndex]
animationTime += tickDelta * droneSpeed
if (animationTime >= 1.0) {
animationTime = 0.0
pathIndex = nextIndex
}
val interpolatedPos = currentPoint.lerp(nextPoint, animationTime.toDouble())
println("DEBUG: Drone moving between points:")
println(" - Current Point: ${currentPoint}")
println(" - Next Point: ${nextPoint}")
println(" - Interpolated Position: $interpolatedPos")
poseStack.translate(interpolatedPos.x - blockEntity.blockPos.x,
interpolatedPos.y - blockEntity.blockPos.y,
interpolatedPos.z - blockEntity.blockPos.z)
println("DEBUG: Drone position interpolated to: $interpolatedPos")
poseStack.mulPose(Axis.YP.rotationDegrees(-yRot))
poseStack.mulPose(Axis.ZP.rotationDegrees(180F)) // flip the drone right side up
renderDrone(droneState, "sleep", tickDelta, poseStack, 1.0F, multiBufferSource, light)
} else {
val yRot = blockState.getValue(HorizontalDirectionalBlock.FACING).toYRot()
val poseEntry = poseStack.last()
val matrix = poseEntry.pose()
poseStack.translate(0.5, 0.0, 0.5)
poseStack.mulPose(Axis.YP.rotationDegrees(yRot))
val posX = matrix.m03()
val posY = matrix.m13()
val posZ = matrix.m23()
println("DEBUG: Drone is NOT in use. Rendering default position at: x=$posX, y=$posY, z=$posZ")
poseStack.mulPose(Axis.ZP.rotationDegrees(180F))
renderDrone(droneState, "sleep", tickDelta, poseStack, 1.0F, multiBufferSource, light)
}
poseStack.popPose()
}
fun renderDrone(
state: DroneState,
pose: String,
tickDelta: Float,
poseStack: PoseStack,
scale: Float = 1F,
multiBufferSource: MultiBufferSource,
light: Int,
) {
state.updatePartialTicks(tickDelta)
val droneResource: ResourceLocation = cobblemonResource("rotom_drone")
val model = VaryingModelRepository.getPoser(droneResource, state) as DroneModel
val texture = VaryingModelRepository.getTexture(droneResource, state)
val buffer = multiBufferSource.getBuffer(RenderType.entityCutout(texture))
model.bufferProvider = multiBufferSource
state.currentModel = model
state.setPose(pose)
model.context = context
context.put(RenderContext.TEXTURE, texture)
context.put(RenderContext.RENDER_STATE, RenderContext.RenderState.BLOCK)
context.put(RenderContext.POSABLE_STATE, state)
model.applyAnimations(
entity = null,
state = state,
headYaw = 0F,
headPitch = 0F,
limbSwing = 0F,
limbSwingAmount = 0F,
ageInTicks = state.animationSeconds * 20
)
poseStack.scale(scale, scale, scale)
// TODO: Make drone lighting dynamic (simply using the light arg makes the drone always appear dark)
model.withLayerContext(multiBufferSource, state, VaryingModelRepository.getLayers(droneResource, state)) {
model.render(context, poseStack, buffer, 200, OverlayTexture.NO_OVERLAY, -0x1)
}
model.setDefault()
}
private fun spawnDebugParticles(blockEntity: StadiumBlockEntity) {
val world = blockEntity.level ?: return
if (!world.isClientSide) return // Only run on the client-side
for (point in blockEntity.pathPoints) {
world.addParticle(
net.minecraft.core.particles.ParticleTypes.FLAME, // Test particle (changeable)
point.x, point.y, point.z, // Position of the particle
0.0, 0.02, 0.0 // Slight upward motion
)
}
// Spawn a special particle at the next interpolation point
if (blockEntity.pathPoints.isNotEmpty()) {
val nextPoint = blockEntity.pathPoints[(pathIndex + 1) % blockEntity.pathPoints.size]
world.addParticle(
net.minecraft.core.particles.ParticleTypes.END_ROD, // Different effect for next point
nextPoint.x, nextPoint.y, nextPoint.z,
0.0, 0.05, 0.0
)
}
}
*/
}
@@ -0,0 +1,308 @@
package com.cobblemon.mod.common.client.render.camera
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.Seat
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository.getPoser
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.mixin.accessor.CameraAccessor
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.client.Camera
import net.minecraft.client.Minecraft
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.HitResult
import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
object CameraTransformationController {
private val transformers = mutableMapOf<Int, MutableList<CameraTransformer>>(
1 to mutableListOf(RidingCameraTransformer()),
)
fun register(priority: Int, transformer: CameraTransformer) {
if (transformers[priority] == null) {
transformers[priority] = mutableListOf()
}
transformers[priority]!!.add(transformer)
}
fun tick(current: CameraTransformation, delta: Float, context: CameraContext): CameraTransformation {
val transformers = transformers.toSortedMap().reversed().values.flatten()
transformers.forEach {
if (it.condition(context)) {
return it.tick(current, delta, context)
}
}
return current
}
}
interface CameraTransformer {
val key: ResourceLocation
val condition: (CameraContext) -> Boolean
fun tick(current: CameraTransformation, delta: Float, context: CameraContext): CameraTransformation
fun onStart() {}
fun onStop() {}
}
data class CameraTransformation(val position: Vec3, val rotation: Quaternionf, val fov: Int)
data class CameraContext(
val camera: Camera,
val entity: Entity,
val eyeHeight: Float,
val eyeHeightO: Float,
val perspective: CameraPerspective
)
enum class CameraPerspective {
FIRST_PERSON,
THIRD_PERSON_BACK,
THIRD_PERSON_FRONT
}
class RidingCameraTransformer : CameraTransformer {
override val key = cobblemonResource("camera/riding")
override val condition: (CameraContext) -> Boolean = { it.entity.vehicle is PokemonEntity }
val positionTransformer = RidingCameraPositionTransformer()
val viewBobPositionTransformer = RidingCameraViewBobPositionTransformer()
val rotationTransformer = OrientationControllerRotationTransformer()
val viewBobRotationTransformer = RidingCameraViewBobRotationTransformer()
override fun tick(current: CameraTransformation, delta: Float, context: CameraContext): CameraTransformation {
val vehicle = context.entity.vehicle as? PokemonEntity ?: return current
MountedPokemonAnimationRenderController.setup(vehicle, delta)
val position = getPosition(delta, context, vehicle)
val rotation = getRotation(current, context)
val fov = Minecraft.getInstance().options.fov().get()
return CameraTransformation(position, rotation, fov)
}
private fun getPosition(delta: Float, context: CameraContext, vehicle: PokemonEntity): Vec3 {
if (context.perspective == CameraPerspective.FIRST_PERSON || Cobblemon.config.viewBobTranslation) {
return viewBobPositionTransformer.getPosition(delta, context, vehicle)
} else {
return positionTransformer.getPosition(delta, context, vehicle)
}
}
private fun getRotation(current: CameraTransformation, context: CameraContext): Quaternionf {
if (Cobblemon.config.viewBobRotation) {
return viewBobRotationTransformer.getRotation()
}
else {
return rotationTransformer.getRotation(current.rotation, context)
}
}
}
class RidingCameraPositionTransformer {
fun getPosition(delta: Float, context: CameraContext, vehicle: PokemonEntity): Vec3 {
val position = Vec3(
Mth.lerp(delta.toDouble(), vehicle.xOld, vehicle.x),
Mth.lerp(delta.toDouble(), vehicle.yOld, vehicle.y),
Mth.lerp(delta.toDouble(), vehicle.zOld, vehicle.z)
)
val orientation = getOrientation(context.entity)
var offset = Vec3.ZERO
offset = applyThirdPersonToOffset(offset, context, vehicle)
offset = applyRollingToOffset(offset, orientation)
offset = applyZoomToOffset(offset, position, context)
val pivotOffset = applyPivotToOffset(vehicle, context.entity, orientation)
return position.add(pivotOffset).add(offset)
}
fun applyPivotToOffset(vehicle: PokemonEntity, passenger: Entity, orientation: Matrix3f): Vec3 {
val seatIndex: Int = vehicle.passengers.indexOf(passenger)
if (seatIndex < 0) return Vec3.ZERO
val seat: Seat = vehicle.seats[seatIndex]
val model = getPoser(vehicle.pokemon.species.resourceIdentifier, FloatingState())
val center = Vec3(0.0, passenger.bbHeight / 2.0, 0.0)
val pivotOffsets = model.thirdPersonPivotOffset
if (!pivotOffsets.containsKey(seat.locator)) return Vec3.ZERO
val pivotOffset = pivotOffsets[seat.locator]!!
val rotatedOffset = orientation.transform(pivotOffset.subtract(center).toVector3f())
return rotatedOffset.toVec3d().add(center)
}
fun applyThirdPersonToOffset(offset: Vec3, context: CameraContext, vehicle: PokemonEntity): Vec3 {
if (context.perspective == CameraPerspective.FIRST_PERSON) return offset
val model = getPoser(vehicle.pokemon.species.resourceIdentifier, FloatingState())
val cameraOffsets = model.thirdPersonCameraOffset
val seatIndex: Int = vehicle.passengers.indexOf(context.entity)
if (seatIndex < 0) return offset
val seat: Seat = vehicle.seats[seatIndex]
val thirdPartyOffset = getThirdPartyOffset(seat, cameraOffsets, context)
return offset.add(thirdPartyOffset)
}
private fun getThirdPartyOffset(seat: Seat, offsets: MutableMap<String, Vec3>, context: CameraContext): Vec3 {
val locatorName = when (context.perspective) {
CameraPerspective.FIRST_PERSON -> seat.locator
CameraPerspective.THIRD_PERSON_FRONT -> seat.locator
CameraPerspective.THIRD_PERSON_BACK -> "${seat.locator}_reverse"
}
if (!offsets.containsKey(locatorName)) return Vec3.ZERO
val offset = offsets[locatorName]!!
return when (context.perspective) {
CameraPerspective.FIRST_PERSON -> Vec3.ZERO
CameraPerspective.THIRD_PERSON_FRONT -> offset
CameraPerspective.THIRD_PERSON_BACK -> offset.multiply(1.0, 1.0, -1.0)
}
}
fun applyRollingToOffset(offset: Vec3, orientation: Matrix3f): Vec3 {
return orientation.transform(offset.toVector3f()).toVec3d()
}
fun applyZoomToOffset(offset: Vec3, pivot: Vec3, context: CameraContext): Vec3 {
val offsetDistance = offset.length()
val offsetDirection = offset.multiply(1 / offsetDistance, 1 / offsetDistance, 1 / offsetDistance)
val maxZoom = getMaxZoom(offsetDistance.toFloat(), offsetDirection.toVector3f(), pivot.toVector3f(), context.camera).toDouble()
return offset.multiply(maxZoom, maxZoom, maxZoom)
}
private fun getMaxZoom(maxZoom: Float, directionVector: Vector3f, positionVector: Vector3f, instance: Camera): Float {
var maxZoom = maxZoom
val pos = Vec3(positionVector)
for (i in 0 .. 7) {
val g = ((i and 1) * 2 - 1).toFloat()
val h = ((i shr 1 and 1) * 2 - 1).toFloat()
val j = ((i shr 2 and 1) * 2 - 1).toFloat()
val vec3 = pos.add((g * 0.1f).toDouble(), (h * 0.1f).toDouble(), (j * 0.1f).toDouble())
val vec32 = vec3.add(Vec3(directionVector).scale(maxZoom.toDouble()))
val level = (instance as CameraAccessor).level
val hitResult: HitResult =
level.clip(ClipContext(vec3, vec32, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, instance.entity))
if (hitResult.type != HitResult.Type.MISS) {
val k = hitResult.getLocation().distanceToSqr(pos).toFloat()
if (k < Mth.square(maxZoom)) {
maxZoom = Mth.sqrt(k)
}
}
}
return maxZoom
}
fun getOrientation(passenger: Entity): Matrix3f {
val xRot = (-1 * Math.toRadians(passenger.xRot.toDouble())).toFloat()
val controller = (passenger as? OrientationControllable)?.getOrientationController()
if (controller == null || !controller.isActive() || controller.orientation == null) {
return Matrix3f()
.rotateY(Math.toRadians((180f - passenger.yRot).toDouble()).toFloat())
.rotateX(xRot)
}
return controller.orientation!!
}
}
class RidingCameraViewBobPositionTransformer {
fun getPosition(delta: Float, context: CameraContext, vehicle: PokemonEntity): Vec3 {
val position = Vec3(
Mth.lerp(delta.toDouble(), vehicle.xOld, vehicle.x),
Mth.lerp(delta.toDouble(), vehicle.yOld, vehicle.y),
Mth.lerp(delta.toDouble(), vehicle.zOld, vehicle.z)
)
var offset = Vec3.ZERO
// Add eye level
offset = applyAABBToOffset(context, delta, offset, context.entity)
// Add first person offset from poser if found
offset = applyFirstPersonToOffset(context, offset, vehicle, context.entity)
// Transform the offset based on the entity's orientation
offset = applyRollingToOffset(offset, delta, context.entity)
// Add the offset from the locator
offset = applyLocatorToOffset(offset, vehicle, context.entity)
return position.add(offset)
}
private fun applyLocatorToOffset(offset: Vec3, vehicle: PokemonEntity, passenger: Entity): Vec3 {
val seatIndex: Int = vehicle.passengers.indexOf(passenger)
if (seatIndex < 0) return offset
val seat: Seat = vehicle.seats[seatIndex]
val delegate = vehicle.delegate as PokemonClientDelegate
val locator = delegate.locatorStates[seat.locator] ?: return offset
return offset.add(Vec3(locator.matrix.getTranslation(Vector3f())))
}
private fun applyAABBToOffset(context: CameraContext, delta: Float, offset: Vec3, passenger: Entity): Vec3 {
val currEyeHeight: Double =
Mth.lerp(delta.toDouble(), context.eyeHeight.toDouble(), context.eyeHeightO.toDouble())
return offset.add(0.0, currEyeHeight - (passenger.bbHeight / 2), 0.0)
}
private fun applyRollingToOffset(offset: Vec3, delta: Float, passenger: Entity): Vec3 {
val rollable = passenger as? OrientationControllable ?: return offset
val controller = rollable.getOrientationController()
if (!controller.isActive()) return offset
return controller.getRenderOrientation(delta).transform(offset.toVector3f()).toVec3d()
}
private fun applyFirstPersonToOffset(
context: CameraContext,
offset: Vec3,
vehicle: PokemonEntity,
passenger: Entity
): Vec3 {
if (context.perspective != CameraPerspective.FIRST_PERSON) return offset
val model = getPoser(vehicle.pokemon.species.resourceIdentifier, FloatingState())
val seatIndex: Int = vehicle.passengers.indexOf(passenger)
val seat: Seat = vehicle.seats[seatIndex]
val cameraOffsets = model.firstPersonCameraOffset
return when (cameraOffsets.containsKey(seat.locator)) {
true -> cameraOffsets[seat.locator]!!
false -> Vec3.ZERO
}
}
}
class RidingCameraViewBobRotationTransformer {
fun getRotation(): Quaternionf {
TODO()
}
}
class OrientationControllerRotationTransformer {
fun getRotation(current: Quaternionf, context: CameraContext): Quaternionf {
val entity = context.entity
val controllable = entity as? OrientationControllable ?: return current
val controller = controllable.getOrientationController()
val orientation = controller.orientation ?: return current
return orientation.normal(Matrix3f()).getNormalizedRotation(Quaternionf())
}
}
@@ -33,9 +33,6 @@ object MountedCameraRenderer
instance: Camera,
entity: Entity,
vehicle: Entity,
x: Double,
y: Double,
z: Double,
thirdPersonReverse: Boolean,
eyeHeightOld: Double,
eyeHeight: Double
@@ -59,7 +56,7 @@ object MountedCameraRenderer
// First person and view bobbing are handled the same way.
if (!instance.isDetached || Cobblemon.config.thirdPersonViewBobbing) {
if (!instance.isDetached || Cobblemon.config.viewBobTranslation) {
val delegate = vehicle.delegate as PokemonClientDelegate
val locator = delegate.locatorStates[seat.locator]
@@ -176,4 +173,4 @@ object MountedCameraRenderer
return maxZoom
}
}
}
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2025 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.drone
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PoseAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
import com.cobblemon.mod.common.entity.PoseType
import com.google.gson.annotations.SerializedName
import net.minecraft.client.model.geom.ModelPart
/**
* A model for rendering the Rotom drone used by the stadium block.
*
* @author Flarkinater
* @since February 14th, 2025
*/
class DroneModel(root: Bone) : PosableModel(root) {
@Transient
@SerializedName("So this name can be literally anything, huh?")
override val rootPart = (root as ModelPart).registerChildWithAllChildren(root.children.entries.first().key)
var droneAnimations: Array<PoseAnimation> = emptyArray()
var droneQuirks: Array<ModelQuirk<*>> = emptyArray()
override fun registerPoses() {
registerPose(
poseType = PoseType.SLEEP,
animations = droneAnimations,
quirks = droneQuirks
)
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2025 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.drone
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
/**
* State for a drone Rotom attached to a stadium block.
*
* @author Flarkinater
* @since February 12th, 2025
*/
class DroneState() : PosableState() {
init {
setPose("sleep")
age = 0
currentPartialTicks = 0F
}
override fun getEntity() = null
override fun updatePartialTicks(partialTicks: Float) {
currentPartialTicks += partialTicks / 2
}
override val schedulingTracker = SchedulingTracker()
}
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.*
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.blockentity.BlockEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.fossil.FossilModel
import com.cobblemon.mod.common.client.render.models.blockbench.drone.DroneModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.AncientPokeBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.BeastBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PokeBallModel
@@ -80,14 +79,12 @@ object VaryingModelRepository {
"poke_balls",
"generic",
"block_entities",
"drone"
)
val poserDirectories: List<Pair<String, Class<out PosableModel>>> = listOf(
"bedrock/posers" to PosableModel::class.java,
"bedrock/pokemon/posers" to PokemonPosableModel::class.java,
"bedrock/fossils/posers" to FossilModel::class.java,
"bedrock/drone/posers" to DroneModel::class.java,
"bedrock/block_entities/posers" to BlockEntityModel::class.java,
"bedrock/npcs/posers" to PosableModel::class.java,
"bedrock/poke_balls/posers" to PosableModel::class.java,
@@ -119,7 +116,6 @@ object VaryingModelRepository {
.registerTypeAdapter(PosableModel::class.java, JsonModelAdapter(::PosableModel))
.registerTypeAdapter(PokemonPosableModel::class.java, JsonModelAdapter(::PokemonPosableModel))
.registerTypeAdapter(FossilModel::class.java, JsonModelAdapter(::FossilModel))
.registerTypeAdapter(DroneModel::class.java, JsonModelAdapter(::DroneModel))
.registerTypeAdapter(BlockEntityModel::class.java, JsonModelAdapter(::BlockEntityModel))
.registerTypeAdapter(Pose::class.java, PoseAdapter { JsonModelAdapter.model!! })
.registerTypeAdapter(ModelPartTransformation::class.java, ModelPartTransformationAdapter)
@@ -262,8 +262,11 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Interface, lang = "party_portrait_animations")
var partyPortraitAnimations = PortraitStyle.NEVER_ANIMATE
@CobblemonConfigField(Category.Riding, lang = "third_person_view_bobbing")
var thirdPersonViewBobbing = false
@CobblemonConfigField(Category.Riding, lang = "view_bob_translation")
var viewBobTranslation = false
@CobblemonConfigField(Category.Riding, lang = "view_bob_rotation")
var viewBobRotation = false
@CobblemonConfigField(Category.Riding, lang = "invert_roll")
var invertRoll = false
@@ -50,8 +50,6 @@ import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.properties.PropertiesCompletionProvider
import com.cobblemon.mod.common.stadium.BattleCameraManagers
import com.cobblemon.mod.common.stadium.BattleCameras
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.cobblemon.mod.common.util.server
@@ -100,8 +98,6 @@ object CobblemonDataProvider : DataProvider {
this.register(CobblemonCosmeticItems)
this.register(CobblemonFlows)
this.register(CobblemonUnlockableWallpapers)
this.register(BattleCameras)
this.register(BattleCameraManagers)
this.register(Marks)
CobblemonSpawnPools.load()
@@ -242,7 +242,6 @@ object CobblemonItemGroups {
entries.accept(CobblemonItems.PC)
entries.accept(CobblemonItems.HEALING_MACHINE)
entries.accept(CobblemonItems.PASTURE)
entries.accept(CobblemonItems.STADIUM_BLOCK)
entries.accept(CobblemonItems.GILDED_CHEST)
entries.accept(CobblemonItems.YELLOW_GILDED_CHEST)
@@ -36,24 +36,16 @@ class DialogueDTO : Encodable, Decodable {
constructor(activeDialogue: ActiveDialogue, includeFaces: Boolean) {
this.dialogueId = activeDialogue.dialogueId
this.speakers = if (includeFaces) activeDialogue.dialogueReference.speakers.mapNotNull { (key, value) ->
val name = value.name?.invoke(activeDialogue)
val gibber = value.gibber?.let(::DialogueGibberDTO)
val face = if (value.face is ExpressionLikeDialogueFaceProvider) {
if (value.face is ExpressionLikeDialogueFaceProvider) {
val resolved = activeDialogue.runtime.resolve(value.face.providerExpression)
if (resolved is ObjectValue<*> && resolved.obj is DialogueFaceProvider) {
resolved.obj as DialogueFaceProvider
key to DialogueSpeakerDTO(value.name?.invoke(activeDialogue), resolved.obj as DialogueFaceProvider)
} else {
return@mapNotNull null
null
}
} else {
value.face
key to DialogueSpeakerDTO(value.name?.invoke(activeDialogue), value.face)
}
key to DialogueSpeakerDTO(
name = name,
face = face,
gibber = gibber
)
}.toMap() else null
this.currentPageDTO = DialoguePageDTO(activeDialogue.currentPage, activeDialogue)
val input = activeDialogue.activeInput.dialogueInput
@@ -96,8 +88,6 @@ class DialogueDTO : Encodable, Decodable {
buffer.writeUUID(value.face.playerId)
buffer.writeBoolean(value.face.isLeftSide)
}
buffer.writeNullable(value.gibber) { _, gibber -> gibber.encode(buffer) }
}
}
}
@@ -115,50 +105,16 @@ class DialogueDTO : Encodable, Decodable {
val name = buffer.readNullable { buffer.readText().copy() }
val faceType = buffer.readNullable { buffer.readString() }
when (faceType) {
"reference" -> {
val entityId = buffer.readInt()
val isLeftSide = buffer.readBoolean()
val gibber = buffer.readNullable { DialogueGibberDTO.decode(buffer) }
key to DialogueSpeakerDTO(
name = name,
face = ReferenceDialogueFaceProvider(
entityId = entityId,
isLeftSide = isLeftSide
),
gibber = gibber
)
}
"reference" -> key to DialogueSpeakerDTO(name, ReferenceDialogueFaceProvider(buffer.readInt(), buffer.readBoolean()))
"artificial" -> {
val modelType = buffer.readString()
val identifier = buffer.readIdentifier()
val aspects = buffer.readList { buffer.readString() }.toSet()
val isLeftSide = buffer.readBoolean()
key to DialogueSpeakerDTO(
name = name,
face = ArtificialDialogueFaceProvider(modelType, identifier, aspects, isLeftSide),
gibber = DialogueGibberDTO.decode(buffer)
)
}
"player" -> {
val playerId = buffer.readUUID()
val isLeftSide = buffer.readBoolean()
val gibber = buffer.readNullable { DialogueGibberDTO.decode(buffer) }
key to DialogueSpeakerDTO(
name = name,
face = PlayerDialogueFaceProvider(
playerId = playerId,
isLeftSide = isLeftSide
),
gibber = gibber
)
}
else -> {
key to DialogueSpeakerDTO(
name = name,
face = null,
gibber = DialogueGibberDTO.decode(buffer)
)
key to DialogueSpeakerDTO(name, ArtificialDialogueFaceProvider(modelType, identifier, aspects, isLeftSide))
}
"player" -> key to DialogueSpeakerDTO(name, PlayerDialogueFaceProvider(buffer.readUUID(), buffer.readBoolean()))
else -> key to DialogueSpeakerDTO(name, null)
}
}
}
@@ -1,66 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.dialogue.dto
import com.cobblemon.mod.common.api.dialogue.DialogueGibber
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.writeIdentifier
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
class DialogueGibberDTO(
val graduallyShowText: Boolean,
val allowSkip: Boolean,
val step: Int,
val interval: Float,
val minPitch: Float,
val maxPitch: Float,
val minVolume: Float,
val maxVolume: Float,
val sounds: List<ResourceLocation>
) {
constructor(gibber: DialogueGibber): this(
graduallyShowText = gibber.graduallyShowText,
allowSkip = gibber.allowSkip,
step = gibber.step,
interval = gibber.interval.toFloat(),
minPitch = gibber.minPitch,
maxPitch = gibber.maxPitch,
minVolume = gibber.minVolume,
maxVolume = gibber.maxVolume,
sounds = gibber.sounds
)
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeBoolean(graduallyShowText)
buffer.writeBoolean(allowSkip)
buffer.writeInt(step)
buffer.writeFloat(interval)
buffer.writeFloat(minPitch)
buffer.writeFloat(maxPitch)
buffer.writeFloat(minVolume)
buffer.writeFloat(maxVolume)
buffer.writeCollection(sounds) { _, value -> buffer.writeIdentifier(value) }
}
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): DialogueGibberDTO {
val graduallyShowText = buffer.readBoolean()
val allowSkip = buffer.readBoolean()
val step = buffer.readInt()
val interval = buffer.readFloat()
val minPitch = buffer.readFloat()
val maxPitch = buffer.readFloat()
val minVolume = buffer.readFloat()
val maxVolume = buffer.readFloat()
val sounds = buffer.readList { it.readIdentifier() }
return DialogueGibberDTO(graduallyShowText, allowSkip, step, interval, minPitch, maxPitch, minVolume, maxVolume, sounds)
}
}
}
@@ -13,9 +13,13 @@ import com.cobblemon.mod.common.api.dialogue.DialoguePage
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readList
import com.cobblemon.mod.common.util.readNullable
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.readText
import com.cobblemon.mod.common.util.writeCollection
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeNullable
import com.cobblemon.mod.common.util.writeString
import com.cobblemon.mod.common.util.writeText
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -26,7 +30,6 @@ class DialoguePageDTO : Encodable, Decodable {
var speaker: String? = null
lateinit var background: ResourceLocation
var lines: MutableList<MutableComponent> = mutableListOf()
var gibber: DialogueGibberDTO? = null
// Later can include some face data probably
var clientActions = mutableListOf<String>()
@@ -42,7 +45,6 @@ class DialoguePageDTO : Encodable, Decodable {
buffer.writeNullable(speaker) { _, value -> buffer.writeString(value)}
buffer.writeIdentifier(background)
buffer.writeCollection(lines) { _, value -> buffer.writeText(value) }
buffer.writeNullable(gibber) { _, value -> value.encode(buffer) }
buffer.writeInt(clientActions.size)
clientActions.forEach { buffer.writeString(it) }
}
@@ -51,7 +53,6 @@ class DialoguePageDTO : Encodable, Decodable {
speaker = buffer.readNullable { buffer.readString() }
background = buffer.readIdentifier()
lines = buffer.readList { (it as RegistryFriendlyByteBuf).readText().copy() }.toMutableList()
gibber = buffer.readNullable { DialogueGibberDTO.decode(buffer) }
val clientActionsSize = buffer.readInt()
for (i in 0 until clientActionsSize) {
clientActions.add(buffer.readString())
@@ -13,6 +13,5 @@ import net.minecraft.network.chat.MutableComponent
class DialogueSpeakerDTO(
val name: MutableComponent? = null,
val face: DialogueFaceProvider?,
val gibber: DialogueGibberDTO? = null
val face: DialogueFaceProvider?
)
@@ -1,192 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.util.*
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
import java.lang.reflect.Type
import kotlin.math.abs
import kotlin.math.atan
import kotlin.math.atan2
class BattleCameraEntry(
var id: ResourceLocation,
val length: Double = 0.0,
val positionTimeline: MutableMap<String, CameraKeyFrame> = mutableMapOf(),
val targetPositionTimeline: MutableMap<String, CameraKeyFrame> = mutableMapOf(),
val move: String = ""
) {
fun resolvePosition(time: Double, yRot: Float): Vec3 {
return resolvePosition(time, positionTimeline).yRot(yRot * (Mth.PI/180))
}
private fun resolvePosition(time: Double, map: MutableMap<String, CameraKeyFrame>): Vec3 {
val runtime = BattleCameraManagerClient.runtime
runtime.environment.setSimpleVariable("anim_time", DoubleValue(time))
var frame1: CameraKeyFrame? = null
var frame2: CameraKeyFrame? = null
var time1 = -1.0
var time2 = -1.0
for (keyframe in map) {
if (time >= keyframe.key.toDouble()) {
if (time1 == -1.0 || abs(time - keyframe.key.toDouble()) < abs(time - time1)) {
frame1 = keyframe.value
time1 = keyframe.key.toDouble()
}
}
if (time < keyframe.key.toDouble()) {
if (time2 == -1.0 || abs(time - keyframe.key.toDouble()) < abs(time - time2)) {
frame2 = keyframe.value
time2 = keyframe.key.toDouble()
}
}
}
val difference = (time - time1) / (time2 - time1)
if (frame1 != null && frame1.interpolation == CameraInterpolation.STEP) {
frame2 = null
}
if (frame1 == null && frame2 == null) {
return Vec3.ZERO
}
else if (frame1 == null) {
frame2!!
val x = runtime.resolveDouble(frame2.x)
val y = runtime.resolveDouble(frame2.y)
val z = runtime.resolveDouble(frame2.z)
return Vec3(x,y,z)
}
else if (frame2 == null) {
val x = runtime.resolveDouble(frame1.x)
val y = runtime.resolveDouble(frame1.y)
val z = runtime.resolveDouble(frame1.z)
return Vec3(x,y,z)
}
else {
val x1 = runtime.resolveDouble(frame1.x)
val y1 = runtime.resolveDouble(frame1.y)
val z1 = runtime.resolveDouble(frame1.z)
val vec1 = Vec3(x1,y1,z1)
val x2 = runtime.resolveDouble(frame2.x)
val y2 = runtime.resolveDouble(frame2.y)
val z2 = runtime.resolveDouble(frame2.z)
val vec2 = Vec3(x2,y2,z2)
return vec1.lerp(vec2, difference)
}
}
fun resolveRotation(time: Double, yRot: Float): Vector3f {
val position = resolvePosition(time, positionTimeline).yRot(yRot * (Mth.PI/180))
val target = resolvePosition(time, targetPositionTimeline).yRot(yRot * (Mth.PI/180))
val offset = target.subtract(position)
val xRot = -atan(offset.y.toFloat()/offset.horizontalDistance().toFloat()) * (180F / Math.PI.toFloat())
val yRot = -atan2(offset.x.toFloat(),offset.z.toFloat()) * (180F / Math.PI.toFloat())
return Vector3f(xRot, yRot, 0.0F)
}
fun encode(buf: RegistryFriendlyByteBuf) {
buf.writeIdentifier(id)
buf.writeDouble(length)
buf.writeMap(positionTimeline, {_, key -> buf.writeString(key)}, {_, value ->
buf.writeString(value.x.getString())
buf.writeString(value.y.getString())
buf.writeString(value.z.getString())
buf.writeEnum(value.interpolation)
})
buf.writeMap(targetPositionTimeline, {_, key -> buf.writeString(key)}, {_, value ->
buf.writeString(value.x.getString())
buf.writeString(value.y.getString())
buf.writeString(value.z.getString())
buf.writeEnum(value.interpolation)
})
buf.writeString(move)
}
companion object {
fun decode(buf: RegistryFriendlyByteBuf) : BattleCameraEntry {
val id = buf.readIdentifier()
val length = buf.readDouble()
val positionTimeline = buf.readMap({_ -> buf.readString()}, {_ ->
CameraKeyFrame(buf.readString().asExpression(), buf.readString().asExpression(), buf.readString().asExpression(), buf.readEnum(CameraInterpolation::class.java))
}).toMutableMap()
val targetPositionTimeline = buf.readMap({_ -> buf.readString()}, {_ ->
CameraKeyFrame(buf.readString().asExpression(), buf.readString().asExpression(), buf.readString().asExpression(), buf.readEnum(CameraInterpolation::class.java))
}).toMutableMap()
val move = buf.readString()
return BattleCameraEntry(id, length, positionTimeline, targetPositionTimeline, move)
}
}
data class CameraKeyFrame (
val x: Expression,
val y: Expression,
val z: Expression,
val interpolation: CameraInterpolation
)
enum class CameraInterpolation {
LINEAR,
STEP
}
object CameraEntryAdapter: JsonDeserializer<BattleCameraEntry> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): BattleCameraEntry {
if (json is JsonObject) {
val length = json["length"]?.asDouble ?: 0.0
val positionTimeline = mutableMapOf<String, CameraKeyFrame>()
json["positions"]?.asJsonObject?.entrySet()?.forEach { (name, position) ->
val xyz = position.asJsonObject["xyz"]
var x = "0.0".asExpression()
var y = "0.0".asExpression()
var z = "0.0".asExpression()
if (xyz != null && xyz.isJsonArray && xyz.asJsonArray.size() == 3) {
x = xyz.asJsonArray[0].asString.asExpression()
y = xyz.asJsonArray[1].asString.asExpression()
z = xyz.asJsonArray[2].asString.asExpression()
}
val interpolation = when (position.asJsonObject["interpolation"]?.asString) {
"linear" -> CameraInterpolation.LINEAR
"step" -> CameraInterpolation.STEP
else -> CameraInterpolation.LINEAR
}
positionTimeline[name] = CameraKeyFrame(x, y, z, interpolation)
}
val targetPositionTimeline = mutableMapOf<String, CameraKeyFrame>()
json["targetPositions"]?.asJsonObject?.entrySet()?.forEach { (name, position) ->
val xyz = position.asJsonObject["xyz"]
var x = "0.0".asExpression()
var y = "0.0".asExpression()
var z = "0.0".asExpression()
if (xyz != null && xyz.isJsonArray && xyz.asJsonArray.size() == 3) {
x = xyz.asJsonArray[0].asString.asExpression()
y = xyz.asJsonArray[1].asString.asExpression()
z = xyz.asJsonArray[2].asString.asExpression()
}
val interpolation = when (position.asJsonObject["interpolation"]?.asString) {
"linear" -> CameraInterpolation.LINEAR
"step" -> CameraInterpolation.STEP
else -> CameraInterpolation.LINEAR
}
targetPositionTimeline[name] = CameraKeyFrame(x, y, z, interpolation)
}
val move = json["move"]?.asString ?: ""
return BattleCameraEntry(cobblemonResource(""), length, positionTimeline, targetPositionTimeline, move)
}
throw IllegalStateException("Stadium Camera could not be parsed")
}
}
}
@@ -1,215 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
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.setupClient
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.util.resolveBoolean
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
import java.util.UUID
import java.util.function.Function
object BattleCameraManagerClient {
//Molang
fun MoLangRuntime.setupCameraFunctions(): MoLangRuntime {
environment.query.addFunctions(
hashMapOf(
"moveCamera" to Function { params ->
if (params.get<MoValue>(0) !is StringValue) {
return@Function Unit
}
val camera = BattleCameras.getMoveCamera(params.get<MoValue>(0).asString())
return@Function camera?.id?.toString() ?: ""
}
)
)
return this
}
val runtime = MoLangRuntime().setup().setupClient().setupCameraFunctions()
var currentStadiumBattleUUID: UUID? = null
var Instruction = "DEFAULT"
var InstructionChangeTime = 0.0
var InstructionJustChanged = false
fun updateState(newState: String) {
Instruction = newState
InstructionChangeTime = 0.0
InstructionJustChanged = true
}
fun onLogout() {
currentStadiumBattleUUID = null
}
fun handleJoin(battleUUID: UUID, cameraManager: ResourceLocation, center: Vec3, yRot: Float) {
currentStadiumBattleUUID = battleUUID
activeCameraManager = cameraManager
battleCenter = center
battleYRot = yRot
cameraTimer = 0.0
currentCameraState = BattleCameraManagers.managers[cameraManager]!!.startingState
cameraPosition = Vec3.ZERO
cameraRotation = Vector3f()
updateCameraData(0.0)
}
fun inStadiumBattle(): Boolean {
return currentStadiumBattleUUID != null
}
fun shouldUseStadiumCamera(): Boolean {
return inStadiumBattle()
}
//Molang Data
fun updateMolangVar() {
val environment = runtime.environment
environment.setSimpleVariable("instruction", StringValue(Instruction))
//environment.setSimpleVariable("instructionChangeTime", DoubleValue(InstructionChangeTime))
environment.setSimpleVariable("instructionJustChanged", DoubleValue(InstructionJustChanged))
val isSpectating = CobblemonClient.battle?.spectating ?: false
environment.setSimpleVariable("isBattling", DoubleValue(!isSpectating))
environment.setSimpleVariable("isSpectating", DoubleValue(isSpectating))
}
fun instructionUpdate(newState: String, additionalValues: MutableMap<String, MoValue>) {
val environment = runtime.environment
Instruction = newState
InstructionChangeTime = 0.0
InstructionJustChanged = true
for (value in additionalValues) {
environment.setSimpleVariable(value.key, value.value)
}
}
//Camera Manager
var battleYRot = 0.0F
var battleCenter = Vec3.ZERO
var activeCameraManager: ResourceLocation? = null
var cameraTimer: Double = 0.0
var currentCameraState: String = ""
var nextCameraState: String = ""
var calculateNextState: Boolean = true
fun tickCameraManager() {
if (!shouldUseStadiumCamera()) return
//Molang Variables
val environment = runtime.environment
if (CobblemonClient.battle != null) {
environment.setSimpleVariable("selectingMove", DoubleValue(CobblemonClient.battle!!.mustChoose))
}
else {
environment.setSimpleVariable("selectingMove", DoubleValue(0.0))
}
val currentManager = BattleCameraManagers.managers[activeCameraManager]!!
val currentState = currentManager.states[currentCameraState]!!
val currentCamera = BattleCameras.cameras[currentState.camera]
InstructionChangeTime += 0.05
if (currentCamera != null) {
cameraTimer += 0.05
if (cameraTimer > currentCamera.length && currentCamera.length != 0.0) {
if (currentState.loop) {
cameraTimer -= currentCamera.length
}
else {
cameraTimer = currentCamera.length
}
}
}
if (!calculateNextState) {
currentCameraState = nextCameraState
calculateNextState = true
cameraTimer = if (currentCamera != null && currentCamera.length != 0.0) cameraTimer - currentCamera.length else 0.0
}
else {
updateMolangVar()
environment.setSimpleVariable("instructionChangeTime", DoubleValue(InstructionChangeTime))
val endOfAnim = if (currentCamera != null) cameraTimer + 1/20 >= currentCamera.length else true
val nextState = evaluateTransitions(currentState, endOfAnim)
if (nextState != null) {
nextCameraState = nextState.targetState
while (true) {
val newState = currentManager.states[nextCameraState]!!
val newCamera = BattleCameras.cameras[newState.camera]
val newEndOfAnim = if (newCamera != null) newCamera.length == 0.0 else true
val newNextState = evaluateTransitions(newState, newEndOfAnim)
if (newNextState == null) {
break
}
else {
nextCameraState = newNextState.targetState
}
}
calculateNextState = false
}
}
if (InstructionJustChanged) {
InstructionJustChanged = false
Instruction = "NONE"
}
}
private fun evaluateTransitions(state: BattleCameraState, checkEnd: Boolean) : BattleCameraTransition? {
var nextState = state.listOfTransitions.firstOrNull { !it.checkOnEnd && runtime.resolveBoolean(it.condition) }
if (nextState == null && checkEnd) {
nextState = state.listOfTransitions.firstOrNull { it.checkOnEnd && runtime.resolveBoolean(it.condition) }
}
return nextState
}
var cameraPosition: Vec3 = Vec3.ZERO
var cameraRotation: Vector3f = Vector3f()
fun updateCameraData(delta: Double) {
if (!shouldUseStadiumCamera()) return
val environment = runtime.environment
updateMolangVar()
environment.setSimpleVariable("instructionChangeTime", DoubleValue(InstructionChangeTime + delta/20))
val currentManager = BattleCameraManagers.managers[activeCameraManager]!!
val currentState = currentManager.states[currentCameraState]!!
val currentCamera = BattleCameras.cameras[currentState.camera]
if (currentCamera != null) {
var renderTime = cameraTimer + delta/20
if (renderTime > currentCamera.length && currentCamera.length != 0.0 && !calculateNextState) {
val newState = currentManager.states[nextCameraState]!!
val newCamera = BattleCameras.cameras[newState.camera] ?: return
environment.setSimpleVariable("anim_time", DoubleValue(renderTime - currentCamera.length))
cameraPosition = newCamera.resolvePosition(renderTime - currentCamera.length, battleYRot)
cameraRotation = newCamera.resolveRotation(renderTime - currentCamera.length, battleYRot)
}
else {
if (renderTime > currentCamera.length && currentCamera.length != 0.0) renderTime = currentCamera.length
environment.setSimpleVariable("anim_time", DoubleValue(renderTime))
cameraPosition = currentCamera.resolvePosition(renderTime, battleYRot)
cameraRotation = currentCamera.resolveRotation(renderTime, battleYRot)
}
cameraPosition = cameraPosition.add(battleCenter)
}
}
}
@@ -1,119 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.util.*
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import java.lang.reflect.Type
class BattleCameraManagerEntry(
var id: ResourceLocation,
val states: MutableMap<String, BattleCameraState>,
val startingState: String
) {
fun encode(buf: RegistryFriendlyByteBuf) {
buf.writeIdentifier(id)
buf.writeMap(states, {_, key -> buf.writeString(key)}, {_, value -> value.encode(buf)})
buf.writeString(startingState)
}
companion object {
fun decode(buf: RegistryFriendlyByteBuf): BattleCameraManagerEntry {
val id = buf.readIdentifier()
val states = buf.readMap({buf.readString()}, {BattleCameraState.decode(buf)}).toMutableMap()
val startState = buf.readString()
return BattleCameraManagerEntry(id, states, startState)
}
}
}
class BattleCameraState(
val camera: ResourceLocation,
val target: StadiumCameraTarget,
val listOfTransitions: MutableList<BattleCameraTransition>,
val loop: Boolean
) {
fun encode(buf: RegistryFriendlyByteBuf) {
buf.writeIdentifier(camera)
buf.writeEnum(target)
buf.writeCollection(listOfTransitions) {_, transition -> transition.encode(buf)}
buf.writeBoolean(loop)
}
companion object {
fun decode(buf: RegistryFriendlyByteBuf): BattleCameraState {
val camera = buf.readIdentifier()
val target = buf.readEnum(StadiumCameraTarget::class.java)
val listOfTransitions = buf.readList { BattleCameraTransition.decode(buf) }.toMutableList()
val loop = buf.readBoolean()
return BattleCameraState(camera, target, listOfTransitions, loop)
}
}
}
class BattleCameraTransition(
val targetState: String,
val checkOnEnd: Boolean,
val condition: Expression
) {
fun encode(buf: RegistryFriendlyByteBuf) {
buf.writeString(targetState)
buf.writeBoolean(checkOnEnd)
buf.writeString(condition.originalString)
}
companion object {
fun decode(buf: RegistryFriendlyByteBuf): BattleCameraTransition {
val targetState = buf.readString()
val checkOnEnd = buf.readBoolean()
val condition = buf.readString().asExpression()
return BattleCameraTransition(targetState, checkOnEnd, condition)
}
}
}
enum class StadiumCameraTarget {
CENTRE,
ACTIVE_POKEMON,
ENEMY_POKEMON,
TRAINER
}
object BattleCameraManagerAdapter: JsonDeserializer<BattleCameraManagerEntry> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): BattleCameraManagerEntry {
if (json is JsonObject) {
val states = mutableMapOf<String, BattleCameraState>()
json["states"]?.asJsonObject?.entrySet()?.forEach { (name, state) ->
val camera = state.asJsonObject["camera"]?.asString?.asResource() ?: "".asResource()
val target = when (state.asJsonObject["target"]?.asString) {
"CENTRE" -> StadiumCameraTarget.CENTRE
"ACTIVE_POKEMON" -> StadiumCameraTarget.ACTIVE_POKEMON
"ENEMY_POKEMON" -> StadiumCameraTarget.ENEMY_POKEMON
"TRAINER" -> StadiumCameraTarget.TRAINER
else -> StadiumCameraTarget.CENTRE
}
val listOfTransitions = mutableListOf<BattleCameraTransition>()
state.asJsonObject["transitions"]?.asJsonArray?.forEach {
val targetState = it.asJsonObject["targetState"]?.asString ?: ""
val checkOnEnd = it.asJsonObject["checkOnEnd"]?.asBoolean ?: false
val condition = it.asJsonObject["condition"]?.asString?.asExpression() ?: "0.0".asExpression()
listOfTransitions.add(BattleCameraTransition(targetState, checkOnEnd, condition))
}
val loop = state.asJsonObject["loop"]?.asBoolean ?: false
states[name] = BattleCameraState(camera, target, listOfTransitions, loop)
}
val startingState = json["startingState"].asString ?: ""
return BattleCameraManagerEntry(cobblemonResource(""), states, startingState)
}
throw IllegalStateException("StadiumCameraManager could not be parsed")
}
}
@@ -1,44 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object BattleCameraManagers: JsonDataRegistry<BattleCameraManagerEntry> {
override val id = cobblemonResource("stadium_managers")
override val type = PackType.SERVER_DATA
override val gson: Gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(BattleCameraManagerEntry::class.java, BattleCameraManagerAdapter)
.create()
override val typeToken: TypeToken<BattleCameraManagerEntry> = TypeToken.get(BattleCameraManagerEntry::class.java)
override val resourcePath = "stadium_managers"
val managers = mutableMapOf<ResourceLocation, BattleCameraManagerEntry>()
override fun reload(data: Map<ResourceLocation, BattleCameraManagerEntry>) {
managers.clear()
data.forEach { location, manager ->
manager.id = location
managers[location] = manager
}
observable.emit(this)
}
override val observable = SimpleObservable<BattleCameraManagers>()
override fun sync(player: ServerPlayer) {
BattleCameraManagersSyncPacket(managers.values).sendToPlayer(player)
}
}
@@ -1,27 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.net.messages.client.data.DataRegistrySyncPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class BattleCameraManagersSyncPacket(managers: Collection<BattleCameraManagerEntry>) :
DataRegistrySyncPacket<BattleCameraManagerEntry, BattleCameraManagersSyncPacket>(managers) {
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: BattleCameraManagerEntry) {
entry.encode(buffer)
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): BattleCameraManagerEntry? {
return BattleCameraManagerEntry.decode(buffer)
}
override fun synchronizeDecoded(entries: Collection<BattleCameraManagerEntry>) {
BattleCameraManagers.reload(entries.associateBy { it.id })
}
override val id = ID
companion object {
val ID = cobblemonResource("stadium_camera_managers_sync")
fun decode(buffer: RegistryFriendlyByteBuf) = BattleCameraManagersSyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
}
@@ -1,29 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.writeIdentifier
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
import java.util.*
class BattleCameraPacket(
val joiningBattle: UUID,
val cameraManager: ResourceLocation,
val center: Vec3,
val yRot: Float
): NetworkPacket<BattleCameraPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(this.joiningBattle)
buffer.writeIdentifier(cameraManager)
buffer.writeVec3(center)
buffer.writeFloat(yRot)
}
companion object {
val ID = cobblemonResource("stadium_join")
fun decode(buffer: RegistryFriendlyByteBuf) = BattleCameraPacket(buffer.readUUID(), buffer.readIdentifier(), buffer.readVec3(), buffer.readFloat())
}
}
@@ -1,10 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import net.minecraft.client.Minecraft
object BattleCameraSetupHandler: ClientNetworkPacketHandler<BattleCameraPacket> {
override fun handle(packet: BattleCameraPacket, client: Minecraft) {
BattleCameraManagerClient.handleJoin(packet.joiningBattle, packet.cameraManager, packet.center, packet.yRot)
}
}
@@ -1,48 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
object BattleCameras: JsonDataRegistry<BattleCameraEntry> {
override val id = cobblemonResource("stadium_cameras")
override val type = PackType.SERVER_DATA
override val gson: Gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(BattleCameraEntry::class.java, BattleCameraEntry.CameraEntryAdapter)
.create()
override val typeToken: TypeToken<BattleCameraEntry> = TypeToken.get(BattleCameraEntry::class.java)
override val resourcePath = "stadium_cameras"
val cameras = mutableMapOf<ResourceLocation, BattleCameraEntry>()
override fun reload(data: Map<ResourceLocation, BattleCameraEntry>) {
cameras.clear()
data.forEach { location, camera ->
camera.id = location
cameras[location] = camera
}
observable.emit(this)
}
override val observable = SimpleObservable<BattleCameras>()
override fun sync(player: ServerPlayer) {
BattleCamerasSyncPacket(cameras.values).sendToPlayer(player)
}
fun getMoveCamera(move: String): BattleCameraEntry? {
return cameras.values.firstOrNull { it.move == move }
}
}
@@ -1,27 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.net.messages.client.data.DataRegistrySyncPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class BattleCamerasSyncPacket(cameras: Collection<BattleCameraEntry>) :
DataRegistrySyncPacket<BattleCameraEntry, BattleCamerasSyncPacket>(cameras) {
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: BattleCameraEntry) {
entry.encode(buffer)
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): BattleCameraEntry? {
return BattleCameraEntry.decode(buffer)
}
override fun synchronizeDecoded(entries: Collection<BattleCameraEntry>) {
BattleCameras.reload(entries.associateBy { it.id })
}
override val id = ID
companion object {
val ID = cobblemonResource("stadium_cameras_sync")
fun decode(buffer: RegistryFriendlyByteBuf) = BattleCamerasSyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
}
@@ -1,12 +0,0 @@
package com.cobblemon.mod.common.stadium
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.phys.Vec3
import java.util.*
abstract class StadiumBattleDefenition {
abstract fun playerPositions(): MutableList<Pair<Int, Vec3>>
abstract fun pokemonPositions(): MutableList<Pair<Int, Vec3>>
}
@@ -1,4 +0,0 @@
package com.cobblemon.mod.common.stadium
class StadiumFormat {
}
@@ -1,34 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import java.util.UUID
object StadiumServerManager {
val activeStadiumBattles = mutableMapOf<UUID, StadiumBattleInstance>()
fun createNewStadiumBattle(battle: UUID, managerResource: ResourceLocation, participants: MutableList<UUID> = mutableListOf<UUID>()) {
val newInstance = StadiumBattleInstance(battle, managerResource)
newInstance.participants = participants
activeStadiumBattles[battle] = newInstance
}
fun terminateNewStadiumBattle(battle: UUID) {
activeStadiumBattles.remove(battle)
}
fun addPlayerToStadiumBattle(battle: UUID, player: UUID) {
val instance = activeStadiumBattles[battle]
if (instance != null) {
if (!instance.participants.contains(player)) instance.participants.add(player)
}
}
}
class StadiumBattleInstance(
val stadiumBattleId: UUID,
val managerResource: ResourceLocation
){
var participants = mutableListOf<UUID>()
}
@@ -1,73 +0,0 @@
package com.cobblemon.mod.common.stadium
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.battles.BattleBuilder
import com.cobblemon.mod.common.battles.SuccessfulBattleStart
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.party
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.phys.Vec3
class StadiumSingleBattle: StadiumBattleDefenition() {
override fun playerPositions(): MutableList<Pair<Int, Vec3>> {
return mutableListOf()
}
override fun pokemonPositions(): MutableList<Pair<Int, Vec3>> {
return mutableListOf()
}
/*
@JvmField
val MAX_PLAYERS = 2
var player1Position = Vec3(0.0,0.0,0.0)
var player2Position = Vec3(0.0,0.0,0.0)
var player1PokemonPosition = Vec3(0.0,0.0,0.0)
var player2PokemonPosition = Vec3(0.0,0.0,0.0)
var activeBattle: PokemonBattle? = null
override fun readyForBattle(): Boolean {
return this.getPlayers().count() == MAX_PLAYERS
}
override fun canAddPlayer(player: ServerPlayer): Boolean {
val party = player.party()
return party.occupied() > 0
}
override fun addPlayer(player: ServerPlayer) {
if (canAddPlayer(player)) {
participants.add(player.uuid)
player.sendPacket(BattleCameraPacket(player.uuid, BattleCameraManagers.managers.keys.first(), Vec3.ZERO, 0F))
}
}
override fun getPlayers(): MutableList<ServerPlayer> {
return participants.mapNotNull { it.getPlayer() }.toMutableList()
}
override fun initializeBattle() {
if (!readyForBattle()) return
val players = getPlayers()
val player1 = players[0]
val player2 = players[1]
val battleResult = BattleBuilder.pvp1v1(player1, player2)
var errored = false
battleResult.ifErrored { errored = true }
if (errored) return
val battle = (battleResult as SuccessfulBattleStart).battle
}
override fun getCurrentCamera(): BattleCameraEntry {
TODO("Not yet implemented")
}
*/
}
@@ -1,14 +0,0 @@
package com.cobblemon.mod.common.util
import com.cobblemon.mod.common.util.math.toEulerXYZ
import net.minecraft.client.Camera
import org.joml.Quaternionf
fun Camera.setRotation(yaw: Float, pitch: Float, roll: Float) {
this.xRot = yaw
this.yRot = pitch
this.rotation.rotationYXZ(Math.PI.toFloat() - pitch * (Math.PI.toFloat() / 180F),-yaw * (Math.PI.toFloat() / 180F),roll * (Math.PI.toFloat() / 180F))
Camera.FORWARDS.rotate(this.rotation, this.forwards)
Camera.UP.rotate(this.rotation, this.up)
Camera.LEFT.rotate(this.rotation, this.left)
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.util.adapters
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
@@ -29,7 +28,7 @@ import java.lang.reflect.Type
*
* @author landonjw
*/
object RidingBehaviourSettingsAdapter : JsonDeserializer<RidingBehaviourSettings?> {
object RidingBehaviourSettingsAdapter : JsonDeserializer<RidingBehaviourSettings> {
val types: MutableMap<ResourceLocation, Class<out RidingBehaviourSettings>> = mutableMapOf(BirdAirBehaviour.KEY to BirdAirSettings::class.java,
DolphinBehaviour.KEY to DolphinSettings::class.java,
GenericLandBehaviour.KEY to GenericLandSettings::class.java,
@@ -51,27 +50,21 @@ object RidingBehaviourSettingsAdapter : JsonDeserializer<RidingBehaviourSettings
RunStrategy.key to CompositeSettings::class.java,
)
override fun deserialize(element: JsonElement, type: Type, context: JsonDeserializationContext): RidingBehaviourSettings? {
override fun deserialize(element: JsonElement, type: Type, context: JsonDeserializationContext): RidingBehaviourSettings {
val root = element.asJsonObject
val key = root.get("key").asString
val keyIdentifier = key.asIdentifierDefaultingNamespace()
if (keyIdentifier == CompositeBehaviour.KEY) {
val strategy = root.get("transitionStrategy").asString
val strategyIdentifier = strategy.asIdentifierDefaultingNamespace()
val behaviourType = types[strategyIdentifier]
if (behaviourType == null) {
Cobblemon.LOGGER.warn("Unknown strategy: $strategyIdentifier for composite behaviour: $key. Skipping.")
return null
}
return context.deserialize(root, behaviourType)
val behaviourType = types[strategyIdentifier] ?: throw IllegalArgumentException("Unknown strategy for composite behaviour: $strategy")
val settings: RidingBehaviourSettings = context.deserialize(root, behaviourType)
return settings
}
else {
val behaviourType = types[keyIdentifier]
if (behaviourType == null) {
Cobblemon.LOGGER.warn("Unknown riding behaviour encountered: $key. Skipping.")
return null
}
return context.deserialize(element, behaviourType)
val behaviourType = types[keyIdentifier] ?: throw IllegalArgumentException("Unknown behaviour: $key")
val settings: RidingBehaviourSettings = context.deserialize(element, behaviourType)
return settings
}
}
}
@@ -1,41 +0,0 @@
{
"format_version": "1.8.0",
"animations": {
"animation.rotom_drone.sleep": {
"loop": true,
"bones": {
"body": {
"rotation": [
"math.sin(query.anim_time * 120) * 6",
0,
"math.cos(query.anim_time * 100) * 4"
],
"position": [
0,
"18 + math.sin(query.anim_time * 90) * 1",
0
]
},
"head": {
"relative_to": {
"rotation": "entity"
},
"rotation": [
0,
0,
0.01
]
}
}
},
"animation.rotom_drone.circle": {
"loop": true,
"bones": {
"rotom_drone": {
"rotation": [30, "90 - q.anim_time*90", 0],
"position": ["48*math.cos(q.anim_time*90)", 64, "48*math.sin(q.anim_time*90)"]
}
}
}
}
}
@@ -1,208 +0,0 @@
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.drone_rotom",
"texture_width": 64,
"texture_height": 32,
"visible_bounds_width": 8,
"visible_bounds_height": 6.5,
"visible_bounds_offset": [0, 2.75, 0]
},
"bones": [
{
"name": "rotom_drone",
"pivot": [0, 0, 0]
},
{
"name": "body",
"parent": "rotom_drone",
"pivot": [0, 8, 0]
},
{
"name": "head",
"parent": "body",
"pivot": [0, 6.5, 0],
"cubes": [
{"origin": [-5.5, 6.5, -3.5], "size": [11, 6, 7], "uv": [0, 0]}
]
},
{
"name": "eyes",
"parent": "head",
"pivot": [0.5, 6.5, -2.5]
},
{
"name": "eye_right",
"parent": "eyes",
"pivot": [-2.9, 9.5, -3.5],
"rotation": [0, 0, -7.5],
"cubes": [
{"origin": [-3.9, 7.5, -3.5], "size": [2, 4, 0], "inflate": 0.01, "uv": [0, 0], "mirror": true},
{"origin": [-3.9, 9, -3.5], "size": [2, 1, 0], "inflate": 0.02, "uv": [0, 6], "mirror": true}
]
},
{
"name": "pupil_right",
"parent": "eye_right",
"pivot": [-2.9, 9.5, -3.5],
"cubes": [
{"origin": [-3.4, 8, -3.5], "size": [1, 3, 0], "inflate": 0.03, "uv": [5, 0], "mirror": true}
]
},
{
"name": "eye_left",
"parent": "eyes",
"pivot": [2.9, 9.5, -3.5],
"rotation": [0, 0, 7.5],
"cubes": [
{"origin": [1.9, 7.5, -3.5], "size": [2, 4, 0], "inflate": 0.01, "uv": [0, 13]},
{"origin": [1.9, 9, -3.5], "size": [2, 1, 0], "inflate": 0.02, "uv": [0, 5]}
]
},
{
"name": "pupil_left",
"parent": "eye_left",
"pivot": [2.9, 9.5, -3.5],
"cubes": [
{"origin": [2.4, 8, -3.5], "size": [1, 3, 0], "inflate": 0.03, "uv": [6, 13]}
]
},
{
"name": "mouth_smile",
"parent": "head",
"pivot": [0, 7.55, -3.5],
"cubes": [
{"origin": [-1, 6.95, -3.5], "size": [2, 1, 0], "inflate": 0.02, "uv": [36, 7]},
{"origin": [-1.5, 7.25, -3.5], "size": [1, 1, 0], "inflate": 0.01, "uv": [40, 7]},
{"origin": [0.5, 7.25, -3.5], "size": [1, 1, 0], "inflate": 0.01, "uv": [42, 7]}
]
},
{
"name": "mouth_closed",
"parent": "head",
"pivot": [0, 7.45, -3.4],
"cubes": [
{"origin": [-0.5, 6.95, -3.4], "size": [1, 1, 0], "inflate": 0.02, "uv": [38, 8]}
]
},
{
"name": "mouth_open",
"parent": "head",
"pivot": [0, 7.45, -3.4],
"cubes": [
{"origin": [-0.5, 6.95, -3.4], "size": [1, 1, 0], "inflate": 0.02, "uv": [36, 8]}
]
},
{
"name": "mouth_sad",
"parent": "head",
"pivot": [0, 7.6, -3.3],
"cubes": [
{"origin": [-1, 7.3, -3.3], "size": [2, 1, 0], "inflate": 0.02, "uv": [36, 9]},
{"origin": [-1.5, 7, -3.3], "size": [1, 1, 0], "inflate": 0.01, "uv": [40, 9]},
{"origin": [0.5, 7, -3.3], "size": [1, 1, 0], "inflate": 0.01, "uv": [42, 9]}
]
},
{
"name": "head_spike1",
"parent": "head",
"pivot": [0, 12.5, 0],
"cubes": [
{"origin": [-1.5, 11.5, -1.5], "size": [3, 4, 3], "uv": [29, 0]}
]
},
{
"name": "head_spike2",
"parent": "head_spike1",
"pivot": [0, 15.5, 0],
"cubes": [
{"origin": [-1, 14.5, -1], "size": [2, 4, 2], "uv": [8, 13]}
]
},
{
"name": "head_spike3",
"parent": "head_spike2",
"pivot": [0, 18.5, 0],
"cubes": [
{"origin": [-0.5, 17.5, -0.5], "size": [1, 3, 1], "uv": [44, 6]}
]
},
{
"name": "torso",
"parent": "body",
"pivot": [0, 7, 0],
"cubes": [
{"origin": [-2, 3, -1.5], "size": [4, 4, 3], "uv": [34, 10]}
]
},
{
"name": "camera",
"parent": "torso",
"pivot": [0, 4.65, -1.25],
"cubes": [
{"origin": [-1, 3.65, -2.75], "size": [2, 2, 2], "uv": [24, 13]},
{"origin": [-0.5, 4.15, -2.75], "size": [1, 1, 0], "inflate": 0.01, "uv": [38, 0]}
]
},
{
"name": "propeller_front_right",
"parent": "torso",
"pivot": [-1.5, 5, -1],
"rotation": [0, 45, 0],
"cubes": [
{"origin": [-2, 5, -3.5], "size": [1, 0, 3], "uv": [37, 0]},
{"origin": [-5.5, 5, -11.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "propeller_front_left",
"parent": "torso",
"pivot": [1.5, 5, -1],
"rotation": [0, -45, 0],
"cubes": [
{"origin": [1, 5, -3.5], "size": [1, 0, 3], "uv": [27, 0]},
{"origin": [-2.5, 5, -11.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "propeller_back_right",
"parent": "torso",
"pivot": [-1.5, 5, 1],
"rotation": [0, -45, 0],
"cubes": [
{"origin": [-2, 5, 0.5], "size": [1, 0, 3], "uv": [39, 0]},
{"origin": [-5.5, 5, 3.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "propeller_back_left",
"parent": "torso",
"pivot": [1.5, 5, 1],
"rotation": [0, 45, 0],
"cubes": [
{"origin": [1, 5, 0.5], "size": [1, 0, 3], "uv": [42, 3]},
{"origin": [-2.5, 5, 3.5], "size": [8, 0, 8], "uv": [40, 0]}
]
},
{
"name": "tail1",
"parent": "torso",
"pivot": [0, 3, 0],
"cubes": [
{"origin": [-1, 1, -1], "size": [2, 3, 2], "uv": [16, 13]}
]
},
{
"name": "tail2",
"parent": "tail1",
"pivot": [0, 1, 0],
"cubes": [
{"origin": [-0.5, -1, -0.5], "size": [1, 3, 1], "uv": [41, 3]}
]
}
]
}
]
}
@@ -1,8 +0,0 @@
{
"poses": {
"sleep": {
"poseTypes": ["SLEEP"],
"animations": ["q.bedrock('rotom_drone', 'sleep')"]
}
}
}
@@ -1,13 +0,0 @@
{
"name": "cobblemon:rotom_drone",
"order": 0,
"variations": [
{
"aspects": [],
"poser": "cobblemon:rotom_drone",
"model": "cobblemon:rotom_drone.geo",
"texture": "cobblemon:textures/drone/rotom_drone.png",
"layers": []
}
]
}
@@ -5,7 +5,7 @@
"identifier": "cobblemon:protect-block",
"basic_render_parameters": {
"material": "particles_add",
"texture": "textures/particles/moves/protect_block"
"texture": "textures/particles/protect-block"
}
},
"events": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:protect-blockchip",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/orb/xsfadeorblite"
"texture": "textures/particles/xs-fadeorb-lite"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:afterspark",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/afterspark"
"texture": "textures/particles/afterspark"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:capturesparks",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/capturesparks"
"texture": "textures/particles/capturesparks"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:capturestar",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/capturestar"
"texture": "textures/particles/capturestar"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:hisuiafterspark",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/hisuiafterspark"
"texture": "textures/particles/hisuiafterspark"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:hisuicapturestar",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/hisuicapturestar"
"texture": "textures/particles/hisuicapturestar"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:hisuipuff",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/hisuipuff"
"texture": "textures/particles/hisuipuff"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:hisuisendspark",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/hisuisendspark"
"texture": "textures/particles/hisuisendspark"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:hisuispark",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/hisuispark"
"texture": "textures/particles/hisuispark"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:hisuitrail",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/balls/hisuitrail"
"texture": "textures/particles/hisuitrail"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:ancient_pokeball_smoke",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/vanilla/big_smoke_white"
"texture": "textures/particles/white_vanilla_smoke"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:cherry_leaves_particle",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/vanilla/cherry_petal"
"texture": "textures/particles/cherry_petal"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:cherry_leaves_faint_particle",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/vanilla/cherry_petal"
"texture": "textures/particles/cherry_petal"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:confetti",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/confetti"
"texture": "textures/particles/confetti"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_bigsparkle",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/sparkle/bigsparkle"
"texture": "textures/particles/bigsparkle"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_buildup",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/orb/glowing_dots_cyan"
"texture": "textures/particles/glowing_dots_cyan"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_flyingsparkles",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/sparkle/glowingsparkle_yellow"
"texture": "textures/particles/glowing_sparkle_yellow"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_godrays",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/lightbeam"
"texture": "textures/particles/lightbeam"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_godrays_small",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/lightbeam"
"texture": "textures/particles/lightbeam"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_implode",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/orb/glowing_dots_cyan"
"texture": "textures/particles/glowing_dots_cyan"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_obscuring",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/smoke/glowingsmoke_cyan"
"texture": "textures/particles/glowing_smoke_cyan"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_smokeburst",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/smoke/glowingsmoke_cyan"
"texture": "textures/particles/glowing_smoke_cyan"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_sparkle",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/sparkle/glowingsparkle_yellow"
"texture": "textures/particles/glowing_sparkle_yellow"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_sparkleburst",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/sparkle/glowingsparkle_yellow"
"texture": "textures/particles/glowing_sparkle_yellow"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:evo_sparklefetti",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/star"
"texture": "textures/particles/star"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:accessory_fish_splash",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/water/fishsplash"
"texture": "cobblemon:fish_splash"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:big_fish_splash",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/water/fishsplash"
"texture": "cobblemon:fish_splash"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:bob_splash",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/water/fishsplash"
"texture": "cobblemon:fish_splash"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:fishing_bobber_big_ripple",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/water/water_ripple"
"texture": "textures/particles/water_ripple"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:fishing_bobber_ripple",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/water/water_ripple"
"texture": "textures/particles/water_ripple"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:fishing_surface_ripple",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/water/water_ripple"
"texture": "textures/particles/water_ripple"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:fishing_wake",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/white"
"texture": "textures/particles/white"
}
},
"events": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:small_fish_splash",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/water/fishsplash"
"texture": "cobblemon:fish_splash"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:splash",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/water/rainsplash"
"texture": "textures/particles/rain_splash"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:burn_actor",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/generic/fire/flame"
"texture": "textures/particles/large-flame"
}
},
"events": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:burn_actorresidual",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/fire/ember"
"texture": "textures/particles/ember"
}
},
"components": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:confused_actor",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/confusion_bird"
"texture": "textures/particles/chirp"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:confused_bird2",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/confusion_bird"
"texture": "textures/particles/chirp"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:confused_bird3",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/confusion_bird"
"texture": "textures/particles/chirp"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:confused_bird4",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/confusion_bird"
"texture": "textures/particles/chirp"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:confused_bird5",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/confusion_bird"
"texture": "textures/particles/chirp"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:confused_bird6",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/confusion_bird"
"texture": "textures/particles/chirp"
}
},
"curves": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:infatuate_actor",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/infatuation_heart"
"texture": "textures/particles/attract_heart"
}
},
"events": {
@@ -5,7 +5,7 @@
"identifier": "cobblemon:infatuate_actorresidual",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/status/infatuation_heart"
"texture": "textures/particles/attract_heart"
}
},
"components": {
Loaded 100 of 564 files, more files were not shown because too many files have changed in this diff. Show more