Compare commits

..
Author SHA1 Message Date
Konoha 642ad77228 Attempt to allow for visibility preconditions on NPCs 2025-09-09 21:28:29 -04:00
770 changed files with 25183 additions and 123872 deletions

No files matched your search

-3
View File
@@ -15,9 +15,6 @@
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
### Fixes
- Fixed Pokémon with a moving walk target frequently spinning in place as they re-calculate their path.
### MoLang & Datapacks
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
-1
View File
@@ -22,7 +22,6 @@
### Changes
- Slots in pastures will now have darker backgrounds and italicised Pokémon names if the Pokémon is not owned by the player.
- The pasture slot will show the name of the Pokémon owner when hovered if the Pokémon is not owned.
- Increased the default PC box count from 30 to 40 to accommodate the fact that we have way more Pokémon now. If you've changed this config option then you'll need to edit it again after starting up with 1.7!
### Developer
- Added `RenamePCBoxEvent.Pre` and `RenamePCBoxEvent.Post` events to prevent players from renaming a box or changing their input.
-1
View File
@@ -4,7 +4,6 @@
### Fixes
- Fixed the "enabled" property in spawn files not actually being respected. Where do they find these devs?
- Fixed Terralith's shrubland not counting as plains for spawning purposes the way it was intended to.
### Developer
- Spawning Influences now have the context of what the other buckets are when adjusting bucket weights. This will break existing influences that do bucket weight adjustment.
-37
View File
@@ -2,7 +2,6 @@
## [1.7.0 (Month xth, 2025)](#1-7-0)
### Additions
- Added `/npcdelete` uuid parameter support and usuable to console
- Added `/spectateBattle <player>` command to spectate battles without having to manually walk up to the target.
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
@@ -144,8 +143,6 @@
- Dedenne
- Noibat
- Noivern
- Furfrou
- You can change Furfrou's form by using shears on it while it holds a certain dye in its cosmetic slot.
#### Gen 7
- Yungoos
@@ -280,19 +277,6 @@
- Parasect
- Bastiodon
- Amaura
- Varoom
- Revavroom
- Bouffalant
- Lickilicky
- Blaziken
- Rhyhorn
- Rhydon
- Rhyperior
- Tauros
- Tauros - Paldea Aqua
- Tauros - Paldea Blaze
- Tauros - Paldea Combat
- Dewgong
### Model updates for the following Pokémon
- Cleffa
@@ -399,10 +383,6 @@
- Starly
- Staravia
- Staraptor
- Varoom
- Revavroom
- Nickit
- Thievul
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
@@ -466,7 +446,6 @@
- Updated `EvolutionCompleteEvent` parameters to include the Source Pokemon that evolved.
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
- Roseli Berry Trees now naturally generate in their preferred biomes.
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -549,8 +528,6 @@
- Fixed Pokémon with alternate forms being created with an incorrect 'forced' tag on their ability.
- Fixed busted abilities and moves in Pokémon data due to removed datapacks etc. causing storage corruption. It now just rerolls their ability / uses Tackle.
- Fixed singular Pokémon corruption causing entire storage corruption. Storages will now skip corrupted Pokémon and print an error to console.
- Fixed species additions not being able to properly mark a species as implemented.
- Fixed Pokémon item models not showing a glint when enchanted.
### Developer
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
@@ -681,20 +658,6 @@
- Added `q.pokemon.is_busy` Returns whether the Pokémon is busy.
- Added `q.pokemon.is_rideable` Returns whether the Pokémon is rideable.
- Fixed `background` field and added `textColor` field for dialogues.
- Added support to reload some data registries:
- MoLang scripts
- Callbacks
- Spawn Detail Presets
- Spawn Pools
- Spawn Rules
- Cosmetic Items
- Dialogues
- Fossils
- Natural Materials
- Action Effects
- Mechanics
- Unlockable Wallpapers
- Starter Data
### Particles
Added new/updated particles for the following moves:
+1 -8
View File
@@ -31,7 +31,6 @@ repositories {
maven(url = "${rootProject.projectDir}/deps")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.neoforged.net/releases")
maven(url = "https://maven.gegy.dev")
mavenLocal()
}
@@ -45,12 +44,6 @@ dependencies {
modCompileOnly(libs.bundles.common.integrations.compileOnly) {
isTransitive = false
}
// LambDynamicLights is handled differently because we need the Mojang-mappings version of it.
modCompileOnly(libs.lambDynamicLights) {
capabilities {
requireCapability("dev.lambdaurora.lambdynamiclights:api-mojmap")
}
}
// Flywheel has no common dep so just pick one and don't use any platform specific code in common
// modCompileOnly(libs.flywheelFabric)
@@ -101,4 +94,4 @@ sourceSets {
}
}
}
}
}
@@ -0,0 +1,102 @@
/*
* 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.compat;
import com.cobblemon.mod.common.CobblemonEntities;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import dev.lambdaurora.lambdynlights.LambDynLights;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandler;
import dev.lambdaurora.lambdynlights.api.DynamicLightHandlers;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import net.minecraft.world.entity.player.Player;
// Java class due to relying on entrypoint on Fabric side
public class LambDynamicLightsCompat {
// This is all called on client side
// These things are checked every tick so try to keep it as free as possible
// That being said it's unlikely school Toshiba person is running dynamic lighting and shaders to enjoy this
// Make sure not to call anything Fabric specific :)
public static void hookCompat() {
// Pokémon entities
DynamicLightHandlers.registerDynamicLightHandler(
CobblemonEntities.POKEMON,
DynamicLightHandler.makeHandler(
LambDynamicLightsCompat::resolvedPokemonLightLevel,
pokemon -> false
)
);
// Shouldered Pokémon
DynamicLightHandlers.registerDynamicLightHandler(
EntityType.PLAYER,
DynamicLightHandler.makeHandler(
LambDynamicLightsCompat::resolvedPlayerLightLevel,
player -> false
)
);
}
private static int resolvedPokemonLightLevel(PokemonEntity pokemon) {
var underwater = !pokemon.level().getFluidState(BlockPos.containing(pokemon.getX(), pokemon.getEyeY(), pokemon.getZ())).isEmpty();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty()?0:LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int resolvedPlayerLightLevel(Player player) {
var underwater = !player.level().getFluidState(BlockPos.containing(player.getX(), player.getEyeY(), player.getZ())).isEmpty();
final int leftShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft()), underwater);
final int rightShoulderLightLevel = extractShoulderLightLevel(PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight()), underwater);
final int itemLightLevel = customHeldItemLightLevel(player.getMainHandItem(), player.getOffhandItem());
return Math.max(itemLightLevel, Math.max(leftShoulderLightLevel, rightShoulderLightLevel));
}
private static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
return Optional.empty();
}
return Optional.of(form.getLightingData().getLightLevel());
}
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 :LambDynLights.getLuminanceFromItemStack(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
private static int customHeldItemLightLevel(ItemStack mainhand, ItemStack offhand) {
return (mainhand.getItem() instanceof PokedexItem || offhand.getItem() instanceof PokedexItem) ? 13 : 0;
}
private static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -1,28 +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.compat.lambdynamiclights;
import com.cobblemon.mod.common.pokemon.FormData;
import com.cobblemon.mod.common.pokemon.lighthing.LightingData;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public abstract class CustomLuminance {
protected static Optional<Integer> extractFormLightLevel(@NotNull FormData form, boolean underwater) {
if (form.getLightingData() == null || !liquidGlowModeSupport(form.getLightingData().getLiquidGlowMode(), underwater)) {
return Optional.empty();
}
return Optional.of(form.getLightingData().getLightLevel());
}
protected static boolean liquidGlowModeSupport(@NotNull LightingData.LiquidGlowMode liquidGlowMode, boolean underwater) {
return underwater ? liquidGlowMode.getGlowsUnderwater() : liquidGlowMode.getGlowsInLand();
}
}
@@ -1,42 +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.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import dev.lambdaurora.lambdynlights.api.DynamicLightsContext;
import dev.lambdaurora.lambdynlights.api.DynamicLightsInitializer;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
public class LambDynamicLightsInitializer implements DynamicLightsInitializer {
public static final EntityLuminance.Type POKEMON_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokemon"),
PokemonLuminance.INSTANCE
);
public static final EntityLuminance.Type PLAYER_LUMINANCE = EntityLuminance.Type.registerSimple(
ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "shoulder"),
PlayerLuminance.INSTANCE
);
@Override
public void onInitializeDynamicLights(DynamicLightsContext context) {
Cobblemon.LOGGER.info("Lamb Dynamic Lights compatibility enabled");
context.entityLightSourceManager().onRegisterEvent().register(ctx -> {
ctx.register(EntityType.PLAYER, PlayerLuminance.INSTANCE);
});
}
@SuppressWarnings({"removal", "UnstableApiUsage"})
@Override
public void onInitializeDynamicLights(ItemLightSourceManager itemLightSourceManager) {}
}
@@ -1,72 +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.compat.lambdynamiclights;
import com.cobblemon.mod.common.Cobblemon;
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
public final class PlayerLuminance extends CustomLuminance implements EntityLuminance {
public static final PlayerLuminance INSTANCE = new PlayerLuminance();
private static final TagKey<Item> POKEDEX = TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(Cobblemon.MODID, "pokedex"));
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.PLAYER_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Entity entity) {
if (entity instanceof Player player) {
final int shoulderLightLevel = shoulderLuminance(itemLightSourceManager, player);
final int itemLightLevel = player.isHolding(stack -> stack.is(POKEDEX)) ? 13 : 0;
return Math.max(shoulderLightLevel, itemLightLevel);
} else {
return 0;
}
}
private @Range(from = 0L, to = 15L) static int shoulderLuminance(@NotNull ItemLightSourceManager itemLightSourceManager, @NotNull Player player) {
final var leftShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityLeft());
final var rightShoulder = PokemonOnShoulderRenderer.shoulderDataFrom(player.getShoulderEntityRight());
if (leftShoulder == null && rightShoulder == null) {
return 0;
}
final int leftShoulderLightLevel = extractShoulderLightLevel(leftShoulder, itemLightSourceManager, player.isUnderWater());
final int rightShoulderLightLevel = extractShoulderLightLevel(rightShoulder, itemLightSourceManager, player.isUnderWater());
return Math.max(leftShoulderLightLevel, rightShoulderLightLevel);
}
private static int extractShoulderLightLevel(@Nullable PokemonOnShoulderRenderer.ShoulderData shoulderData, @NotNull ItemLightSourceManager itemLightSourceManager, boolean underwater) {
if (shoulderData == null) {
return 0;
}
ItemStack item = shoulderData.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(shoulderData.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
}
}
@@ -1,45 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.compat.lambdynamiclights;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import dev.lambdaurora.lambdynlights.api.entity.luminance.EntityLuminance;
import dev.lambdaurora.lambdynlights.api.item.ItemLightSourceManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
public final class PokemonLuminance extends CustomLuminance implements EntityLuminance {
public static final PokemonLuminance INSTANCE = new PokemonLuminance();
private PokemonLuminance() {}
@Override
public @NotNull Type type() {
return LambDynamicLightsInitializer.POKEMON_LUMINANCE;
}
@Override
public @Range(from = 0L, to = 15L) int getLuminance(
@NotNull ItemLightSourceManager itemLightSourceManager,
@NotNull Entity entity
) {
if (entity instanceof PokemonEntity pokemon) {
boolean underwater = pokemon.isUnderWater();
ItemStack item = pokemon.getShownItem();
int itemLightLevel = item.isEmpty() ? 0 : itemLightSourceManager.getLuminance(item, underwater);
int formLightLevel = extractFormLightLevel(pokemon.getForm(), underwater).orElse(0);
return Math.max(itemLightLevel, formLightLevel);
} else {
return 0;
}
}
}
@@ -25,9 +25,9 @@ public class TargetingConditionsMixin {
@Inject(method = "test", at = @At("HEAD"), cancellable = true)
public void test(@Nullable LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> ci) {
if (targetEntity instanceof ServerPlayer player) {
if (player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE)) return;
boolean canTargetInBattle = player.level().getGameRules().getBoolean(CobblemonGameRules.MOB_TARGET_IN_BATTLE);
boolean targetInBattle = Cobblemon.INSTANCE.getBattleRegistry().getBattleByParticipatingPlayer(player) != null;
if (targetInBattle) ci.setReturnValue(false);
if (!canTargetInBattle && targetInBattle) ci.setReturnValue(false);
}
}
}
@@ -31,15 +31,14 @@ public class AbstractClientPlayerMixin {
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
cir.setReturnValue(usageContext.getFovMultiplier());
} else {
// Only modify fov through riding if the pokedex is not open
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
}
// Return custom fov mult for riding
cir.setReturnValue(ride.rideFovMult());
}
LocalPlayer player = (LocalPlayer) (Object) this;
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
PokemonEntity ride = (PokemonEntity) player.getVehicle();
//Return custom fov mult
cir.setReturnValue(ride.rideFovMult());
}
}
}
@@ -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.player.MountedPlayerRenderer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.model.AgeableListModel;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.model.Model;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* This mixin adds to the head of the renderToBuffer function and allows us to detect if it is a HumanoidModel and
* is being rendered as part of a player in the world who is riding a PokemonEntity. If so we need to obey any
* root animations that have been applied earlier.
*
* @author Hiroku
* @since May 16, 2025
*/
@Mixin(AgeableListModel.class)
public class AgeableListModelMixin {
@Inject(method = "renderToBuffer", at = @At("HEAD"))
private void cobblemon$renderToBuffer(
PoseStack poseStack,
VertexConsumer vertexConsumer,
int packedLight,
int packedOverlay,
int colour,
CallbackInfo ci
) {
Model model = (Model) (Object) this;
if (model instanceof HumanoidModel<?> && MountedPlayerRenderer.shouldApplyRootAnimation) {
MountedPlayerRenderer.INSTANCE.animateRoot(poseStack);
}
}
}
@@ -19,17 +19,20 @@ import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
@Mixin(HumanoidModel.class)
public class HumanoidModelMixin {
@Unique HashMap<String, ModelPart> relevantPartsByName = new HashMap<>();
@Shadow @Final
public ModelPart head;
@Shadow @Final
public ModelPart hat;
@Shadow @Final
public ModelPart rightArm;
@Shadow @Final
public ModelPart leftArm;
@@ -37,8 +40,6 @@ public class HumanoidModelMixin {
public ModelPart rightLeg;
@Shadow @Final
public ModelPart leftLeg;
@Shadow @Final
public ModelPart body;
@Inject(method = "setupAnim*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/model/geom/ModelPart;copyFrom(Lnet/minecraft/client/model/geom/ModelPart;)V", shift = At.Shift.BEFORE))
public void cobblemon$animateRiders(
@@ -50,23 +51,19 @@ public class HumanoidModelMixin {
float headPitch,
CallbackInfo ci
) {
MountedPlayerRenderer.shouldApplyRootAnimation = false;
if (MountedPlayerRenderer.relevantPartsByName.size() <= 1) {
MountedPlayerRenderer.relevantPartsByName.put("arm_left", leftArm);
MountedPlayerRenderer.relevantPartsByName.put("arm_right", rightArm);
MountedPlayerRenderer.relevantPartsByName.put("leg_left", leftLeg);
MountedPlayerRenderer.relevantPartsByName.put("leg_right", rightLeg);
MountedPlayerRenderer.relevantPartsByName.put("head", head);
MountedPlayerRenderer.relevantPartsByName.put("hat", hat);
MountedPlayerRenderer.relevantPartsByName.put("torso", body);
if (relevantPartsByName.isEmpty()) {
relevantPartsByName.put("arm_left", leftArm);
relevantPartsByName.put("arm_right", rightArm);
relevantPartsByName.put("leg_left", leftLeg);
relevantPartsByName.put("leg_right", rightLeg);
relevantPartsByName.put("head", head);
}
Entity vehicle = entity.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity && entity instanceof OrientationControllable) {
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) ->
behaviour.shouldRotateRiderHead(settings, state, pokemonEntity)
);
var shouldRotatePlayerHead = pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
return behaviour.shouldRotateRiderHead(settings, state, pokemonEntity);
});
if (!shouldRotatePlayerHead) {
netHeadYaw = 0f;
@@ -79,8 +76,7 @@ public class HumanoidModelMixin {
if (!(entity instanceof AbstractClientPlayer player)) return;
MountedPlayerRenderer.shouldApplyRootAnimation = true;
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
MountedPlayerRenderer.INSTANCE.animate(pokemonEntity, player, relevantPartsByName, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
}
}
}
@@ -174,16 +174,6 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
return this.cobblemon$isDoubleJumping;
}
@Inject(method = "isHandsBusy", at = @At("HEAD"), cancellable = true)
private void cobblemon$isHandsBusy(CallbackInfoReturnable<Boolean> cir) {
Entity vehicle = this.getVehicle();
if (vehicle instanceof PokemonEntity pokemonEntity) {
int seatIndex = pokemonEntity.getPassengers().indexOf(this);
Seat seat = pokemonEntity.getSeats().get(seatIndex);
cir.setReturnValue(seat.getHandsBusy());
}
}
@Override
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
Entity vehicle = this.getVehicle();
@@ -117,8 +117,6 @@ public abstract class MouseHandlerMixin {
@Local(argsOnly = true) double d,
@Local(argsOnly = true) double movementTime
) {
boolean returnValue = true;
PokedexUsageContext usageContext = CobblemonClient.INSTANCE.getPokedexUsageContext();
if (usageContext.getScanningGuiOpen()) {
this.smoothTurnY.reset();
@@ -129,7 +127,7 @@ public abstract class MouseHandlerMixin {
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
returnValue = false;
return false;
}
// Clamp player rotation if riding and the vehicle demands it
@@ -145,33 +143,35 @@ public abstract class MouseHandlerMixin {
pitchSmoother.reset();
rollSmoother.reset();
yawSmoother.reset();
return returnValue;
return true;
}
// Send mouse input to be interpreted into rotation
// deltas by the ride controller
//Send mouse input to be interpreted into rotation
//deltas by the ride controller
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
// Perform Rotation using mouse influenced rotation deltas.
//Perform Rotation using mouse influenced rotation deltas.
controllable.getOrientationController().rotate(
(float) angVecMouse.x,
(float) angVecMouse.y,
(float) angVecMouse.z
);
// Gather and apply the current rotation deltas
//Gather and apply the current rotation deltas
var angRot = cobblemon$getAngularVelocity(movementTime);
// Apply smoothing if requested by the controller.
// This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing()) {
//Apply smoothing if requested by the controller.
//This Might be best if done by the controller itself?
if(cobblemon$shouldUseAngVelSmoothing())
{
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
}
// Otherwise simply apply the smoothing
else {
//Otherwise simply apply the smoothing
else
{
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
}
return false;
@@ -10,7 +10,8 @@ package com.cobblemon.mod.common.mixin.client.snapshots;
import com.cobblemon.mod.common.CobblemonBuildDetails;
import com.cobblemon.mod.common.client.gui.snapshots.SnapshotWarningScreen;
import com.cobblemon.mod.common.client.persisted.ClientPersistedData;
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementData;
import com.cobblemon.mod.common.client.snapshots.SnapshotAcknowledgementReader;
import kotlin.Unit;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
@@ -41,7 +42,7 @@ public final class SnapshotWarningMixin {
return Unit.INSTANCE;
}
ClientPersistedData.INSTANCE.writeSnapshotsData(new ClientPersistedData.SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
SnapshotAcknowledgementReader.INSTANCE.write(new SnapshotAcknowledgementData(CobblemonBuildDetails.VERSION, dsa));
runnable.run();
return Unit.INSTANCE;
@@ -53,7 +54,7 @@ public final class SnapshotWarningMixin {
@Unique
@SuppressWarnings("ResultOfMethodCallIgnored")
private boolean cobblemon$isAcknowledged() {
ClientPersistedData.SnapshotAcknowledgementData data = ClientPersistedData.INSTANCE.readSnapshotsData();
SnapshotAcknowledgementData data = SnapshotAcknowledgementReader.INSTANCE.read();
if (data == null) {
return false;
}
@@ -11,10 +11,7 @@ package com.cobblemon.mod.common.mixin.entity;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -42,17 +39,10 @@ public abstract class EntityMixin {
target = "Lnet/minecraft/world/entity/Entity;onGround()Z"
)
)
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original) {
public boolean cobblemon$forceOnGroundForStepUp(Entity entity, Operation<Boolean> original)
{
if (entity instanceof PokemonEntity && entity.hasControllingPassenger()) {
BlockPos below = entity.blockPosition().below();
Level level = entity.level();
var blockStateBelow = level.getBlockState(below);
boolean isAirOrLiquid = blockStateBelow.isAir() || !blockStateBelow.getFluidState().isEmpty();
boolean canSupportEntity = blockStateBelow.isFaceSturdy(level, below, Direction.UP);
boolean standingOnSolid = canSupportEntity && !isAirOrLiquid;
if (standingOnSolid) {
return true;
}
return true;
}
return original.call(entity);
}
@@ -213,7 +213,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.OAK_WALL_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
@JvmField
@@ -26,10 +26,10 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(PokemonItemComponent.PACKET_CODEC)
.build())
val HELD_ITEM_EFFECT: DataComponentType<HeldItemEffectComponent> = create("held_item_effect", DataComponentType.builder<HeldItemEffectComponent>()
.persistent(HeldItemEffectComponent.CODEC)
.networkSynchronized(HeldItemEffectComponent.PACKET_CODEC)
.build())
val HELD_ITEM_REP: DataComponentType<HeldItemCapableComponent> = DataComponentType.builder<HeldItemCapableComponent>()
.persistent(HeldItemCapableComponent.CODEC)
.networkSynchronized(HeldItemCapableComponent.PACKET_CODEC)
.build()
val BAIT: DataComponentType<RodBaitComponent> = create("bait", DataComponentType.builder<RodBaitComponent>()
.persistent(RodBaitComponent.CODEC)
@@ -86,7 +86,6 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:ingredient"), INGREDIENT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
@@ -193,19 +192,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val campfire_pots = mutableListOf<CampfirePotItem>()
@JvmField
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, CampfirePotColor.RED)
val CAMPFIRE_POT_RED = campfirePotItem(CobblemonBlocks.RED_CAMPFIRE_POT, "red")
@JvmField
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, CampfirePotColor.YELLOW)
val CAMPFIRE_POT_YELLOW = campfirePotItem(CobblemonBlocks.YELLOW_CAMPFIRE_POT, "yellow")
@JvmField
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, CampfirePotColor.GREEN)
val CAMPFIRE_POT_GREEN = campfirePotItem(CobblemonBlocks.GREEN_CAMPFIRE_POT, "green")
@JvmField
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, CampfirePotColor.BLUE)
val CAMPFIRE_POT_BLUE = campfirePotItem(CobblemonBlocks.BLUE_CAMPFIRE_POT, "blue")
@JvmField
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, CampfirePotColor.PINK)
val CAMPFIRE_POT_PINK = campfirePotItem(CobblemonBlocks.PINK_CAMPFIRE_POT, "pink")
@JvmField
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, CampfirePotColor.BLACK)
val CAMPFIRE_POT_BLACK = campfirePotItem(CobblemonBlocks.BLACK_CAMPFIRE_POT, "black")
@JvmField
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, CampfirePotColor.WHITE)
val CAMPFIRE_POT_WHITE = campfirePotItem(CobblemonBlocks.WHITE_CAMPFIRE_POT, "white")
@JvmField
val HEARTY_GRAINS = compostableItem("hearty_grains", ItemNameBlockItem(CobblemonBlocks.HEARTY_GRAINS, Properties().rarity(Rarity.COMMON)))
@@ -1501,8 +1500,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
}
)
private fun campfirePotItem(block: Block, color: CampfirePotColor): CampfirePotItem {
val item = create("campfire_pot_${color.suffix}", CampfirePotItem(block, color))
private fun campfirePotItem(block: Block, type: String): CampfirePotItem {
val item = create("campfire_pot_${type}", CampfirePotItem(block))
campfire_pots.add(item)
return item
}
@@ -1649,4 +1648,4 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun foodItem(nutrition: Int, saturationModifier: Float): Item {
return CobblemonItem(Item.Properties().food(FoodProperties.Builder().nutrition(nutrition).saturationModifier(saturationModifier).build()))
}
}
}
@@ -39,7 +39,6 @@ object CobblemonMechanics : DataRegistry {
var potions = PotionsMechanic()
override fun sync(player: ServerPlayer) {}
override fun reload(manager: ResourceManager) {
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
@@ -9,8 +9,8 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.CalculateSeatPositionsHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
@@ -20,7 +20,7 @@ import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandl
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.cooking.ToggleCookingPotLidHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.RideSettingsSyncHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
@@ -65,8 +65,8 @@ import com.cobblemon.mod.common.client.net.trade.TradeProcessStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeStartedHandler
import com.cobblemon.mod.common.client.net.trade.TradeUpdatedHandler
import com.cobblemon.mod.common.net.PacketRegisterInfo
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.CalculateSeatPositionsPacket
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
@@ -157,7 +157,6 @@ import com.cobblemon.mod.common.net.messages.server.callback.party.PartyPokemonS
import com.cobblemon.mod.common.net.messages.server.callback.party.PartySelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyMoveSelectCancelledPacket
import com.cobblemon.mod.common.net.messages.server.callback.partymove.PartyPokemonMoveSelectedPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatRangePacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
import com.cobblemon.mod.common.net.messages.server.dialogue.EscapeDialoguePacket
@@ -171,11 +170,11 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpasturePokemonPack
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.FinishScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokedex.scanner.StartScanningPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.interact.InteractPokemonPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetItemHiddenPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetActiveMarkPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
@@ -209,7 +208,6 @@ import com.cobblemon.mod.common.net.serverhandling.callback.party.PartyPokemonSe
import com.cobblemon.mod.common.net.serverhandling.callback.party.PartySelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyMoveSelectCancelledHandler
import com.cobblemon.mod.common.net.serverhandling.callback.partymove.PartyPokemonMoveSelectedHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatRangeHandler
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingStatsHandler
import com.cobblemon.mod.common.net.serverhandling.dialogue.EscapeDialogueHandler
@@ -224,11 +222,11 @@ import com.cobblemon.mod.common.net.serverhandling.pasture.UnpasturePokemonHandl
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.FinishScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokedex.scanner.StartScanningHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.interact.InteractPokemonHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetItemHiddenHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
@@ -312,7 +310,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(MarksPotentialUpdatePacket.ID, MarksPotentialUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(MarkingsUpdatePacket.ID, MarkingsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideBoostsUpdatePacket.ID, RideBoostsUpdatePacket::decode, PokemonUpdatePacketHandler()))
list.add(PacketRegisterInfo(RideStaminaUpdatePacket.ID, RideStaminaUpdatePacket::decode, PokemonUpdatePacketHandler()))
// Evolution start
list.add(PacketRegisterInfo(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler()))
@@ -391,6 +388,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler))
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -407,7 +405,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(CosmeticItemAssignmentSyncPacket.ID, CosmeticItemAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -581,7 +578,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatePacket.ID, ServerboundUpdateRidingStatePacket::decode, ServerboundUpdateRidingStateHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
@@ -1,106 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.BirdSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.GliderSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HelicopterSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.HoverSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.JetSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.air.RocketSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinSettings
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import net.minecraft.advancements.critereon.MinMaxBounds
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
object CobblemonRideSettings : DataRegistry {
override val id: ResourceLocation = cobblemonResource("ride_settings")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<CobblemonRideSettings>()
val gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
.create()
var bird = BirdSettings()
var glider = GliderSettings()
var helicopter = HelicopterSettings()
var hover = HoverSettings()
var jet = JetSettings()
var rocket = RocketSettings()
var horse = HorseSettings()
var minekart = MinekartSettings()
var vehicle = VehicleSettings()
var boat = BoatSettings()
var burst = BurstSettings()
var dolphin = DolphinSettings()
override fun sync(player: ServerPlayer) {
player.sendPacket(
RideSettingsSyncPacket(
bird = bird,
glider = glider,
helicopter = helicopter,
hover = hover,
jet = jet,
rocket = rocket,
horse = horse,
minekart = minekart,
vehicle = vehicle,
boat = boat,
burst = burst,
dolphin = dolphin
)
)
}
override fun reload(manager: ResourceManager) {
bird = loadStyle(manager, "bird", BirdSettings::class.java)
glider = loadStyle(manager, "glider", GliderSettings::class.java)
helicopter = loadStyle(manager, "helicopter", HelicopterSettings::class.java)
hover = loadStyle(manager, "hover", HoverSettings::class.java)
jet = loadStyle(manager, "jet", JetSettings::class.java)
rocket = loadStyle(manager, "rocket", RocketSettings::class.java)
horse = loadStyle(manager, "horse", HorseSettings::class.java)
minekart = loadStyle(manager, "minekart", MinekartSettings::class.java)
vehicle = loadStyle(manager, "vehicle", VehicleSettings::class.java)
boat = loadStyle(manager, "boat", BoatSettings::class.java)
burst = loadStyle(manager, "burst", BurstSettings::class.java)
dolphin = loadStyle(manager, "dolphin", DolphinSettings::class.java)
}
private fun <T : RidingBehaviourSettings> loadStyle(manager: ResourceManager, name: String, clazz: Class<T>): T {
manager.getResourceOrThrow(cobblemonResource("ride_settings/$name.json")).open().use {
return gson.fromJson(it.reader(), clazz)
}
}
}
@@ -12,9 +12,11 @@ import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import kotlin.jvm.optionals.getOrNull
import net.minecraft.world.entity.LivingEntity
@@ -25,9 +27,7 @@ import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
/**
* Checks nearby entities and counts how many have nominated this entity as their leader. We normally update this
* as people join and leave the herd, but this is a fallback to make sure we don't get stuck with an incorrect value
* like if the entities die or whatever.
* Checks nearby entities and counts how many have nominated this entity as their leader.
*
* @author Hiroku
* @since June 17th, 2025
@@ -53,7 +53,7 @@ class FindAirTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.getIsSubmerged() || entity.isInLava) {
if (!entity.getIsSubmerged()) {
return@Trigger false
}
@@ -8,16 +8,17 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.ai.ToleratedHerdLeader
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -56,21 +57,21 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
!it.brain.hasMemoryValue(CobblemonMemories.POKEMON_DROWSY)
}.forEach { possibleLeader ->
possibleLeader as PokemonEntity
val bestMatchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
val possibleLeaderIsAlreadyALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
val currentLeaderHerdTier = currentLeaderEntity?.getHerdTier() ?: entity.getHerdTier()
if (currentLeaderHerdTier >= bestMatchingHerdLeader.tier) {
val matchingHerdLeader = entity.behaviour.herd.bestMatchLeader(entity, possibleLeader) ?: return@forEach
val alreadyIsALeader = possibleLeader.brain.hasMemoryValue(CobblemonMemories.HERD_SIZE)
val nestedHerdTier = currentLeaderEntity?.getHerdTier() ?: 0
if (nestedHerdTier >= matchingHerdLeader.tier) {
return@forEach // This leader is following a leader of a higher tier, we can't replace them
}
val distance = entity.distanceTo(possibleLeader)
if (bestMatchingHerdLeader.tier < bestTier) {
if (matchingHerdLeader.tier < bestTier) {
return@forEach // No need to check further if this leader is worse than the best found so far
} else if ((distance > bestDistance && !possibleLeaderIsAlreadyALeader) && bestMatchingHerdLeader.tier == bestTier) {
} else if ((distance > bestDistance && !alreadyIsALeader) && matchingHerdLeader.tier == bestTier) {
return@forEach // No need to check further if this leader is farther than the best found so far, unless they are already a leader - I'd prefer to join a pack
} else {
bestLeader = possibleLeader
bestDistance = distance
bestTier = bestMatchingHerdLeader.tier
bestTier = matchingHerdLeader.tier
}
}
return bestLeader ?: currentLeaderEntity
@@ -118,7 +119,6 @@ class FindHerdLeaderTaskConfig : SingleTaskConfig {
herdLeader.set(bestLeader.uuid.toString())
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
bestLeader.adjustHerdSize(1)
return@Trigger true
}
}
@@ -14,19 +14,15 @@ import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.FindRestingPlaceTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.resolveBoolean
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class FindRestingPlaceTaskConfig : SingleTaskConfig {
val condition: ExpressionLike = "true".asExpressionLike()
val horizontalSearchDistance: ExpressionOrEntityVariable = Either.left("16".asExpression())
val verticalSearchDistance: ExpressionOrEntityVariable = Either.left("5".asExpression())
@@ -35,13 +31,6 @@ class FindRestingPlaceTaskConfig : SingleTaskConfig {
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (entity !is PokemonEntity) {
return null
}
if (!behaviourConfigurationContext.runtime.resolveBoolean(condition)) {
return null
}
behaviourConfigurationContext.addMemories(
CobblemonMemories.PATH_COOLDOWN,
CobblemonMemories.POKEMON_DROWSY,
@@ -57,7 +57,7 @@ class GoToLandTaskConfig : SingleTaskConfig {
it.absent(CobblemonMemories.PATH_COOLDOWN)
).apply(it) { walkTarget, pathCooldown ->
Trigger { world, entity, time ->
if (!entity.isInWater) {
if (!entity.isInLiquid) {
return@Trigger false
}
@@ -11,20 +11,15 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.CobblemonSensors
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.GoToSleepTask
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.resolveBoolean
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class GoToSleepTaskConfig : SingleTaskConfig {
var onlyFromStatus: ExpressionOrEntityVariable = Either.left("false".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
@@ -39,6 +34,6 @@ class GoToSleepTaskConfig : SingleTaskConfig {
CobblemonMemories.POKEMON_SLEEPING
)
behaviourConfigurationContext.addSensors(CobblemonSensors.POKEMON_DROWSY)
return WrapperLivingEntityTask(GoToSleepTask.create(onlyFromStatus.resolveBoolean(behaviourConfigurationContext.runtime)), PokemonEntity::class.java)
return WrapperLivingEntityTask(GoToSleepTask.create(), PokemonEntity::class.java)
}
}
@@ -10,28 +10,17 @@ package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.npc.ai.LookAtSpeakerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
class LookAtSpeakerTaskConfig : SingleTaskConfig {
val condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime)) {
return null
}
return LookAtSpeakerTask.create().also {
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
}
) = LookAtSpeakerTask.create().also {
behaviourConfigurationContext.addMemories(MemoryModuleType.LOOK_TARGET, CobblemonMemories.DIALOGUES)
}
}
@@ -8,14 +8,15 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import java.util.UUID
import net.minecraft.world.entity.LivingEntity
@@ -81,8 +82,6 @@ class MaintainHerdLeaderTaskConfig : SingleTaskConfig {
if (bestLeader != null && bestLeader != leader) {
bestLeader.brain.eraseMemory(CobblemonMemories.HERD_LEADER)
entity.brain.setMemory(CobblemonMemories.HERD_LEADER, bestLeader.uuid.toString())
bestLeader.adjustHerdSize(1)
leader.adjustHerdSize(-1)
}
}
return@Trigger true
@@ -14,8 +14,6 @@ import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.OmniPathingEntity
import com.cobblemon.mod.common.entity.ai.CobblemonRandomSurfacePos
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
@@ -28,7 +26,6 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.BehaviorUtils
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
@@ -44,27 +41,24 @@ class WanderTaskConfig : SingleTaskConfig {
}
val condition = booleanVariable(WANDER, "wanders", true).asExpressible()
val wanderChance = numberVariable(WANDER, "wander_chance", 1 / (20 * 6F)).asExpressible()
val wanderChance = numberVariable(WANDER, "wander_chance", 1/(20 * 6F)).asExpressible()
val horizontalRange = numberVariable(WANDER, "horizontal_wander_range", 10).asExpressible()
val verticalRange = numberVariable(WANDER, "vertical_wander_range", 5).asExpressible()
val speedMultiplier =
numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val avoidTargetingAir: ExpressionOrEntityVariable =
Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
val speedMultiplier = numberVariable(SharedEntityVariables.MOVEMENT_CATEGORY, SharedEntityVariables.WALK_SPEED, 0.35).asExpressible()
val avoidTargetingAir: ExpressionOrEntityVariable = Either.left("true".asExpression()) // Whether to avoid air blocks when wandering
val minimumHeight: ExpressionOrEntityVariable = Either.left("0".asExpression()) // Height off the ground
val maximumHeight: ExpressionOrEntityVariable = Either.left("-1".asExpression()) // Height off the ground
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) =
listOf(
condition,
wanderChance,
horizontalRange,
verticalRange,
speedMultiplier,
avoidTargetingAir,
minimumHeight,
maximumHeight
).asVariables()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
wanderChance,
horizontalRange,
verticalRange,
speedMultiplier,
avoidTargetingAir,
minimumHeight,
maximumHeight
).asVariables()
private fun applyHeightConstraints(
pos: BlockPos,
@@ -83,7 +77,7 @@ class WanderTaskConfig : SingleTaskConfig {
(1..MAX_LOOK_DOWN_DISTANCE).firstOrNull {
val newPos = pos.below(it)
if (!world.getBlockState(newPos).isAir) {
return@firstOrNull true
return@firstOrNull true
} else if (newPos.y <= world.minBuildHeight) {
// Don't adjust downward into the void (Mostly if we're in the end)
// This doesn't stop fliers from wandering off end islands,
@@ -132,7 +126,7 @@ class WanderTaskConfig : SingleTaskConfig {
it.registered(CobblemonMemories.WANDER_CONTROL)
).apply(it) { walkTarget, lookTarget, pathCooldown, wanderControl ->
Trigger { world, entity, time ->
if (entity !is PathfinderMob || (entity.isUnderWater && !(entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())))) {
if (entity !is PathfinderMob || entity.isUnderWater) {
return@Trigger false
}
@@ -140,9 +134,10 @@ class WanderTaskConfig : SingleTaskConfig {
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean(mainThreadRuntime)
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
return@Trigger false
}
pathCooldown.setWithExpiry(true, wanderControl.pathCooldownTicks.toLong())
val minimumHeight = minimumHeight.resolveInt(mainThreadRuntime)
val maximumHeight = maximumHeight.resolveInt(mainThreadRuntime)
@@ -162,28 +157,14 @@ class WanderTaskConfig : SingleTaskConfig {
maximumHeight,
minimumHeight
)
} else if (entity.isUnderWater && entity.canBreatheUnderwater() && !((entity as OmniPathingEntity).canSwimInWater())) {
BehaviorUtils.getRandomSwimmablePos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} else if (entity is OmniPathingEntity && (entity.canWalkOnWater() || entity.canWalkOnLava())) {
CobblemonRandomSurfacePos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} else {
LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
LandRandomPos.getPos(
entity,
horizontalRange.resolveInt(mainThreadRuntime),
verticalRange.resolveInt(mainThreadRuntime)
)
} ?: continue
if (targetVec == null) continue
pos = applyHeightConstraints(
pos = BlockPos.containing(targetVec),
minimumHeight = minimumHeight,
@@ -201,17 +182,8 @@ class WanderTaskConfig : SingleTaskConfig {
pos = pos,
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
completionRange = 0,
nodeTypeFilter = { nodeType ->
entity.canBreatheUnderwater() || nodeType !in listOf(
PathType.WATER,
PathType.WATER_BORDER
)
},
destinationNodeTypeFilter = { nodeType ->
!avoidsTargetingAir || nodeType !in listOf(
PathType.OPEN
)
}
nodeTypeFilter = { nodeType -> nodeType !in listOf(PathType.WATER, PathType.WATER_BORDER) },
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
)
)
lookTarget.set(BlockPosTracker(pos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
@@ -31,10 +31,8 @@ import net.minecraft.world.entity.ai.behavior.BlockPosTracker
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.level.material.Fluids
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.Vec3
import kotlin.math.ceil
class WaterWanderTaskConfig : SingleTaskConfig {
val condition = booleanVariable(WANDER, "water_wanders", true).asExpressible()
@@ -102,33 +100,6 @@ class WaterWanderTaskConfig : SingleTaskConfig {
return@Trigger false
}
// Move target pos down so that we don't move out of water too much
val requiredDepth = ceil(0.5 + entity.eyeHeight)
// Get the current depth of the block
var depth = 1
var testPos = pos.above()
var blockState = entity.level().getBlockState(testPos)
while (blockState.fluidState.type == Fluids.WATER) {
depth++
testPos = testPos!!.above()
blockState = entity.level().getBlockState(testPos)
}
if (depth < requiredDepth) {
// Iterate down until we reach the desired min depth or we reach a non water block
pos = pos.below()
blockState = entity.level().getBlockState(pos)
while (blockState.fluidState.type == Fluids.WATER && depth < requiredDepth) {
depth ++
pos = pos!!.below()
blockState = entity.level().getBlockState(pos)
}
}
if (depth < requiredDepth) {
return@Trigger false
}
walkTarget.set(
CobblemonWalkTarget(
pos = pos,
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.cooking
import net.minecraft.ChatFormatting
import net.minecraft.util.StringRepresentable
/**
@@ -17,16 +18,16 @@ import net.minecraft.util.StringRepresentable
* @author Licious
* @since November 28th, 2022
*/
enum class Flavour(val colour: Int): StringRepresentable {
SPICY(0xFA795E),
DRY(0x65AFF3),
SWEET(0xFF82CF),
BITTER(0x77C96A),
SOUR(0xF3D465),
MILD(0x9D99BF);
enum class Flavour(val colour: Int, val chatFormatting: ChatFormatting): StringRepresentable {
SPICY(0x00FF00, ChatFormatting.RED),
DRY(0xFF0000, ChatFormatting.DARK_AQUA),
SWEET(0x0000FF, ChatFormatting.LIGHT_PURPLE),
BITTER(0xFFFF00, ChatFormatting.GREEN),
SOUR(0xFF00FF, ChatFormatting.YELLOW),
MILD(0xFF00FF, ChatFormatting.DARK_PURPLE);
override fun getSerializedName() = name
companion object {
val CODEC = StringRepresentable.fromEnum<Flavour> { Flavour.entries.toTypedArray() }
}
}
}
@@ -9,11 +9,13 @@
package com.cobblemon.mod.common.api.cooking
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.item.PokePuffItem
import com.cobblemon.mod.common.item.PokePuffItem.Companion.DISLIKED_FLAVOR_MULTIPLIER
import com.cobblemon.mod.common.item.PokePuffItem.Companion.LIKED_FLAVOR_MULTIPLIER
import com.cobblemon.mod.common.pokemon.Nature
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
import net.minecraft.core.registries.BuiltInRegistries
object PokePuffUtils {
@@ -23,12 +25,11 @@ object PokePuffUtils {
val (baseFriendship) = calculateBaseFriendship(stack)
val multiplier = if (
dominantFlavour == nature.favouriteFlavour ||
(dominantFlavour == Flavour.MILD && isNeutralNature)
) LIKED_FLAVOR_MULTIPLIER else -DISLIKED_FLAVOR_MULTIPLIER
return (baseFriendship / multiplier).toInt()
return when {
dominantFlavour == nature.favouriteFlavour -> (baseFriendship / LIKED_FLAVOR_MULTIPLIER).toInt()
dominantFlavour == Flavour.MILD && isNeutralNature -> (baseFriendship / 2.0).coerceAtLeast(1.0).toInt()
else -> (-baseFriendship / DISLIKED_FLAVOR_MULTIPLIER).toInt()
}
}
fun calculateBaseFriendship(stack: ItemStack): Pair<Double, Double> {
@@ -76,8 +77,8 @@ object PokePuffUtils {
Flavour.SPICY -> "attack"
Flavour.DRY -> "special_attack"
Flavour.SWEET -> "speed"
Flavour.BITTER -> "special_defence"
Flavour.SOUR -> "defence"
Flavour.BITTER -> "special_defense"
Flavour.SOUR -> "defense"
Flavour.MILD -> "neutral"
}
}
@@ -18,13 +18,14 @@ import net.minecraft.resources.ResourceLocation
* @since August 5th, 2022
*/
interface DataProvider {
/**
* Registers a [DataRegistry] to listen for updates.
* The updates will automatically happen on the correct sides based on [DataRegistry.type].
*
* @param registry The [DataRegistry] being registered.
*/
fun <T : DataRegistry> register(registry: T, reloadable: Boolean): T
fun <T : DataRegistry> register(registry: T): T
/**
* Attempts to find a [DataRegistry] with the given [ResourceLocation].
@@ -152,9 +152,6 @@ object CobblemonEvents {
@JvmField
val RIDE_EVENT_PRE = CancelableObservable<RidePokemonEvent.Pre>()
@JvmField
val RIDE_EVENT_APPLY_STAMINA = EventObservable<RidePokemonEvent.ApplyStamina>()
@JvmField
val RIDE_EVENT_POST = EventObservable<RidePokemonEvent.Post>()
@@ -8,7 +8,9 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.util.server
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
@@ -47,36 +49,4 @@ interface RidePokemonEvent {
"pokemon" to pokemon.struct
)
}
/**
* Event published when stamina is being applied. At this point you can modify the stamina that will get used on the
* mount or you can make the stamina infinite. Handy hey.
*/
class ApplyStamina(
override val player: ServerPlayer,
override val pokemon: PokemonEntity,
var rideStamina: Float
) : RidePokemonEvent {
val context = mutableMapOf(
"player" to player.asMoLangValue(),
"pokemon" to pokemon.struct,
"ride_stamina" to DoubleValue(rideStamina)
)
val functions = moLangFunctionMap(
"set_ride_stamina" to { params ->
rideStamina = params.getDouble(0).toFloat().coerceIn(0F, 1F)
DoubleValue.ONE
},
"set_infinite_stamina" to { _ ->
rideStamina = -1F
DoubleValue.ONE
},
)
fun setInfiniteStamina() {
rideStamina = -1F
}
fun isInfiniteStamina() = rideStamina == -1F
}
}
@@ -1,57 +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.item
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
import java.io.File
object HeldItems : DataRegistry {
override val id = cobblemonResource("held_items")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<HeldItems>()
override fun sync(player: ServerPlayer) {}
// val heldItems = PrioritizedList<BagItemLike>()
internal val heldItemsScripts = mutableMapOf<String, String>() // showdown item ID to JavaScript
val showdownItems = hashSetOf<String>()
override fun reload(manager: ResourceManager) {
heldItemsScripts.clear()
ShowdownService.service.resetRegistryData("heldItem")
manager.listResources("held_items") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
val js = reader.readText()
heldItemsScripts[resolvedIdentifier.path] = js
}
}
}
ShowdownService.service.sendRegistryData(heldItemsScripts, "heldItem")
val itemsJson = ShowdownService.service.getRegistryData("heldItem")
for (i in 0 until itemsJson.size()) {
val jsItem = itemsJson[i].asJsonObject
showdownItems += jsItem.get("id").asString
}
CobblemonHeldItemManager.load()
this.observable.emit(this)
}
}
@@ -1236,7 +1236,7 @@ object MoLangFunctions {
}
}
map.put("has_walk_target") { _ ->
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
DoubleValue(entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
}
}
map
@@ -57,6 +57,7 @@ class NPCClass {
var isMovable: Boolean = true
var isInvulnerable = false
var isLeashable = true
var visibilityCondition = true
var allowProjectileHits = true
var hideNameTag = false
@@ -98,6 +99,7 @@ class NPCClass {
buffer.writeBoolean(isMovable)
buffer.writeBoolean(isInvulnerable)
buffer.writeBoolean(isLeashable)
buffer.writeBoolean(visibilityCondition)
buffer.writeBoolean(allowProjectileHits)
buffer.writeBoolean(hideNameTag)
}
@@ -150,6 +152,7 @@ class NPCClass {
isMovable = buffer.readBoolean()
isInvulnerable = buffer.readBoolean()
isLeashable = buffer.readBoolean()
visibilityCondition = buffer.readBoolean()
allowProjectileHits = buffer.readBoolean()
hideNameTag = buffer.readBoolean()
}
@@ -37,6 +37,7 @@ class NPCPreset {
var isMovable: Boolean? = null
var isInvulnerable: Boolean? = null
var isLeashable: Boolean? = null
var visibilityCondition: Boolean? = null
var allowProjectileHits: Boolean? = null
var hideNameTag: Boolean? = null
@@ -63,6 +64,7 @@ class NPCPreset {
isMovable?.let { npcClass.isMovable = it }
isInvulnerable?.let { npcClass.isInvulnerable = it }
isLeashable?.let { npcClass.isLeashable = it }
visibilityCondition?.let { npcClass.visibilityCondition = it }
allowProjectileHits?.let { npcClass.allowProjectileHits = it }
hideNameTag?.let { npcClass.hideNameTag = it }
}
@@ -15,7 +15,6 @@ import net.minecraft.world.entity.LivingEntity
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.abs
import kotlin.math.asin
import kotlin.math.sign
@@ -114,7 +113,7 @@ open class OrientationController(val entity: LivingEntity) {
fun applyGlobalPitch(deltaPitchDegrees: Float) = updateOrientation { original ->
val currQuat = Quaternionf().setFromUnnormalized(original)
val horzLeftVector = Vector3f(0.0f, -abs(this.upVector.y.sign), 0.0f).cross(this.forwardVector)
val horzLeftVector = Vector3f(0.0f, this.upVector.y.sign, 0.0f).cross(this.forwardVector)
// Avoid NaN issue when normalizing and the forwardVector and upVector are equal
val pitchAxis = if (horzLeftVector.lengthSquared() < 0.01) this.leftVector else horzLeftVector
@@ -131,7 +131,6 @@ open class PokemonProperties {
props.species = parseSpeciesIdentifier(keyPairs)
props.form = parseForm(keyPairs)
props.friendship = parseIntProperty(keyPairs, listOf("friendship"))?.coerceIn(0, Cobblemon.config.maxPokemonFriendship)
props.fullness = parseIntProperty(keyPairs, listOf("fullness"))
props.pokeball = parseIdentifierOfRegistry(keyPairs, listOf("pokeball")) { identifier -> PokeBalls.getPokeBall(identifier)?.name?.toString() }
props.nature = parseIdentifierOfRegistry(keyPairs, listOf("nature")) { identifier -> Natures.getNature(identifier)?.name?.toString() }
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
@@ -327,7 +326,6 @@ open class PokemonProperties {
var gender: Gender? = null
var level: Int? = null
var friendship: Int? = null
var fullness: Int? = null
var pokeball: String? = null
var nature: String? = null
var ability: String? = null
@@ -399,7 +397,6 @@ open class PokemonProperties {
pokemon.setFriendship(pokemon.form.baseFriendship)
}
}
fullness?.let { pokemon.currentFullness = it }
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
customProperties.forEach { it.apply(pokemon) }
@@ -507,7 +504,6 @@ open class PokemonProperties {
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
fullness?.takeIf { it != pokemon.currentFullness }?.let { return false }
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
nature?.takeIf { it != pokemon.nature.name.toString() }?.let { return false }
ability?.takeIf { it != pokemon.ability.name }?.let { return false }
@@ -592,7 +588,6 @@ open class PokemonProperties {
originalTrainer?.takeIf { it != properties.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != properties.originalTrainerType }?.let{ return false }
moves?.takeIf { it.any { move -> properties.moves?.none { it == move } == true } }?.let { return false }
fullness?.takeIf { it != properties.fullness }?.let { return false }
return true
}
@@ -641,7 +636,6 @@ open class PokemonProperties {
nickname?.let { nbt.putString(DataKeys.POKEMON_NICKNAME, Component.Serializer.toJson(it, registryLookup)) }
form?.let { nbt.putString(DataKeys.POKEMON_FORM_ID, it) }
friendship?.let { nbt.putInt(DataKeys.POKEMON_FRIENDSHIP, it) }
fullness?.let { nbt.putInt(DataKeys.POKEMON_FULLNESS, it) }
pokeball?.let { nbt.putString(DataKeys.POKEMON_CAUGHT_BALL, it) }
nature?.let { nbt.putString(DataKeys.POKEMON_NATURE, it) }
ability?.let { nbt.putString(DataKeys.POKEMON_ABILITY, it) }
@@ -673,7 +667,6 @@ open class PokemonProperties {
nickname = if (tag.contains(DataKeys.POKEMON_NICKNAME)) Component.Serializer.fromJson(tag.getString(DataKeys.POKEMON_NICKNAME), registryLookup) else null
form = if (tag.contains(DataKeys.POKEMON_FORM_ID)) tag.getString(DataKeys.POKEMON_FORM_ID) else null
friendship = if (tag.contains(DataKeys.POKEMON_FRIENDSHIP)) tag.getInt(DataKeys.POKEMON_FRIENDSHIP) else null
fullness = if (tag.contains(DataKeys.POKEMON_FULLNESS)) tag.getInt(DataKeys.POKEMON_FULLNESS) else null
pokeball = if (tag.contains(DataKeys.POKEMON_CAUGHT_BALL)) tag.getString(DataKeys.POKEMON_CAUGHT_BALL) else null
nature = if (tag.contains(DataKeys.POKEMON_NATURE)) tag.getString(DataKeys.POKEMON_NATURE) else null
ability = if (tag.contains(DataKeys.POKEMON_ABILITY)) tag.getString(DataKeys.POKEMON_ABILITY) else null
@@ -707,7 +700,6 @@ open class PokemonProperties {
//nickname?.let { json.addProperty(DataKeys.POKEMON_NICKNAME, Text.Serialization.toJsonString(it)) }
form?.let { json.addProperty(DataKeys.POKEMON_FORM_ID, it) }
friendship?.let { json.addProperty(DataKeys.POKEMON_FRIENDSHIP, it) }
fullness?.let {json.addProperty(DataKeys.POKEMON_FULLNESS, it)}
pokeball?.let { json.addProperty(DataKeys.POKEMON_CAUGHT_BALL, it) }
nature?.let { json.addProperty(DataKeys.POKEMON_NATURE, it) }
ability?.let { json.addProperty(DataKeys.POKEMON_ABILITY, it) }
@@ -740,7 +732,6 @@ open class PokemonProperties {
//nickname = json.get(DataKeys.POKEMON_NICKNAME)?.asString?.let { Text.Serialization.fromJson(it) }
form = json.get(DataKeys.POKEMON_FORM_ID)?.asString
friendship = json.get(DataKeys.POKEMON_FRIENDSHIP)?.asInt
fullness = json.get(DataKeys.POKEMON_FULLNESS)?.asInt
pokeball = json.get(DataKeys.POKEMON_CAUGHT_BALL)?.asString
nature = json.get(DataKeys.POKEMON_NATURE)?.asString
ability = json.get(DataKeys.POKEMON_ABILITY)?.asString
@@ -772,7 +763,6 @@ open class PokemonProperties {
shiny?.let { pieces.add("shiny=$it") }
gender?.let { pieces.add("gender=$it")}
friendship?.let { pieces.add("friendship=$it") }
fullness?.let {pieces.add("fullness=$it")}
pokeball?.let { pieces.add("pokeball=$it") }
nature?.let { pieces.add("nature=$it") }
ability?.let { pieces.add("ability=$it") }
@@ -43,6 +43,7 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -53,8 +54,10 @@ import com.cobblemon.mod.common.pokemon.ai.ObtainableItemConditionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifClient
import com.google.common.collect.HashBasedTable
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -62,11 +65,13 @@ import com.google.gson.reflect.TypeToken
import com.mojang.datafixers.util.Either
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.tags.TagKey
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.entity.EntityDimensions
import net.minecraft.world.entity.ai.memory.MemoryModuleType
@@ -146,17 +151,24 @@ object PokemonSpecies : JsonDataRegistry<Species> {
val species: Collection<Species>
get() = this.speciesByIdentifier.values
val implemented: List<Species>
get() = this.species.filter { it.implemented }
val implemented = mutableListOf<Species>()
init {
SpeciesAdditions.observable.subscribe {
implemented.clear()
this.species.forEach(Species::initialize)
this.species.forEach {
if (it.implemented) {
this.implemented.add(it)
}
}
this.species.forEach(Species::resolveEvolutionMoves)
Cobblemon.showdownThread.queue {
it.resetRegistryData("species")
it.sendRegistryData(allShowdownSpecies(), "species")
it.indicateSpeciesInitialized()
// Reload this with the mod
CobblemonHeldItemManager.load()
Cobblemon.LOGGER.info("Loaded {} Pokémon species", this.speciesByIdentifier.size)
this.observable.emit(this)
}
@@ -221,6 +233,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
override fun reload(data: Map<ResourceLocation, Species>) {
this.speciesByIdentifier.clear()
this.implemented.clear()
this.speciesByDex.clear()
data.forEach { (identifier, species) ->
species.resourceIdentifier = identifier
@@ -228,6 +241,11 @@ object PokemonSpecies : JsonDataRegistry<Species> {
this.speciesByDex.remove(old.resourceIdentifier.namespace, old.nationalPokedexNumber)
}
this.speciesByDex.put(species.resourceIdentifier.namespace, species.nationalPokedexNumber, species)
ifClient {
if (species.implemented) {
this.implemented.add(species)
}
}
}
}
@@ -8,6 +8,9 @@
package com.cobblemon.mod.common.api.riding
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import net.minecraft.world.entity.PlayerRideableJumping
/**
@@ -9,7 +9,10 @@
package com.cobblemon.mod.common.api.riding
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -29,10 +32,10 @@ class RidingProperties(
{ buffer.readEnumConstant(RidingStyle::class.java) },
{
val key = buffer.readResourceLocation()
val behaviour = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
?: error("Unknown controller key: $key")
behaviour.decode(buffer)
behaviour
settings.decode(buffer)
settings
}
)
}
@@ -48,10 +51,7 @@ class RidingProperties(
buffer.writeMap(
v,
{ _, style -> buffer.writeEnumConstant(style) },
{ _, behaviour ->
buffer.writeResourceLocation(behaviour.key)
behaviour.encode(buffer)
}
{ _, behaviour -> behaviour.encode(buffer) }
)
}
}
@@ -25,8 +25,7 @@ data class Seat(
val locator: String?,
val offset: Vec3?,
val poseOffsets: MutableList<SeatPoseOffset>?,
val poseAnimations: MutableList<SeatPoseAnimations>?,
val handsBusy: Boolean = false,
val poseAnimations: MutableList<SeatPoseAnimations>?
) : Encodable {
fun getOffset(poseType: PoseType) : Vec3 {
return poseOffsets?.firstOrNull { poseType in it.poseTypes }?.offset ?: Vec3.ZERO
@@ -41,7 +40,6 @@ data class Seat(
buffer.writeNullable(poseAnimations) { _, v ->
buffer.writeCollection(v) { _, animations -> animations.encode(buffer) }
}
buffer.writeBoolean(handsBusy)
}
companion object {
@@ -50,8 +48,7 @@ data class Seat(
buffer.readNullable { buffer.readString() },
buffer.readNullable { buffer.readVec3() },
buffer.readNullable { buffer.readList { SeatPoseOffset.decode(buffer) } },
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } },
buffer.readBoolean()
buffer.readNullable { buffer.readList { SeatPoseAnimations.decode(buffer) } }
)
}
}
@@ -10,8 +10,10 @@ package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
/**
* Represents static settings of a riding behaviour.
@@ -19,17 +21,17 @@ import net.minecraft.resources.ResourceLocation
* Typically this will be initialized for each pokemon form during deserialization
* to determine how they should ride.
*
* These also exist in a datapacked folder, ride_settings, which are used for the fallback
* values if non-stat settings are omitted in the pokemon form JSON.
*
* @author landonjw
*/
interface RidingBehaviourSettings: Encodable, Decodable {
val key: ResourceLocation
val stats: MutableMap<RidingStat, IntRange>
fun calculate(stat: RidingStat, boostAmount: Float): Float {
fun calculate(stat: RidingStat, boosts: Int): Float {
val range = stats[stat] ?: return 0F
return range.first + boostAmount
val boostQuotient = boosts / 255F
return range.first + (range.last - range.first) * boostQuotient
}
fun hasStat(stat: RidingStat) = stats.containsKey(stat)
}
@@ -18,7 +18,7 @@ import net.minecraft.world.phys.Vec3
* This is intended to contain mutable state that is passed between the client and server
* or temporary state that is used during the riding process.
*
* This will be destroyed when the main rider dismounts the Pokemon.
* This will be destroyed when the a user dismounts the Pokemon.
*
* @author landonjw
*/
@@ -8,17 +8,13 @@
package com.cobblemon.mod.common.api.riding.behaviour
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.DoubleJump
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.client.MountedCameraTypeHandler
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.FluidTags
import net.minecraft.world.entity.LivingEntity
import kotlin.collections.get
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.player.Player
/**
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
@@ -53,15 +49,6 @@ class RidingController(
if (newTransition != null && behaviourSettings != null) {
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
context?.let { newState.rideVelocity.set(it.state.rideVelocity.get(), forced = true) }
val previousKey = context?.behaviour
val newKey = behaviourSettings.key
for (passenger in entity.passengers) {
if (passenger is Player && passenger !is ServerPlayer) {
MountedCameraTypeHandler.handleTransition(previousKey ?: continue, newKey)
}
}
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
lastTransitionAge = entity.ticksLived
} else {
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.orientation.OrientationController
import com.cobblemon.mod.common.api.riding.RidingStyle
@@ -24,16 +23,15 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
@@ -50,22 +48,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val poseProvider = PoseProvider<BirdSettings, BirdState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
val globalBird: BirdSettings
get() = CobblemonRideSettings.bird
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
//Need to check other fluids
if (vehicle.isInWater || vehicle.isUnderWater) {
return@any false
}
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
if (it.y.toDouble() == (vehicle.position().y)) {
val blockState = vehicle.level().getBlockState(it.below())
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
}
true
}
return true
}
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
@@ -90,24 +74,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
) {
if(vehicle.level().isClientSide) {
tickStamina(settings, state, vehicle, driver)
checkUpsideDown(state, vehicle, driver)
}
}
private fun checkUpsideDown(
state: BirdState,
vehicle: PokemonEntity,
driver: Player,
) {
val controller = (driver as OrientationControllable).orientationController
if (cos(controller.roll.toRadians()) < 0.0) {
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
} else if (state.stamina.get() == 0.0f) { // checks stamina depletion and forces dive if depleted
val upsideDownRate = 1.0 / (2.0 * 20.0) // 1 seconds to max out
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate))
} else { // Its right side up or has stamina so reset it
state.currUpsideDownForce.set(0.0)
}
}
@@ -122,12 +88,11 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
val totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalBird.staminaExpr!!) * 20.0f
val totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr) * 20.0f
val stamDrainRate = (1.0f / totalStam).toFloat()
val newStam = if (driver.zza > 0.0) max(0.0f,stam - stamDrainRate)
else if (!state.gliding.get()) max(0.0f,stam - stamDrainRate * 0.5f) // if hovering half the stam drain
else min(1.0f,stam + stamDrainRate * 0.2f)
else min(1.0f,stam + stamDrainRate)
state.stamina.set(newStam)
}
@@ -148,6 +113,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
driver: Player,
input: Vec3
): Vec3 {
var yVel = 0.0
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
@@ -166,10 +133,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
}
// The downward force used to encourage players to stop flying upside down.
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
return velocity
}
@@ -178,9 +142,9 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
*/
fun calculateRideSpaceVel(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity, driver: Player) {
// retrieve stats
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0)
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBird.accelerationExpr!!) * 20.0)
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0)
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glidespeedExpr)
var glideSpeedChange = 0.0
var activeInput = false
@@ -264,7 +228,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
//Check if the ride should be gliding
if (driver.isLocalPlayer) {
if ((activeInput && state.stamina.get() > 0.0) || state.rideVelocity.get().z < 0.2) {
if (activeInput && state.stamina.get() > 0.0) {
state.gliding.set(false)
} else {
state.gliding.set(true)
@@ -289,23 +253,21 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //* sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))// * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
// Dampen yaw when upside down
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
controller.applyGlobalYaw(trueYawDelt.toFloat() * yawDampen)
// Correct orientation when at low speeds
correctOrientation(settings, state, vehicle, controller, deltaTime)
controller.applyGlobalPitch(state.currPitchCorrectionForce.get().toRadians())
//yaw, pitch, roll
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
return Vec3(0.0, state.currPitchCorrectionForce.get(), state.currRollCorrectionForce.get())
}
private fun correctOrientation(
@@ -317,11 +279,11 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
) {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!)
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTimeRoll.get() - rollCorrectionTimer)).coerceIn(0.0, 1.0).toFloat()
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect)
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
val rollArrivalDeg = 90.0
if(state.rideVelocity.get().length() < 0.1 || state.noInputTimeRoll.get() > rollCorrectionTimer) {
if(state.rideVelocity.get().length() < 0.1 || state.noInputTime.get() > rollCorrectionTimer) {
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
@@ -343,18 +305,13 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
}
//Calculate correcting pitch force.
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 8f * state.currUpsideDownForce.get() else 15f
val maxPitchForce = maxPitchCorrectionRate.toFloat() / 40.0f
val pitchArrivalDeg = 45.0f
var desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0 else 0.0
// if (cos(controller.roll.toRadians()) < 0.0) {
// desiredPitch *= -1 // If not rolled over then flip the pitch force. There are better ways to do this :(
// }
if((state.rideVelocity.get().length() < 0.1 && controller.pitch < 0.0) || state.currUpsideDownForce.get() != 0.0) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(Mth.wrapDegrees(desiredPitch.toFloat().angleDifference(controller.pitch)))) / pitchArrivalDeg)
val desiredPitchForce = maxPitchCorrectionRate.toFloat() * arrivalInfluence
var steeredPitchForce = (desiredPitchForce - state.currPitchCorrectionForce.get())
val maxPitchCorrectionRate = 15
val maxPitchForce = maxPitchCorrectionRate / 40.0
val pitchArrivalDeg = 90.0
if(state.rideVelocity.get().length() < 0.1) {
val arrivalInfluence = (min(pitchArrivalDeg,abs(controller.pitch).toDouble()) / pitchArrivalDeg)
val desiredPitchForce = (0 - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
// Ensure the 'steering' correction force doesn't exceed our maxForce
if (abs(steeredPitchForce) > maxPitchForce) {
@@ -385,23 +342,12 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
// Begin the roll correction time counter if not enough mouse input
// is detected
if (mouseX < 1 && abs(mouseY) < 1) {
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime)
} else if (cos(controller.roll.toRadians()) < 0.1) {
state.noInputTimeRoll.set(state.noInputTimeRoll.get() + deltaTime * 0.5)
}
else {
state.noInputTimeRoll.set(0.0)
}
if (abs(mouseY) < 1) {
state.noInputTimePitch.set(state.noInputTimePitch.get() + deltaTime)
if (abs(mouseX) < 1 && abs(mouseY) < 1) {
state.noInputTime.set(state.noInputTime.get() + deltaTime)
} else {
state.noInputTimePitch.set(0.0)
state.noInputTime.set(0.0)
}
return when {
@@ -450,8 +396,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
// Set roll to zero if transitioning to noroll config
controller.rotateRoll(controller.roll * -1.0f)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
//Smooth out mouse input.
val smoothingSpeed = 4.0
@@ -495,11 +441,11 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
//Smooth out mouse input.
val smoothingSpeed = 2.0
val smoothingSpeed = 4.0
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
// of being upside down
@@ -508,31 +454,27 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
val currSpeed = state.rideVelocity.get().length()
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
val hoverSpeed = 0.1
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
// Yaw locally when at or below hoverSpeed
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
val localYaw = if (currHorzSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
else 0.0 //xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
val localYaw = if (currSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
else xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
// Pitch locally up or down depending upon a number of factors:
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
// - Do not pitch at all when at or below hover speed
// - pitch faster at higher speeds
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalBird.horizontalPitchExpr!!)
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr)
var localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
localPitch *= if (currSpeed > hoverSpeed || yInput > 0.0) RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed).coerceIn(0.2, 1.0)
localPitch *= if (currSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
else 0.0
// Roll less when slow and not at all when at hoverSpeed or lower.
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
// pitching globally
val pitchInfluencedRollCorrection = 0.0
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2).coerceIn(0.0f, 0.7f) //Coerce into how much roll you want to let in in the deadzone
val inputInRolledDir = if (xInput.sign.toFloat() == controller.roll.sign) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
val rollDampen = (1 - howRolledAmI * abs(cos(controller.pitch.toRadians())).pow(2) * inputInRolledDir)
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
val pitchInfluencedRollCorrection = 0.0 // if ( currSpeed > hoverSpeed * 2) 0.4*(abs(yInput - localPitch) * controller.roll.sign * -1.0) else 0.0
val rollForce = if (currSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) + pitchInfluencedRollCorrection
else 0.0
//yaw, pitch, roll
@@ -583,8 +525,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val currSpeed = state.rideVelocity.get().length()
// Must I ensure that topspeed is greater than minimum?
@@ -646,17 +588,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
return false
}
override fun damageOnCollision(
settings: BirdSettings,
state: BirdState,
vehicle: PokemonEntity,
impactVec: Vec3
): Boolean {
if (!state.gliding.get()) return false
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
}
override fun getRideSounds(
settings: BirdSettings,
state: BirdState,
@@ -672,56 +603,57 @@ class BirdSettings : RidingBehaviourSettings {
override val key = BirdBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteAltitude: Expression? = null
var infiniteAltitude: Expression = "false".asExpression()
private set
var infiniteStamina: Expression? = null
var infiniteStamina: Expression = "false".asExpression()
private set
var timeToRollCorrect: Expression? = null
var timeToRollCorrect: Expression = "0.5".asExpression()
private set
var handlingExpr: Expression? = null
var horizontalPitchExpr: Expression? = null
var mouseInputMult: Expression? = null
var speedExpr: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 90.0, 45.0)".asExpression()
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.2, 0.1)".asExpression()
var mouseInputMult: Expression = "q.get_ride_stats('SKILL', 'AIR', 1.2, 0.8)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 14.0, 8.0)".asExpression()
// Seconds from stationary to top speed
var accelerationExpr: Expression? = null
var staminaExpr: Expression? = null
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 5.0)".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 22.0, 12.0)".asExpression()
var glideSpeedExpr: Expression? = null
var glidespeedExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.2, 1.5)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteAltitude)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(glideSpeedExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(horizontalPitchExpr)
buffer.writeNullableExpression(mouseInputMult)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(timeToRollCorrect)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(glidespeedExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(horizontalPitchExpr)
buffer.writeExpression(mouseInputMult)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(timeToRollCorrect)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteAltitude = buffer.readNullableExpression()
infiniteStamina = buffer.readNullableExpression()
glideSpeedExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
horizontalPitchExpr = buffer.readNullableExpression()
mouseInputMult = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
timeToRollCorrect = buffer.readNullableExpression()
infiniteAltitude = buffer.readExpression()
infiniteStamina = buffer.readExpression()
glidespeedExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
horizontalPitchExpr = buffer.readExpression()
mouseInputMult = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
timeToRollCorrect = buffer.readExpression()
}
}
@@ -730,12 +662,11 @@ class BirdState : RidingBehaviourState() {
var lastGlide = ridingState(-100L, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
var noInputTimeRoll = ridingState(0.0, Side.CLIENT)
var noInputTimePitch = ridingState(0.0, Side.CLIENT)
var currUpsideDownForce = ridingState(0.0, Side.CLIENT)
var noInputTime = ridingState(0.0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeFloat(stamina.get())
buffer.writeBoolean(gliding.get())
}
@@ -750,8 +681,7 @@ class BirdState : RidingBehaviourState() {
lastGlide.set(-100L, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
currPitchCorrectionForce.set(0.0, forced = true)
noInputTimeRoll.set(0.0, forced = true)
noInputTimePitch.set(0.0, forced = true)
noInputTime.set(0.0, forced = true)
}
override fun toString(): String {
@@ -765,8 +695,7 @@ class BirdState : RidingBehaviourState() {
it.lastGlide.set(this.lastGlide.get(), forced = true)
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
it.currPitchCorrectionForce.set(this.currPitchCorrectionForce.get(), forced = true)
it.noInputTimeRoll.set(this.noInputTimeRoll.get(), forced = true)
it.noInputTimePitch.set(this.noInputTimePitch.get(), forced = true)
it.noInputTime.set(this.noInputTime.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -775,16 +704,3 @@ class BirdState : RidingBehaviourState() {
return super.shouldSync(previous)
}
}
/**
* Calculates the shortest difference between two angles.
* The result will be in the range [-180, 180].
* A positive result indicates a clockwise direction from this angle to the other,
* a negative result indicates a counter-clockwise direction.
*/
fun Float.angleDifference(other: Float): Float {
var diff = other - this
while (diff <= -180F) diff += 360F
while (diff > 180F) diff -= 360F
return diff
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
@@ -38,9 +37,6 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
override val key = KEY
val globalGlider: GliderSettings
get() = CobblemonRideSettings.glider
override fun getRidingStyle(settings: GliderSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.AIR
}
@@ -57,7 +53,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
}
override fun speed(settings: GliderSettings, state: RidingBehaviourState, vehicle: PokemonEntity, driver: Player): Float {
return vehicle.runtime.resolveFloat(settings.speed ?: globalGlider.speed!!)
return vehicle.runtime.resolveFloat(settings.speed)
}
override fun rotation(
@@ -76,9 +72,10 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
driver: Player,
input: Vec3
): Vec3 {
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe ?: globalGlider.canStrafe!!)) driver.xxa.toDouble() else 0.0
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed ?: globalGlider.glideSpeed!!)
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
val zVector = driver.zza.toDouble()
val speedStat = settings.calculate(RidingStat.SPEED, 0)
return Vec3(xVector, yVector, zVector)
}
@@ -199,30 +196,31 @@ class GliderSettings : RidingBehaviourSettings {
override val key = GliderBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var glideSpeed: Expression? = null
var glideSpeed: Expression = "0.1".asExpression()
private set
var speed: Expression? = null
var speed: Expression = "1.0".asExpression()
private set
var canStrafe: Expression? = null
var canStrafe: Expression = "false".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(glideSpeed)
buffer.writeNullableExpression(speed)
buffer.writeNullableExpression(canStrafe)
buffer.writeExpression(glideSpeed)
buffer.writeExpression(speed)
buffer.writeExpression(canStrafe)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
glideSpeed = buffer.readNullableExpression()
speed = buffer.readNullableExpression()
canStrafe = buffer.readNullableExpression()
glideSpeed = buffer.readExpression()
speed = buffer.readExpression()
canStrafe = buffer.readExpression()
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
@@ -22,16 +21,7 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sin
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -40,6 +30,9 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sin
class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourState> {
companion object {
@@ -48,8 +41,6 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
}
override val key = KEY
val globalHelicopter: HelicopterSettings
get() = CobblemonRideSettings.helicopter
override fun getRidingStyle(settings: HelicopterSettings, state: RidingBehaviourState): RidingStyle {
return RidingStyle.AIR
@@ -102,8 +93,8 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
val runtime = vehicle.runtime
//If the player is not modulating height then hover
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity ?: globalHelicopter.verticalVelocity!!)
var yVel = if (driver.jumping) runtime.resolveDouble(settings.verticalVelocity)
else if (driver.isShiftKeyDown) -runtime.resolveDouble(settings.verticalVelocity)
else (0.0)
yVel *= 0.25
@@ -299,35 +290,36 @@ class HelicopterSettings : RidingBehaviourSettings {
override val key = HelicopterBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var gravity: Expression? = null
var gravity: Expression = "1.0".asExpression()
private set
var horizontalAcceleration: Expression? = null
var horizontalAcceleration: Expression = "0.1".asExpression()
private set
var verticalVelocity: Expression? = null
var verticalVelocity: Expression = "1.0".asExpression()
private set
var speed: Expression? = null
var speed: Expression = "1.0".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(gravity)
buffer.writeNullableExpression(horizontalAcceleration)
buffer.writeNullableExpression(verticalVelocity)
buffer.writeNullableExpression(speed)
buffer.writeExpression(gravity)
buffer.writeExpression(horizontalAcceleration)
buffer.writeExpression(verticalVelocity)
buffer.writeExpression(speed)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readNullableExpression()
horizontalAcceleration = buffer.readNullableExpression()
verticalVelocity = buffer.readNullableExpression()
speed = buffer.readNullableExpression()
gravity = buffer.readExpression()
horizontalAcceleration = buffer.readExpression()
verticalVelocity = buffer.readExpression()
speed = buffer.readExpression()
}
}
@@ -9,9 +9,10 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
@@ -26,6 +27,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.ClipContext
@@ -42,8 +44,6 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
}
override val key = KEY
val globalHover: HoverSettings
get() = CobblemonRideSettings.hover
override fun getRidingStyle(settings: HoverSettings, state: HoverState): RidingStyle {
return RidingStyle.AIR
@@ -97,31 +97,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver: Player,
input: Vec3
) {
if(vehicle.level().isClientSide) {
tickStamina(settings, state, vehicle)
checkTooHigh(settings, state, vehicle)
}
}
fun checkTooHigh(
settings: HoverSettings,
state: HoverState,
vehicle: PokemonEntity,
) {
val heightLimit = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHover.jumpExpr!!)
val pos = vehicle.position()
val level = Minecraft.getInstance().player?.level() ?: return
val hit = level.clip(
ClipContext(
pos,
pos.subtract(0.0, heightLimit, 0.0),
ClipContext.Block.COLLIDER,
ClipContext.Fluid.NONE,
vehicle
)
)
state.tooHigh.set(hit.type == HitResult.Type.MISS)
tickStamina(settings, state, vehicle)
}
fun tickStamina(
@@ -129,13 +105,10 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
state: HoverState,
vehicle: PokemonEntity
) {
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHover.infiniteStamina!!)) {
return
}
val stam = state.stamina.get()
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHover.staminaExpr!!)).toFloat() / 20.0f
val stamReplenishRate = (1.0f / vehicle.runtime.resolveDouble(settings.stamReplenishTimeSeconds ?: globalHover.stamReplenishTimeSeconds!!) ).toFloat() / 20.0f // 2 seconds to replenish a full bar of stamina
val newStam = if (state.tooHigh.get()) max(0.0f, stam - stamDrainRate) else min(1.0f,stam + stamReplenishRate)
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
val newStam = min(1.0f,stam + stamDrainRate)
state.stamina.set(newStam)
}
@@ -181,8 +154,8 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHover.handlingExpr!!) / 20.0
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0
val maxYawDiff = 90.0f
// Normalize the current rotation diff
@@ -217,8 +190,8 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver: Player
): Vec3 {
// Convert these to their tick values
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHover.speedExpr!!) / 20.0
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHover.accelerationExpr!!) * 20.0)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
var newVelocity = state.rideVelocity.get()
@@ -233,7 +206,6 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
// Vertical movement based on driver input.
val vertInput = when {
state.stamina.get() == 0.0f -> -1.0
driver.jumping -> 1.0
driver.isShiftKeyDown -> -1.0
else -> 0.0
@@ -247,6 +219,28 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
driver.zza.sign.toDouble()
).normalize()
/******************************************************
* Boosting Logic
*****************************************************/
if (driver.level().isClientSide) {
if(Minecraft.getInstance().options.keySprint.isDown() && state.stamina.get() == 1.0f && !state.isBoosting.get()) {
state.isBoosting.set(true)
val localBoostVel = if (inputVel.horizontalDistance() == 0.0) Vec3(0.0, 0.0, 1.0) else Vec3(inputVel.x, 0.0, inputVel.z)
state.boostVec.set(localBoostVel)
state.boostTicks.set(0)
state.stamina.set(0.0f)
}
}
val maxBoostTicks = 20
val boostPower = (topSpeed * 20) * 0.1 * vehicle.runtime.resolveDouble(settings.jumpExpr)
if (state.isBoosting.get() && state.boostTicks.get() < maxBoostTicks) {
newVelocity = newVelocity.add(state.boostVec.get().scale(boostPower / maxBoostTicks))
state.boostTicks.set(state.boostTicks.get() + 1)
} else if (state.isBoosting.get()) {
state.isBoosting.set(false)
}
// Air Friction/Resistance
// When the dot between friction and input is -1 we want to apply none of it.
// In short we want to avoid applying friction that opposes our input force.
@@ -433,61 +427,51 @@ class HoverSettings : RidingBehaviourSettings {
override val key = HoverBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteStamina: Expression? = null
private set
var canJump: Expression? = null
private set
var stamReplenishTimeSeconds: Expression? = null
var canJump = "true".asExpression()
private set
// Speed in block per second
var speedExpr: Expression? = null
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 13.0, 6.0)".asExpression()
private set
// Acceleration in number of seconds to top speed
var accelerationExpr: Expression? = null
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', 2.0, 5.0)".asExpression()
private set
// Amount of seconds between boosts
var staminaExpr: Expression? = null
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 1, 4)".asExpression()
private set
// Blocks before stamina drain
var jumpExpr: Expression? = null
// Power of boost as a fraction of top speed
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 1.2, 0.2)".asExpression()
private set
// Turn rate in degrees per second when stationary
var handlingExpr: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 360.0, 90)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(stamReplenishTimeSeconds)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
stamReplenishTimeSeconds = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
}
}
@@ -497,20 +481,17 @@ class HoverState : RidingBehaviourState() {
var isBoosting = ridingState(false, Side.BOTH)
var boostVec = ridingState(Vec3.ZERO, Side.CLIENT)
var boostTicks = ridingState(0, Side.CLIENT)
var tooHigh = ridingState(false, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeDouble(speed.get())
buffer.writeBoolean(isBoosting.get())
buffer.writeBoolean(tooHigh.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
speed.set(buffer.readDouble(), forced = true)
isBoosting.set(buffer.readBoolean(), forced = true)
tooHigh.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
@@ -519,7 +500,6 @@ class HoverState : RidingBehaviourState() {
isBoosting.set(false, forced = true)
boostVec.set(Vec3.ZERO, forced = true)
boostTicks.set(0, forced = true)
tooHigh.set(false, forced = true)
}
override fun copy() = HoverState().also {
@@ -528,7 +508,6 @@ class HoverState : RidingBehaviourState() {
it.isBoosting.set(this.isBoosting.get(), forced = true)
it.boostVec.set(this.boostVec.get(), forced = true)
it.boostTicks.set(this.boostTicks.get(), forced = true)
it.tooHigh.set(this.tooHigh.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -11,35 +11,22 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseState
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import net.minecraft.client.Minecraft
import com.cobblemon.mod.common.util.*
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.config.CobblemonConfig
import net.minecraft.client.Minecraft
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
@@ -47,7 +34,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Vector3f
import kotlin.math.*
class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
companion object {
@@ -60,9 +47,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
return RidingStyle.AIR
}
val globalJet: JetSettings
get() = CobblemonRideSettings.jet
val poseProvider = PoseProvider<JetSettings, JetState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
@@ -103,7 +87,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
if(state.stamina.get() != 0.0f && boostKeyPressed) {
if (state.stamina.get() >= 0.0f) {
if (state.stamina.get() >= 0.25f) {
//If on the previous tick the boost key was held then don't change if the ride is boosting
if(state.boostIsToggleable.get()) {
//flip the boosting state if boost key is pressed
@@ -127,19 +111,13 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
) {
val stam = state.stamina.get()
var newStam = stam
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalJet.staminaExpr!!)).toFloat() / 20.0f
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
if (state.boosting.get()) {
newStam = max(0.0f,stam - stamDrainRate * 1.5f)
} else {
newStam = max(0.0f,stam - stamDrainRate)
}
// If out of stamina then increase noStamTickCnt
if (newStam == 0.0f) {
state.noStamTickCnt.set(state.noStamTickCnt.get() + 1)
} else {
state.noStamTickCnt.get()
newStam = min(1.0f,stam + stamDrainRate * 0.25f)
}
state.stamina.set(newStam)
@@ -161,26 +139,23 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
driver: Player,
input: Vec3
): Vec3 {
var upForce = 0.0
var forwardForce = 0.0
val controller = (driver as? OrientationControllable)?.orientationController
if (controller == null || controller.orientation == null) return Vec3.ZERO
//Calculate ride space velocity
calculateRideSpaceVel(settings, state, vehicle, driver)
// The downward force used to encourage players to stop flying upside down.
val maxDownwardForceTickCnt = 20.0
val extraDownwardForce = if(state.stamina.get() == 0.0f) -0.3 * (state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0, 1.0) else 0.0 // 6 blocks a second downward
//Translate ride space velocity to world space velocity.
if (controller != null) {
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
}
// Convert the local velocity vector into a world vector
val localVelVec = Vector3f(
state.rideVelocity.get().x.toFloat(),
(state.rideVelocity.get().y).toFloat(),
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed? orr.. right handed? idk, flip it though
)
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians()) // Unrotate preemptively as this vector gets rotate later down the line in MC logic.
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
val velocity = Vec3(0.0, upForce, forwardForce)
return worldVelVec
return velocity
}
/*
@@ -192,28 +167,17 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
vehicle: PokemonEntity,
driver: Player
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalJet.speedExpr!!) / 20.0
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalJet.accelerationExpr!!) * 20.0)
val deccel = vehicle.runtime.resolveDouble(settings.deccelRate ?: globalJet.deccelRate!!)//0.005
val minSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.minSpeedFactor ?: globalJet.minSpeedFactor!!)
val speed = state.rideVelocity.get().z
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalJet.jumpExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) / 400.0
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed) / 20.0
val speed = state.rideVelocity.get().length()
val boostMult = vehicle.runtime.resolveDouble(settings.jumpExpr)
val boostTopSpeed = topSpeed * boostMult
val boostAccel = accel * boostMult
//speed up and slow down based on input
if (state.stamina.get() == 0.0f) {
// Decelerate currently always a constant half of max acceleration.
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - (deccel * 1.3), 0.3 * 0.5)
)
)
}
else if (state.boosting.get() && speed < boostTopSpeed) {
if (state.boosting.get() && speed < boostTopSpeed) {
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
@@ -237,7 +201,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - (deccel), minSpeed)
max(state.rideVelocity.get().z - ((accel) / 2), minSpeed)
)
)
} else if (speed > topSpeed) {
@@ -245,17 +209,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
state.rideVelocity.get().scale(0.98)
)
}
/****************************************************************************
* Kill strafing velocities carried over from controller transition. Don't
* kill immediately to give some momentum to transitions.
***************************************************************************/
state.rideVelocity.set(Vec3(
lerp(state.rideVelocity.get().x, 0.0, 0.03),
lerp(state.rideVelocity.get().y, 0.0, 0.03),
state.rideVelocity.get().z
))
}
override fun angRollVel(
@@ -272,7 +225,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
val cappedDeltaTime = min(deltaTime, 0.2)
//Get handling in degrees per second
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr ?: globalJet.handlingYawExpr!!)
val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
//Base the change off of deltatime.
var handlingYaw = yawRotRate * (cappedDeltaTime)
@@ -352,8 +305,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
//Get handling in degrees per second
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
//convert it to delta time
handling *= (cappedDeltaTime)
@@ -406,8 +358,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
//Get handling in degrees per second
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
//convert it to delta time
handling *= (cappedDeltaTime)
@@ -483,12 +434,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val maxDownwardForceTickCnt = 20.0f
return when {
state.noStamTickCnt.get() != 0 -> 1f - 0.2f*(state.noStamTickCnt.get() / maxDownwardForceTickCnt).coerceIn(0.0f,1.0f)
state.boosting.get() -> 1.2f
else -> 1.0f
}
return if (state.boosting.get()) 1.2f else 1.0f
}
override fun useAngVelSmoothing(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
@@ -561,70 +507,66 @@ class JetSettings : RidingBehaviourSettings {
override val key = JetBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var gravity: Expression? = null
var gravity: Expression = "0".asExpression()
private set
var deccelRate: Expression? = null
var minSpeed: Expression = "12.0".asExpression()
private set
// Mult to top speed in order to derive minSpeed
var minSpeedFactor: Expression? = null
private set
var handlingYawExpr: Expression? = null
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
private set
// Make configurable by json
var infiniteStamina: Expression? = null
var infiniteStamina: Expression = "false".asExpression()
private set
// Boost power. Mult for top speed and accel while boosting
var jumpExpr: Expression? = null
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 1.7, 1.2)".asExpression()
private set
// Turn rate in degrees per second
var handlingExpr: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 120.0, 60.0)".asExpression()
private set
// Top Speed in blocks per second
var speedExpr: Expression? = null
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 36.0, 20.0)".asExpression()
private set
// Seconds to get to top speed
var accelerationExpr: Expression? = null
// Acceleration in blocks per s^2
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', 5.0, 2.5)".asExpression()
private set
// Time in seconds to drain full bar of stamina flying
var staminaExpr: Expression? = null
// Time in seconds to drain full bar of stamina when boosting
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 8.0, 4.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(gravity)
buffer.writeNullableExpression(deccelRate)
buffer.writeNullableExpression(minSpeedFactor)
buffer.writeNullableExpression(handlingYawExpr)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeExpression(gravity)
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
gravity = buffer.readNullableExpression()
deccelRate = buffer.readNullableExpression()
minSpeedFactor = buffer.readNullableExpression()
handlingYawExpr = buffer.readNullableExpression()
infiniteStamina = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
gravity = buffer.readExpression()
minSpeed = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
}
}
@@ -635,7 +577,6 @@ class JetState : RidingBehaviourState() {
var boosting = ridingState(false, Side.BOTH)
var boostIsToggleable = ridingState(false, Side.BOTH)
var canSpeedBurst = ridingState(false, Side.BOTH)
var noStamTickCnt = ridingState(0, Side.CLIENT) // Value that is increased for every tick you are out of stamina
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
@@ -662,7 +603,6 @@ class JetState : RidingBehaviourState() {
boosting.set(false, forced = true)
boostIsToggleable.set(false, forced = true)
canSpeedBurst.set(true, forced = true)
noStamTickCnt.set(0, forced = true)
}
override fun copy() = JetState().also {
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
@@ -41,8 +40,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
}
override val key = KEY
val globalRocket: RocketSettings
get() = CobblemonRideSettings.rocket
override fun getRidingStyle(settings: RocketSettings, state: RocketState): RidingStyle {
return RidingStyle.AIR
@@ -87,9 +84,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
driver: Player
): Float {
// Use this as a "tick" function and check to see if the driver is "boosting"
if (vehicle.level().isClientSide) {
if(vehicle.level().isClientSide) {
handleBoosting(settings, state, vehicle, driver)
tickStamina(settings, state, vehicle, driver)
tickStamina(settings, state, vehicle)
}
return state.rideVelocity.get().length().toFloat()
@@ -104,6 +101,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
//If the forward key is not held then it cannot be boosting
if(Minecraft.getInstance().options.keyUp.isDown() && state.stamina.get() != 0.0f) {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
if (state.stamina.get() >= 0.25f) {
//If on the previous tick the boost key was held then don't change if the ride is boosting
if(state.boostIsToggleable.get() && boostKeyPressed) {
//flip the boosting state if boost key is pressed
@@ -111,6 +109,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
}
//If the boost key is not held then next tick boosting is toggleable
state.boostIsToggleable.set(!boostKeyPressed)
}
} else {
//Turn off boost and reset boost params
state.boostIsToggleable.set(true)
@@ -122,22 +121,18 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
fun tickStamina(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
vehicle: PokemonEntity
) {
val stam = state.stamina.get()
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalRocket.infiniteStamina!!)) {
return
}
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalRocket.staminaExpr!!) * 20.0f
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr) * 20.0f
val stamDrainRate = (1.0f / boostTime).toFloat()
val newStam = if(state.boosting.get() || driver.jumping) max(0.0f,stam - stamDrainRate) else stam
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
else min(1.0f,stam + stamDrainRate)
state.stamina.set(newStam)
}
@@ -186,13 +181,13 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
var newMomentum = state.turnMomentum.get().toDouble()
// Degrees per tick
val maxTurnMomentum = vehicle.runtime.resolveDouble(settings.maxTurnRate ?: globalRocket.maxTurnRate!!) / 20.0f
val maxTurnMomentum = vehicle.runtime.resolveDouble(settings.maxTurnRate) / 20.0f
// Turn rate increase per tick. Based off number of seconds to get to max turn rate
val turningAcceleration = maxTurnMomentum / (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalRocket.handlingExpr!!) * 20.0f)
val turningAcceleration = maxTurnMomentum / (vehicle.runtime.resolveDouble(settings.handlingExpr) * 20.0f)
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
// Base boost stats off of normal turning stats
val boostHandlingMod = vehicle.runtime.resolveDouble(settings.boostHandlingMod ?: globalRocket.boostHandlingMod!!)
val boostHandlingMod = vehicle.runtime.resolveDouble(settings.boostHandlingMod)
val boostMaxTurnMomentum = maxTurnMomentum * boostHandlingMod
val boostTurnInput = turnInput * boostHandlingMod
@@ -237,9 +232,9 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
vehicle: PokemonEntity,
driver: Player
): Vec3 {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalRocket.accelerationExpr!!)
val jump = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalRocket.jumpExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val jump = vehicle.runtime.resolveDouble(settings.jumpExpr)
var newVelocity = vehicle.deltaMovement
@@ -263,7 +258,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
(newVelocity.z + (accel * forwardInput.toDouble())))
} else if ((state.boosting.get())) {
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod)
val boostSpeed = topSpeed * boostMod
val boostAccel = if(newVelocity.length() < boostSpeed) accel * (boostMod/1.5) else 0.0
val forwardInput = 1.0f
@@ -271,7 +266,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
if(state.canSpeedBurst.get()) {
burst = 0.5f
state.stamina.set(max(0.0f,state.stamina.get() - 0.05f))
state.stamina.set(max(0.0f,state.stamina.get() - 0.1f))
state.canSpeedBurst.set(false)
}
@@ -306,7 +301,7 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
} else {
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
// More force if traveling downwards to allow for quicker fall stops
vertInput = (if (state.stamina.get() == 0.0f) -1.0f else if(newVelocity.y < 0) jump.toFloat() * 3.0f else jump.toFloat()).toDouble()
vertInput = if (newVelocity.y < 0) jump * 3 else jump
// Reset falldistance if upward motion is detected
vehicle.resetFallDistance()
newVelocity = Vec3(
@@ -405,8 +400,8 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
driver: Player
): Float {
if (state.boosting.get()) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalRocket.speedExpr!!)
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod ?: globalRocket.boostSpeedMod!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val boostMod = vehicle.runtime.resolveDouble(settings.boostSpeedMod)
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * boostMod)
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
} else {
@@ -479,18 +474,6 @@ class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
return false
}
override fun damageOnCollision(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
impactVec: Vec3
): Boolean {
if (!state.boosting.get()) return false
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
}
override fun getRideSounds(
settings: RocketSettings,
state: RocketState,
@@ -506,67 +489,60 @@ class RocketSettings : RidingBehaviourSettings {
override val key = RocketBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteStamina: Expression? = null
private set
// Boost multiplier for speed
var boostSpeedMod: Expression? = null
var boostSpeedMod: Expression = "5.0".asExpression()
private set
// Boost multiplier for handling
var boostHandlingMod: Expression? = null
var boostHandlingMod: Expression = "0.3".asExpression()
private set
// Max turn rate in degrees per second
var maxTurnRate: Expression? = null
var maxTurnRate: Expression = "100.0".asExpression()
private set
var speedExpr: Expression? = null
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.2, 0.6)".asExpression()
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression? = null
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
private set
// air time in seconds
var staminaExpr: Expression? = null
// Boost time in seconds
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 7.5, 2.5)".asExpression()
private set
var jumpExpr: Expression? = null
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 2.0, 1.0)".asExpression()
private set
// How long it takes to get to max turn rate from rest (in seconds)
var handlingExpr: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 0.5, 3.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(boostSpeedMod)
buffer.writeNullableExpression(boostHandlingMod)
buffer.writeNullableExpression(maxTurnRate)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteStamina = buffer.readNullableExpression()
boostSpeedMod = buffer.readNullableExpression()
boostHandlingMod = buffer.readNullableExpression()
maxTurnRate = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
}
}
class RocketState : RidingBehaviourState() {
@@ -387,10 +387,9 @@ open class CompositeSettings : RidingBehaviourSettings {
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeRidingStats(stats)
buffer.writeResourceLocation(key)
buffer.writeResourceLocation(transitionStrategy)
buffer.writeResourceLocation(defaultBehaviour.key)
defaultBehaviour.encode(buffer)
buffer.writeResourceLocation(alternateBehaviour.key)
alternateBehaviour.encode(buffer)
}
@@ -41,10 +41,6 @@ interface CompositeRidingStrategy<T : CompositeSettings> {
toState: RidingBehaviourState,
toSettings: RidingBehaviourSettings
) {
if (vehicle.level().isClientSide) {
val fromSettings = if (settings.defaultBehaviour == toSettings) settings.alternateBehaviour else settings.defaultBehaviour
}
toState.stamina.set(fromState.stamina.get())
toState.rideVelocity.set(fromState.rideVelocity.get())
fromState.reset()
@@ -9,34 +9,15 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
import kotlin.math.sqrt
import com.cobblemon.mod.common.util.*
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -49,6 +30,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
companion object {
@@ -56,8 +38,6 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
}
override val key = KEY
val globalHorse: HorseSettings
get() = CobblemonRideSettings.horse
override fun getRidingStyle(settings: HorseSettings, state: HorseState): RidingStyle {
return RidingStyle.LAND
@@ -152,13 +132,8 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
) {
val stam = state.stamina.get()
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalHorse.infiniteStamina!!)) {
return
}
var newStam = stam
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalHorse.staminaExpr!!)).toFloat() / 20.0f
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr)).toFloat() / 20.0f
if (state.sprinting.get()) {
newStam = max(0.0f,stam - stamDrainRate)
@@ -208,10 +183,10 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalHorse.handlingExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val walkHandlingBoost = 5
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
val maxYawDiff = vehicle.runtime.resolveFloat(settings.lookYawLimit)
// Normalize the current rotation diff
val rotDiff = Mth.wrapDegrees(driver.yRot - vehicle.yRot).coerceIn(-maxYawDiff,maxYawDiff)
@@ -255,34 +230,21 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: Player
): Vec3 {
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint ?: globalHorse.canSprint!!)
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalHorse.canJump!!)
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalHorse.jumpExpr!!) * 0.75
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalHorse.speedExpr!!)
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
val jumpForce = vehicle.runtime.resolveDouble(settings.jumpExpr) * 0.75
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val walkSpeed = getWalkSpeed(vehicle)
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalHorse.accelerationExpr!!) * 20.0)
val accel = topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)
var activeInput = false
/******************************************************
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
val dmSpeed = vehicle.deltaMovement.length()
val rvSpeed = state.rideVelocity.get().length()
var newVelocity = state.rideVelocity.get() //.normalize().scale(vehicle.deltaMovement.horizontalDistance())
/******************************************************
* Gather the previous velocity and check for horizontal
* collisions
*****************************************************/
// if (dmSpeed > rvSpeed) {
// // align the velocity vector to be in local vehicle space
// newVelocity = vehicle.deltaMovement
// val yawAligned = Matrix3f().rotateY(-vehicle.yRot.toRadians())
// newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
// }
if (vehicle.horizontalCollision) {
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
}
@@ -294,7 +256,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
// If on a tight turn then do not speed up past half of top speed in order to turn quicker
// Also determine how fast to be slowing down based on how far turned you are
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit ?: globalHorse.lookYawLimit!!)
val lookYawLimit = vehicle.runtime.resolveFloat(settings.lookYawLimit)
val percOfMaxTurnSpeed = abs(Mth.wrapDegrees(driver.yRot - vehicle.yRot) / lookYawLimit) * 100.0f
val turnPercThresh = 0.0f
val s = min(((percOfMaxTurnSpeed - turnPercThresh) / (100.0f - turnPercThresh)).pow(1),1.0f)
@@ -536,67 +498,59 @@ class HorseSettings : RidingBehaviourSettings {
override val key = HorseBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteStamina: Expression? = null
private set
var canJump: Expression? = null
var canJump = "true".asExpression()
private set
var canSprint: Expression? = null
var canSprint = "true".asExpression()
private set
var lookYawLimit: Expression? = null
var lookYawLimit = "90.0f".asExpression()
private set
var speedExpr: Expression? = null
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.4)".asExpression()
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression? = null
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', 0.5, 2.0)".asExpression()
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression? = null
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
private set
//Between a one block jump and a six block jump
var jumpExpr: Expression? = null
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 1.2, 0.45)".asExpression()
private set
var handlingExpr: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 180.0, 40.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(canSprint)
buffer.writeNullableExpression(lookYawLimit)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(canSprint)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(canJump)
buffer.writeExpression(canSprint)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
canSprint = buffer.readNullableExpression()
lookYawLimit = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
canSprint = buffer.readNullableExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
canJump = buffer.readExpression()
canSprint = buffer.readExpression()
}
}
@@ -10,36 +10,18 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.SidedRidingState
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -50,6 +32,12 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Matrix3f
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
companion object {
@@ -57,8 +45,6 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
}
override val key = KEY
val globalMinekart: MinekartSettings
get() = CobblemonRideSettings.minekart
override fun getRidingStyle(settings: MinekartSettings, state: MinekartState): RidingStyle {
return RidingStyle.LAND
@@ -98,7 +84,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
driver: Player
): Float {
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit ?: globalMinekart.boostLimit!!)
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit)
//TODO: should this be by how much turn has accumulated or simply time turning... or both?
val boostPerSecond = 20.0f
val boostLoss = 40.0f
@@ -156,8 +142,8 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
var newMomentum = state.turnMomentum.get().toDouble()
//TODO: maybe tie this to max turnspeeds rather than the turn acceleration
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!)
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalMinekart.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val driftInput = turnInput * 0.25f
@@ -216,9 +202,9 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
): Vec3 {
// Check to see if the ride should be walking or sprinting
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalMinekart.speedExpr!!) * 0.6 * 2.0
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.7 else rideTopSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalMinekart.accelerationExpr!!) * 0.3 * 3 * 2.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
val driftAngle = 20.0f
//Align the direction of this vector with the vehicles current heading.
@@ -341,7 +327,7 @@ class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalMinekart.canJump!!)
return vehicle.runtime.resolveBoolean(settings.canJump)
}
override fun setRideBar(
@@ -461,57 +447,45 @@ class MinekartSettings : RidingBehaviourSettings {
override val key = MinekartBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteStamina: Expression? = null
private set
var canJump: Expression? = null
var canJump = "true".asExpression()
private set
var boostLimit: Expression? = null
var boostLimit = "100.0f".asExpression()
private set
var speedExpr: Expression? = null
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression? = null
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
var staminaExpr: Expression? = null
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression? = null
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
private set
var handlingExpr: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(boostLimit)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeExpression(canJump)
buffer.writeExpression(boostLimit)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
boostLimit = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
canJump = buffer.readExpression()
boostLimit = buffer.readExpression()
}
}
@@ -10,35 +10,18 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.SidedRidingState
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sign
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -50,6 +33,10 @@ import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Matrix3f
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sign
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
companion object {
@@ -57,8 +44,6 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
}
override val key = KEY
val globalVehicle: VehicleSettings
get() = CobblemonRideSettings.vehicle
override fun getRidingStyle(settings: VehicleSettings, state: VehicleState): RidingStyle {
return RidingStyle.LAND
@@ -141,7 +126,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
var newMomentum = state.turnMomentum.get().toDouble()
// Grab turningAcceleration and divide 20 twice to get
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalVehicle.handlingExpr!!) * 1.5 / 20.0f) / 20.0f * 4.0
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val maxTurnMomentum = 80.0f / 20.0f
@@ -152,7 +137,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
if(state.drifting.get() || state.inAir.get()) {
if( newMomentum < driftMaxTurnMomentum) {
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!))
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr))
newMomentum += driftTurnInput
}
newMomentum = lerp(newMomentum, 0.0, 0.03)
@@ -192,10 +177,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
// Check to see if the ride should be walking or sprinting
//val walkSpeed = getWalkSpeed(vehicle)
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalVehicle.speedExpr!!) * 0.6 * 2.0
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.5 else rideTopSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalVehicle.accelerationExpr!!) * 0.3 * 3 * 2.0
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
//Flag for determining if player is actively inputting
var activeInput = false
@@ -265,7 +250,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
val canJump = vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
//Jump the thang!
if (driver.jumping && vehicle.onGround() && canJump) {
val jumpForce = 0.5
@@ -311,7 +296,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump ?: globalVehicle.canJump!!)
return vehicle.runtime.resolveBoolean(settings.canJump)
}
override fun setRideBar(
@@ -427,79 +412,66 @@ class VehicleSettings : RidingBehaviourSettings {
override val key = VehicleBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteStamina: Expression? = null
var canJump = "true".asExpression()
private set
var canJump: Expression? = null
var speed = "0.3".asExpression()
private set
var speed: Expression? = null
var driveFactor = "1.0".asExpression()
private set
var driveFactor: Expression? = null
var reverseDriveFactor = "0.25".asExpression()
private set
var reverseDriveFactor: Expression? = null
var rotationSpeed = "45/20".asExpression()
private set
var rotationSpeed: Expression? = null
var lookYawLimit = "101".asExpression()
private set
var lookYawLimit: Expression? = null
private set
var speedExpr: Expression? = null
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
private set
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
var accelerationExpr: Expression? = null
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression? = null
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression? = null
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
private set
var handlingExpr: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(speed)
buffer.writeNullableExpression(driveFactor)
buffer.writeNullableExpression(reverseDriveFactor)
buffer.writeNullableExpression(rotationSpeed)
buffer.writeNullableExpression(lookYawLimit)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeExpression(canJump)
buffer.writeExpression(speed)
buffer.writeExpression(driveFactor)
buffer.writeExpression(reverseDriveFactor)
buffer.writeExpression(rotationSpeed)
buffer.writeExpression(lookYawLimit)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
speed = buffer.readNullableExpression()
driveFactor = buffer.readNullableExpression()
reverseDriveFactor = buffer.readNullableExpression()
rotationSpeed = buffer.readNullableExpression()
lookYawLimit = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
canJump = buffer.readExpression()
speed = buffer.readExpression()
driveFactor = buffer.readExpression()
reverseDriveFactor = buffer.readExpression()
rotationSpeed = buffer.readExpression()
lookYawLimit = buffer.readExpression()
}
}
@@ -8,34 +8,16 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import net.minecraft.core.BlockPos
import com.cobblemon.mod.common.util.*
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
@@ -45,6 +27,9 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
companion object {
@@ -52,8 +37,6 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
}
override val key = KEY
val globalBoat: BoatSettings
get() = CobblemonRideSettings.boat
override fun getRidingStyle(settings: BoatSettings, state: BoatState): RidingStyle {
return RidingStyle.LIQUID
@@ -103,7 +86,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
if (!state.isVehicleSprinting.get()) {
if (state.staminaBuffer.get() >= 20) {
val staminaIncrease = vehicle.runtime.resolveDouble(settings.staminaIncreaseExpr ?: globalBoat.staminaIncreaseExpr!!)
val staminaIncrease = vehicle.runtime.resolveDouble(settings.staminaIncreaseExpr)
state.stamina.set(min(1.0f, state.stamina.get() + 0.01f * staminaIncrease.toFloat()))
}
}
@@ -150,14 +133,14 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
}
private fun applyVelocityFromInput(velocity: Vec3, vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState): Vec3 {
val speed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBoat.speedExpr!!)
val acceleration = speed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalBoat.accelerationExpr!!) * 20)
val speed = vehicle.runtime.resolveDouble(settings.speedExpr)
val acceleration = vehicle.runtime.resolveDouble(settings.accelerationExpr)
if (state.jumpBuffer.get() != -1 || !(vehicle.isInWater || vehicle.isUnderWater)) {
return velocity
}
val sprintModifier = vehicle.runtime.resolveDouble(settings.sprintSpeedModifier ?: globalBoat.sprintSpeedModifier!!)
val sprintModifier = vehicle.runtime.resolveDouble(settings.sprintSpeedModifier)
val activeSprintModifier = if (state.isVehicleSprinting.get() && hasStamina(state)) sprintModifier else 1.0
val forwardInput = driver.zza.toDouble()
val delta = when {
@@ -180,34 +163,23 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
if (!driver.jumping) return velocity // Not jumping
if (state.jumpBuffer.get() != -1) return velocity // Already jumped very recently
val jumpStrength = vehicle.runtime.resolveDouble(settings.jumpStrengthExpr ?: globalBoat.jumpStrengthExpr!!)
val jumpStrength = vehicle.runtime.resolveDouble(settings.jumpStrengthExpr)
state.jumpBuffer.set(0)
return Vec3(velocity.x, velocity.y + jumpStrength, velocity.z)
}
private fun applyGravity(velocity: Vec3, vehicle: PokemonEntity, settings: BoatSettings, state: BoatState): Vec3 {
if (state.jumpBuffer.get() == -1 && (vehicle.isInWater || vehicle.isUnderWater)) {
if (shouldFloatHigher(vehicle, settings)) {
return Vec3(velocity.x, 0.5, velocity.z)
}
else {
return Vec3(velocity.x, 0.0, velocity.z)
}
val verticalVelocity = if (vehicle.isUnderWater) 0.5 else 0.0
return Vec3(velocity.x, verticalVelocity, velocity.z)
}
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity ?: globalBoat.terminalVelocity!!)
val terminalVelocity = vehicle.runtime.resolveDouble(settings.terminalVelocity)
val gravity = (9.8 / ( 20.0)) * 0.2
return Vec3(velocity.x, max(velocity.y - gravity, terminalVelocity), velocity.z)
}
private fun shouldFloatHigher(vehicle: PokemonEntity, settings: BoatSettings): Boolean {
val surfaceOffset = vehicle.runtime.resolveFloat(settings.surfaceLevelOffset ?: globalBoat.surfaceLevelOffset!!)
val blockPos = BlockPos.containing(vehicle.x, vehicle.eyeY + surfaceOffset, vehicle.z)
val fluidState = vehicle.level().getFluidState(blockPos)
return !fluidState.isEmpty
}
private fun applyStrafeRotation(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState) {
val skillModifier = vehicle.runtime.resolveDouble(settings.rotationSpeedModifierExpr ?: globalBoat.rotationSpeedModifierExpr!!)
val skillModifier = vehicle.runtime.resolveDouble(settings.rotationspeedModifierExpr)
val strafe = driver.xxa
if (abs(strafe) > 0) {
val increase = if (strafe > 0) -1 else 1
@@ -220,10 +192,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
}
private fun consumeStamina(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState, drain: Float) {
val staminaModifier = vehicle.runtime.resolveFloat(settings.staminaDecreaseExpr ?: globalBoat.staminaDecreaseExpr!!)
if (staminaModifier == 0F) {
return
}
val staminaModifier = vehicle.runtime.resolveFloat(settings.staminaDecreaseExpr)
state.stamina.set(max(0f, state.stamina.get() - drain / staminaModifier))
if (state.stamina.get() == 0f) {
state.isVehicleSprinting.set(false)
@@ -329,7 +298,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val sprintFov = vehicle.runtime.resolveFloat(settings.sprintFovModifier ?: globalBoat.sprintFovModifier!!)
val sprintFov = vehicle.runtime.resolveFloat(settings.sprintFovModifier)
return if (state.isVehicleSprinting.get()) sprintFov else 1.0f
}
@@ -374,7 +343,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
state: BoatState,
vehicle: PokemonEntity
): Boolean {
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead ?: globalBoat.rotatePokemonHead!!)
return vehicle.runtime.resolveBoolean(settings.rotatePokemonHead)
}
override fun shouldRotateRiderHead(
@@ -404,67 +373,46 @@ class BoatSettings : RidingBehaviourSettings {
override val stats = mutableMapOf<RidingStat, IntRange>()
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
var terminalVelocity: Expression? = null
var terminalVelocity = "-2.0".asExpression()
private set
var rotatePokemonHead: Expression? = null
var rotatePokemonHead = "true".asExpression()
private set
var staminaIncreaseExpr: Expression? = null
var staminaIncreaseExpr = "q.get_ride_stats('STAMINA', 'LIQUID', 2.0, 1.0)".asExpression()
private set
var staminaDecreaseExpr: Expression? = null
var staminaDecreaseExpr = "q.get_ride_stats('STAMINA', 'LIQUID', 5.0, 1.0)".asExpression()
private set
var rotationSpeedModifierExpr: Expression? = null
var rotationspeedModifierExpr = "q.get_ride_stats('SKILL', 'LIQUID', 1.5, 0.5)".asExpression()
private set
var jumpStrengthExpr: Expression? = null
var jumpStrengthExpr = "q.get_ride_stats('JUMP', 'LIQUID', 2.5, 1.0)".asExpression()
private set
var speedExpr: Expression? = null
var speedExpr = "q.get_ride_stats('SPEED', 'LIQUID', 2.0, 0.5)".asExpression()
private set
var accelerationExpr: Expression? = null
var accelerationExpr = "q.get_ride_stats('ACCELERATION', 'LIQUID', 0.5, 0.05)".asExpression()
private set
var sprintSpeedModifier: Expression? = null
var sprintSpeedModifier = "1.5".asExpression()
private set
var sprintFovModifier: Expression? = null
var surfaceLevelOffset: Expression? = null
var sprintFovModifier = "1.2".asExpression()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(terminalVelocity)
buffer.writeNullableExpression(rotatePokemonHead)
buffer.writeNullableExpression(staminaIncreaseExpr)
buffer.writeNullableExpression(staminaDecreaseExpr)
buffer.writeNullableExpression(rotationSpeedModifierExpr)
buffer.writeNullableExpression(jumpStrengthExpr)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(sprintSpeedModifier)
buffer.writeNullableExpression(sprintFovModifier)
buffer.writeNullableExpression(surfaceLevelOffset)
buffer.writeExpression(terminalVelocity)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
terminalVelocity = buffer.readNullableExpression()
rotatePokemonHead = buffer.readNullableExpression()
staminaIncreaseExpr = buffer.readNullableExpression()
staminaDecreaseExpr = buffer.readNullableExpression()
rotationSpeedModifierExpr = buffer.readNullableExpression()
jumpStrengthExpr = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
sprintSpeedModifier = buffer.readNullableExpression()
sprintFovModifier = buffer.readNullableExpression()
surfaceLevelOffset = buffer.readNullableExpression()
terminalVelocity = buffer.readExpression()
}
}
@@ -8,14 +8,10 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
@@ -25,7 +21,6 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.writeNullable
import com.cobblemon.mod.common.util.writeRidingStats
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
@@ -43,8 +38,6 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
}
override val key = KEY
val globalBurst: BurstSettings
get() = CobblemonRideSettings.burst
override fun getRidingStyle(settings: BurstSettings, state: BurstState): RidingStyle {
return RidingStyle.LIQUID
@@ -74,7 +67,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
vehicle: PokemonEntity,
driver: Player
): Float {
if (state.dashing.get()) {
if(state.dashing.get()) {
state.ticks.set(state.ticks.get() + 1)
if(state.ticks.get() >= DASH_TICKS) {
state.dashing.set(false)
@@ -84,7 +77,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
}
state.dashing.set(true)
return settings.dashSpeed ?: globalBurst.dashSpeed!!
return settings.dashSpeed
}
override fun rotation(
@@ -246,21 +239,22 @@ class BurstSettings : RidingBehaviourSettings {
override val key = BurstBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var dashSpeed: Float? = null
var dashSpeed = 1F
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullable(dashSpeed) { buf, speed -> buf.writeFloat(speed) }
buffer.writeFloat(dashSpeed)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
dashSpeed = buffer.readNullable { buf -> buf.readFloat() }
dashSpeed = buffer.readFloat()
}
}
@@ -10,33 +10,21 @@ package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.orientation.OrientationController
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
import com.cobblemon.mod.common.api.riding.behaviour.Side
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
import com.cobblemon.mod.common.api.riding.behaviour.*
import com.cobblemon.mod.common.api.riding.posing.PoseOption
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toDegrees
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.readNullableExpression
import com.cobblemon.mod.common.util.readRidingStats
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.resolveDouble
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import kotlin.math.*
import net.minecraft.client.Minecraft
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
@@ -47,6 +35,18 @@ import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import org.joml.Vector3f
import kotlin.math.abs
import kotlin.math.asin
import kotlin.math.cos
import kotlin.math.hypot
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sign
import kotlin.math.sin
import kotlin.math.sqrt
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
companion object {
@@ -54,8 +54,6 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
override val key = KEY
val globalDolphin: DolphinSettings
get() = CobblemonRideSettings.dolphin
override fun getRidingStyle(settings: DolphinSettings, state: DolphinState): RidingStyle {
return RidingStyle.LIQUID
@@ -119,14 +117,10 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
) {
val stam = state.stamina.get()
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalDolphin.infiniteStamina!!)) {
return
}
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
// proportional to the number of ticks of boost thus making a full boost take x ticks
// in short: "Stamina drains slower at higher values and also replenishes slower"
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalDolphin.staminaExpr!!) * 20.0f
val boostTime = vehicle.runtime.resolveDouble(settings.staminaExpr) * 20.0f
val stamDrainRate = (1.0f / boostTime).toFloat()
val newStam = if (state.boosting.get()) max(0.0f,stam - stamDrainRate)
@@ -155,9 +149,9 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
val controller = (driver as OrientationControllable).orientationController
val runtime = vehicle.runtime
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr ?: globalDolphin.jumpExpr!!) else 1.0
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0) * boostMod
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalDolphin.accelerationExpr!!) * 20.0)) * boostMod
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr) else 1.0
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0) * boostMod
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr) * 20.0)) * boostMod
/*********************************************
* Breaching logic
@@ -195,8 +189,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
val currSpeed = state.speed.get()
val strafeFactor = runtime.resolveDouble(settings.strafeFactor ?: globalDolphin.strafeFactor!!)
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor ?: globalDolphin.reverseDriveFactor!!)
val strafeFactor = runtime.resolveDouble(settings.strafeFactor)
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor)
val f = -driver.xxa * strafeFactor
when {
driver.zza > 0.0 -> state.speed.set(min(topSpeed, currSpeed + accel))
@@ -231,8 +225,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
val currSpeed = vehicle.deltaMovement.length()
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))
@@ -259,7 +253,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
// Calculate correcting roll force.
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
val currSpeed = vehicle.deltaMovement.length()
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalDolphin.timeToRollCorrect!!)
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect)
val maxRollCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0f
else 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
val maxRollForce = maxRollCorrectionRate / 40.0
@@ -380,8 +374,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
controller.rotateRoll(controller.roll * -1.0f)
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!) * boostingHandleDebuff
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * boostingHandleDebuff
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult)
//Smooth out mouse input.
val smoothingSpeed = 4.0
@@ -424,9 +418,9 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) / 20.0
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!) * boostingHandleDebuff
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult) * boostingHandleDebuff
//Smooth out mouse input.
val smoothingSpeed = 4.0
@@ -445,7 +439,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
// - Do not pitch at all when at or below hover speed
// - pitch faster at higher speeds
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalDolphin.horizontalPitchExpr!!)
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr)
val localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
// localPitch *= if (currSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
// else 0.0
@@ -566,66 +560,62 @@ class DolphinSettings : RidingBehaviourSettings {
override val key = DolphinBehaviour.KEY
override val stats = mutableMapOf<RidingStat, IntRange>()
var infiniteStamina: Expression? = null
var canJump = "true".asExpression()
private set
var canJump: Expression? = null
var reverseDriveFactor = "0.25".asExpression()
private set
var reverseDriveFactor: Expression? = null
var strafeFactor = "0.2".asExpression()
private set
var strafeFactor: Expression? = null
private set
var timeToRollCorrect: Expression? = null
var timeToRollCorrect: Expression = "0.5".asExpression()
private set
// Boost influence when boosting
var jumpExpr: Expression? = null
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LIQUID', 2.2, 1.2)".asExpression()
// Yaw rate in degrees per second
var handlingExpr: Expression? = null
var horizontalPitchExpr: Expression? = null
var mouseInputMult: Expression? = null
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 100.0, 45.0)".asExpression()
var horizontalPitchExpr: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 0.3, 0.2)".asExpression()
var mouseInputMult: Expression = "q.get_ride_stats('SKILL', 'LIQUID', 1.2, 0.8)".asExpression()
// Top speed in Bl/s
var speedExpr: Expression? = null
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LIQUID', 15.0, 5.0)".asExpression()
// Seconds to get to top speed
var accelerationExpr: Expression? = null
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'LIQUID', 2.0, 5.0)".asExpression()
// Seconds of boost
var staminaExpr: Expression? = null
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LIQUID', 7.0, 3.0)".asExpression()
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
buffer.writeRidingStats(stats)
rideSounds.encode(buffer)
buffer.writeNullableExpression(infiniteStamina)
buffer.writeNullableExpression(canJump)
buffer.writeNullableExpression(reverseDriveFactor)
buffer.writeNullableExpression(strafeFactor)
buffer.writeNullableExpression(jumpExpr)
buffer.writeNullableExpression(handlingExpr)
buffer.writeNullableExpression(horizontalPitchExpr)
buffer.writeNullableExpression(mouseInputMult)
buffer.writeNullableExpression(speedExpr)
buffer.writeNullableExpression(accelerationExpr)
buffer.writeNullableExpression(staminaExpr)
buffer.writeExpression(canJump)
buffer.writeExpression(reverseDriveFactor)
buffer.writeExpression(strafeFactor)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(horizontalPitchExpr)
buffer.writeExpression(mouseInputMult)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
stats.putAll(buffer.readRidingStats())
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteStamina = buffer.readNullableExpression()
canJump = buffer.readNullableExpression()
reverseDriveFactor = buffer.readNullableExpression()
strafeFactor = buffer.readNullableExpression()
jumpExpr = buffer.readNullableExpression()
handlingExpr = buffer.readNullableExpression()
horizontalPitchExpr = buffer.readNullableExpression()
mouseInputMult = buffer.readNullableExpression()
speedExpr = buffer.readNullableExpression()
accelerationExpr = buffer.readNullableExpression()
staminaExpr = buffer.readNullableExpression()
canJump = buffer.readExpression()
reverseDriveFactor = buffer.readExpression()
strafeFactor = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
horizontalPitchExpr = buffer.readExpression()
mouseInputMult = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
}
}
@@ -42,10 +42,9 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
var fade: Float = 0.0f
var shouldMuffle: Boolean = soundSettings.muffleEnabled
var rideAttenuation: RideAttenuationModel = soundSettings.attenuationModel
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is no muffle and 0.0 is full hFgain muting
var isPassenger: Boolean = ride.passengers.any { passenger ->
Minecraft.getInstance().player?.id == passenger.id
}
var muffleAmount: Float = 1.0f // Currently slightly misleading. 1.0 is on muffle and 0.0 is full hFgain muting
// TODO: revisit below flag once multi passenger support is enabled
var isPassenger: Boolean = Minecraft.getInstance().player?.id == ride.controllingPassenger?.id
init {
this.looping = true
@@ -21,11 +21,11 @@ import net.minecraft.network.chat.Component
enum class RidingStat(
val flavour: Flavour
) {
SPEED(Flavour.SWEET),
ACCELERATION(Flavour.SPICY),
SKILL(Flavour.DRY),
SPEED(Flavour.SWEET),
STAMINA(Flavour.SOUR),
JUMP(Flavour.BITTER);
JUMP(Flavour.BITTER),
STAMINA(Flavour.SOUR);
companion object {
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
@@ -34,7 +34,7 @@ object CobblemonSpawnPools {
SpawnablePositionTypePrecalculation,
BucketPrecalculation,
BiomePrecalculation
), reloadable = true
)
)
}
@@ -24,12 +24,14 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
import com.cobblemon.mod.common.util.math.pow
import com.cobblemon.mod.common.util.toBlockPos
import com.cobblemon.mod.common.util.toVec3d
import kotlin.math.ceil
import kotlin.math.sqrt
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.Holder
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.ai.village.poi.PoiManager
import net.minecraft.world.entity.ai.village.poi.PoiType
import net.minecraft.world.level.block.Blocks
@@ -112,6 +114,73 @@ object LurePokeSnackDetector : SpawningInfluenceDetector {
}
}
fun attemptSafeMove2(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity, ) {
val inWater = entity.isInWater
// Random offset in a cardinal direction (north, south, east, west)
val randomDirection = listOf(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST).random()
val initialOffsetPos = startPos.relative(randomDirection)
// Variables to track the best (closest) safe position
var safePos: BlockPos? = null
var bestDistanceSq = Double.MAX_VALUE
val maxRadius = 10
// Search within a sphere around the initial offset position
for (dx in -maxRadius..maxRadius) {
for (dy in -maxRadius..maxRadius) {
for (dz in -maxRadius..maxRadius) {
val candidatePos = initialOffsetPos.offset(dx, dy, dz)
val distanceSq = startPos.distSqr(candidatePos)
// Check if within sphere radius
if (dx*dx + dy*dy + dz*dz <= maxRadius*maxRadius) {
// Check collision at candidate position
val currentBox = entity.boundingBox.move(
candidatePos.x - entity.x,
candidatePos.y - entity.y,
candidatePos.z - entity.z
)
if (level.noCollision(entity, currentBox)) {
val blockBelowPos = candidatePos.below()
val blockState = level.getBlockState(blockBelowPos)
val isWater = blockState.block == Blocks.WATER
if (inWater) {
if (isWater) {
if (distanceSq < bestDistanceSq) {
bestDistanceSq = distanceSq
safePos = candidatePos
}
}
} else {
if (!blockState.isAir) {
if (distanceSq < bestDistanceSq) {
bestDistanceSq = distanceSq
safePos = candidatePos
}
}
}
}
}
}
}
}
if (safePos != null) {
entity.moveTo(safePos.toVec3d())
PokeSnackBlockParticlesPacket(startPos, safePos).sendToPlayersAround(
startPos.x.toDouble(),
startPos.y.toDouble(),
startPos.z.toDouble(),
64.0,
level.dimension()
)
}
}
fun attemptSafeMove(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity) {
val inWater = entity.isInWater
val maxDistance = 10 // maximum search radius
@@ -186,4 +255,4 @@ object LurePokeSnackDetector : SpawningInfluenceDetector {
}
override fun detectFromBlock(level: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<SpawningZoneInfluence>()
}
}
@@ -161,10 +161,6 @@ open class PlayerPartyStore(
if (pokemon.interactionCooldowns.any()) {
pokemon.tickInteractionCooldown()
}
if (pokemon.entity?.passengers?.isNotEmpty() != true) {
pokemon.rideStamina += 0.1F // Recover all stamina in 10 seconds, as long as no one's on it
}
}
// Friendship
// ToDo expand this down the line just a very basic implementation for the first releases
@@ -100,7 +100,7 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
if (state.getValue(HALF) == DoubleBlockHalf.UPPER || !canGrow(world, pos, state)) return
if (hasSufficientLight(world, pos) && random.nextInt(16) == 0) {
if (hasSufficientLight(world, pos) && random.nextInt(7) == 0) {
grow(world, pos, state, 1)
}
}
@@ -1,19 +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.campfirepot
enum class CampfirePotColor(val suffix: String) {
RED("red"),
BLUE("blue"),
GREEN("green"),
PINK("pink"),
WHITE("white"),
YELLOW("yellow"),
BLACK("black");
}
@@ -56,7 +56,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
CampfireBlockEntity.Companion.CRAFTING_GRID_WIDTH
)
this.resultContainer = ResultContainer()
this.containerData = SimpleContainerData(4)
this.containerData = SimpleContainerData(3)
this.addDataSlots(containerData)
this.player = playerInventory.player
this.level = playerInventory.player.level()
@@ -280,4 +280,4 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
value: Int
) {
}
}
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonRecipeTypes
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.cooking.getColourMixFromSeasonings
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
import com.cobblemon.mod.common.item.components.PotComponent
@@ -58,8 +57,8 @@ import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent
import net.minecraft.world.phys.Vec3
import org.joml.Vector4f
import com.cobblemon.mod.common.item.CampfirePotItem
import java.util.*
import kotlin.math.sign
class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlockEntity(
CobblemonBlockEntities.CAMPFIRE,
@@ -85,7 +84,6 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
const val COOKING_PROGRESS_INDEX = 0
const val COOKING_PROGRESS_TOTAL_TIME_INDEX = 1
const val IS_LID_OPEN_INDEX = 2
const val COOKING_POT_COLOR_INDEX = 3
const val BASE_BROTH_COLOR = 0xFDFACF
const val BASE_BROTH_BUBBLE_COLOR = 0xFFFEFDE4.toInt()
@@ -244,7 +242,6 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime
IS_LID_OPEN_INDEX -> if (this@CampfireBlockEntity.blockState.getValue(CampfireBlock.LID)) 0 else 1
COOKING_POT_COLOR_INDEX -> (this@CampfireBlockEntity.getPotItem()?.item as? CampfirePotItem)?.color?.ordinal ?: 0
else -> 0
}
}
@@ -254,12 +251,11 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
COOKING_PROGRESS_INDEX -> this@CampfireBlockEntity.cookingProgress = value
COOKING_PROGRESS_TOTAL_TIME_INDEX -> this@CampfireBlockEntity.cookingTotalTime = value
IS_LID_OPEN_INDEX -> this@CampfireBlockEntity.toggleLid(value == 1)
COOKING_POT_COLOR_INDEX -> {}
}
}
override fun getCount(): Int {
return 4
return 3
}
}
@@ -478,12 +474,11 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
}
override fun canPlaceItemThroughFace(index: Int, itemStack: ItemStack, direction: Direction?): Boolean {
return if(direction == Direction.UP && Seasonings.isSeasoning(itemStack)){
SEASONING_SLOTS.contains(index)
} else {
CRAFTING_GRID_SLOTS.contains(index)
return when (direction) {
Direction.UP -> SEASONING_SLOTS.contains(index)
else -> CRAFTING_GRID_SLOTS.contains(index)
}
}
}
}
@@ -29,15 +29,7 @@ import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
import com.cobblemon.mod.common.client.render.DeferredRenderer
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.CampfireBlockEntityRenderer
import com.cobblemon.mod.common.client.render.block.DisplayCaseRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.LecternBlockEntityRenderer
import com.cobblemon.mod.common.client.render.block.PokeSnackBlockEntityRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.color.AprijuiceItemColorProvider
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
@@ -50,9 +42,7 @@ import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererR
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.*
import com.cobblemon.mod.common.client.render.npc.NPCRenderer
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer
@@ -60,25 +50,18 @@ import com.cobblemon.mod.common.client.requests.ClientPlayerActionRequests
import com.cobblemon.mod.common.client.sound.BattleMusicController
import com.cobblemon.mod.common.client.sound.EntitySoundTracker
import com.cobblemon.mod.common.client.storage.ClientStorageManager
import com.cobblemon.mod.common.client.tooltips.AprijuiceTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.CobblemonTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.FishingBaitTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.FishingRodTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.PokePuffTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.RecipeSeasoningAbsorbtionTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.SeasoningTooltipGenerator
import com.cobblemon.mod.common.client.tooltips.TooltipManager
import com.cobblemon.mod.common.client.tooltips.*
import com.cobblemon.mod.common.client.trade.ClientTrade
import com.cobblemon.mod.common.debug.RidingDebugRenderer
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.food.SinisterTeaItem
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isLookingAt
import io.github.flemmli97.debugutils.api.RegisterDebugRenderers
import net.minecraft.client.Minecraft
import net.minecraft.client.color.block.BlockColor
import net.minecraft.client.color.item.ItemColor
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.MenuScreens
import net.minecraft.client.gui.screens.Screen
@@ -121,7 +104,7 @@ object CobblemonClient {
teamData = ClientPlayerTeamData()
clientPokedexData = ClientPokedexManager(mutableMapOf())
storage.onLogin()
// CobblemonDataProvider.canReload = false
CobblemonDataProvider.canReload = false
}
fun onLogout() {
@@ -130,7 +113,7 @@ object CobblemonClient {
battleOverlay.onLogout()
ClientTaskTracker.clear()
checkedStarterScreen = false
// CobblemonDataProvider.canReload = true
CobblemonDataProvider.canReload = true
DeferredRenderer.clearAll()
ClientPlayerIcon.clear()
}
@@ -196,11 +179,6 @@ object CobblemonClient {
}
ClientPlayerIcon.onTick()
}
val debugId = cobblemonResource("riding")
RegisterDebugRenderers.registerCustomDebugRenderer(debugId, RidingDebugRenderer)
RegisterDebugRenderers.registerServerToggle(debugId)
RegisterDebugRenderers.registerClientHandler(debugId) { RidingDebugRenderer.clientHandler(it) }
}
private fun registerTooltipManagers() {
@@ -443,4 +421,5 @@ object CobblemonClient {
)
}
}
}
@@ -1,71 +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
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.client.persisted.ClientPersistedData
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.client.CameraType
import net.minecraft.client.Minecraft
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.Entity
/**
* The purpose here is to keep track of the last camera type that was used for each riding behaviour or just
* the last overall, then change the camera type where necessary when mounting/dismounting or switching
* between different riding behaviours. This is dependent on a config option, but the general idea is we
* want the camera to be whatever the player feels most comfy with without them having to fiddle with it
* more than once.
*/
object MountedCameraTypeHandler {
var unmountedCameraType: CameraType = CameraType.FIRST_PERSON
fun handleMount(passenger: Entity, vehicle: PokemonEntity) {
val mc = Minecraft.getInstance()
val key = vehicle.ridingController?.context?.settings?.key
if (passenger != mc.player || key == null) {
return
}
unmountedCameraType = mc.options.cameraType
val data = ClientPersistedData.ridingPerspectives
if (Cobblemon.config.rememberRidingCamera) {
val lastCameraTypeForKey = data.perspectives[key]
if (lastCameraTypeForKey != null) {
mc.options.cameraType = lastCameraTypeForKey
}
}
}
fun handleDismount(passenger: Entity, vehicle: PokemonEntity) {
val mc = Minecraft.getInstance()
val key = vehicle.ridingController?.context?.settings?.key
if (passenger != mc.player || key == null) {
return
}
val currentCameraType = mc.options.cameraType
if (currentCameraType != unmountedCameraType) {
mc.options.cameraType = unmountedCameraType
}
val data = ClientPersistedData.ridingPerspectives
data.updatePerspective(key, currentCameraType)
}
fun handleTransition(fromKey: ResourceLocation, toKey: ResourceLocation) {
val mc = Minecraft.getInstance()
val currentCameraType = mc.options.cameraType
val data = ClientPersistedData.ridingPerspectives
data.updatePerspective(fromKey, currentCameraType)
if (Cobblemon.config.rememberRidingCamera) {
val lastCameraTypeForKey = data.perspectives[toKey]
if (lastCameraTypeForKey != null) {
mc.options.cameraType = lastCameraTypeForKey
}
}
}
}
@@ -135,11 +135,7 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
// Configure what controls to show for each behaviour
when (currentBehaviourKey) {
BirdBehaviour.KEY -> {}
BoatBehaviour.KEY -> {
showVerticalMouse = false
showHorizontalMouse = false
showSneakKey = false
}
BoatBehaviour.KEY -> {}
BurstBehaviour.KEY -> {}
DolphinBehaviour.KEY -> {}
GliderBehaviour.KEY -> {}
@@ -12,9 +12,7 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.ifServer
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.runOnServer
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.EditBox
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.cookingpot
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
@@ -22,7 +21,6 @@ import net.minecraft.sounds.SoundEvents
class CookButton(
pX: Int, pY: Int,
var selected: Boolean = false,
var color: CampfirePotColor,
onPress: OnPress
): Button(pX, pY, SIZE.toInt(), SIZE.toInt(), Component.literal("Cook"), onPress, DEFAULT_NARRATION) {
@@ -30,11 +28,11 @@ class CookButton(
private const val SIZE = 20F
private val buttonResource = cobblemonResource("textures/gui/campfirepot/button.png")
private val buttonIconResource = cobblemonResource("textures/item/campfire_pots/campfire_pot_red.png")
private val buttonIconOpenResource = cobblemonResource("textures/item/campfire_pots/campfire_pot_red_open.png")
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val closedIcon = cobblemonResource("textures/item/campfire_pots/campfire_pot_${color.suffix}.png")
val openIcon = cobblemonResource("textures/item/campfire_pots/campfire_pot_${color.suffix}_open.png")
blitk(
matrixStack = context.pose(),
texture = buttonResource,
@@ -48,7 +46,7 @@ class CookButton(
blitk(
matrixStack = context.pose(),
texture = if (selected) closedIcon else openIcon,
texture = if (selected) buttonIconResource else buttonIconOpenResource,
x = x + 2,
y = y + 2,
width = 16,
@@ -11,10 +11,8 @@ package com.cobblemon.mod.common.client.gui.cookingpot
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.block.campfirepot.CampfirePotColor
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
import com.cobblemon.mod.common.block.campfirepot.SeasoningSlot
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.COOKING_POT_COLOR_INDEX
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
import com.cobblemon.mod.common.integration.jei.CobblemonJeiPlugin
import com.cobblemon.mod.common.integration.jei.cooking.CampfirePotRecipeCategory
@@ -120,8 +118,7 @@ class CookingPotScreen(
this.titleLabelY = 6
if (::cookButton.isInitialized) removeWidget(cookButton)
val color = CampfirePotColor.entries[menu.containerData.get(COOKING_POT_COLOR_INDEX).coerceIn(0, CampfirePotColor.entries.lastIndex)]
cookButton = CookButton(this.leftPos + 97, topPos + 56, menu.containerData.get(IS_LID_OPEN_INDEX) == 0, color) {
cookButton = CookButton(this.leftPos + 97, topPos + 56, menu.containerData.get(IS_LID_OPEN_INDEX) == 0) {
val isLidClosed = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
sendToServer(ToggleCookingPotLidPacket(isLidClosed))
}
@@ -164,7 +161,6 @@ class CookingPotScreen(
mouseY: Int,
partialTicks: Float
) {
cookButton.color = CampfirePotColor.entries[menu.containerData.get(COOKING_POT_COLOR_INDEX).coerceIn(0, CampfirePotColor.entries.lastIndex)]
cookButton.selected = menu.containerData.get(IS_LID_OPEN_INDEX) == 0
cookButton.setPosition(this.leftPos + 97, topPos + 56)
@@ -1,179 +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.gui.debug.riding
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingSettingsPacket
import com.cobblemon.mod.common.net.messages.server.debug.ServerboundUpdateRidingStatsPacket
import com.cobblemon.mod.common.net.serverhandling.debug.ServerboundUpdateRidingSettingsHandler
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.resolve
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.EditBox
import net.minecraft.client.gui.screens.Screen
import java.awt.Color
/**
* Ride Settings Editor to change the underlying expressions and
* stat mins and maxes of your ride on the fly (pun intended)
*
* Description
*
* @author Jackson
* @since September 13, 2025
*/
class RideSettingsEditorGUI(val parentScreen: Screen, val vehicle: PokemonEntity, val rideStyle: RidingStyle, val rideSettings: RidingBehaviourSettings) : Screen(lang("ui.debug.riding_settings")), CobblemonRenderable {
private val settingWidgets = mutableListOf<Pair<String, EditBox>>()
private val originalValues = mutableMapOf<String, String>() // Used to keep track of saved state of editboxes
override fun init() {
super.init()
clearWidgets()
// Take note of the current saved values in the settings
if (originalValues.isEmpty()) {
val expressionVariables = getExpressionVariables(rideSettings)
for ((name, value) in expressionVariables) {
originalValues[name] = value
}
}
addRenderableWidget(
Button.builder("Back".text()) {
this.minecraft?.setScreen(parentScreen)
}.bounds(10, 10, 40, 20).build()
)
addRenderableWidget(
Button.builder("Save".text()) { button ->
saveRideSettings()
}.bounds(300 + 110, 10, 40, 20).build()
)
createExpressionEditBoxes()
}
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
// Make the background blank and transparent?
guiGraphics.fill(0, 0, width, height, Color(0, 0, 0, 100).rgb)
super.render(guiGraphics, mouseX, mouseY, partialTick)
for ((name, editBox) in settingWidgets) {
// Position the text next to the EditBox
guiGraphics.drawString(font, name, editBox.x - 105, editBox.y + 5, 0xFFFFFF)
}
}
private fun createExpressionEditBoxes() {
val expressionVariables = getExpressionVariables(rideSettings)
var currentY = 40
for (variable in expressionVariables) {
val (name, value) = variable
val editBox = EditBox(this.minecraft!!.font, 110, currentY, 300, 20, name.text())
editBox.setMaxLength(256)
editBox.value = value
// Dry run expression changes and if valid then turn white and if
// invalid then turn red. If it remains the same it is green
editBox.setResponder { newExpression ->
when {
!validateExpression(newExpression) -> editBox.setTextColor(0xFF5555)
newExpression == originalValues[name] -> editBox.setTextColor(0x55FF55)
else -> editBox.setTextColor(0xFFFFFF)
}
}
// Set all Boxes to green as they are currently saved
editBox.setTextColor(0x55FF55)
// Add widget
addRenderableWidget(editBox)
// Add widget to list
settingWidgets.add(name to editBox)
currentY += 25
}
}
private fun saveRideSettings() {
for ((name, editBox) in settingWidgets) {
// If expression is valid then save and turn green
if (validateExpression(editBox.value)) {
ServerboundUpdateRidingSettingsHandler.modifyRideSettingsExpression(
vehicle,
rideStyle,
name,
editBox.value
)
// Update the running list of last saved values too
originalValues[name] = editBox.value
// Update server with
CobblemonNetwork.sendToServer(
ServerboundUpdateRidingSettingsPacket(
vehicle.id,
rideStyle,
name,
editBox.value
)
)
editBox.setTextColor(0x55FF55)
}
}
}
private fun validateExpression(expression: String): Boolean {
return try {
// Attempt to parse as expression
val parsedExpression = expression.asExpression()
// Dry run to ensure its not borked.
vehicle.runtime.resolve(parsedExpression)
true
} catch (e: Exception) {
false
}
}
private fun getExpressionVariables(settings: RidingBehaviourSettings): List<Pair<String, String>> {
val clazz = settings.javaClass
val fields = clazz.declaredFields
// Iterate through all expression fields and return the list of those that
// aer expressions
return fields.mapNotNull { field ->
if (field.type == Expression::class.java) {
field.isAccessible = true // Make private fields accessible
val name = field.name
// Get the value, cast it, and call .getString() on it
val value = (field.get(settings) as Expression).getString()
// Create the pair and return it for the list
name to value
} else {
// If it's not an Expression (they all honestly should be if being used by the
// behaviour in question) then ignore
null
}
}
}
}
@@ -33,28 +33,26 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
var ridingStyle: RidingStyle = RidingStyle.LAND
private lateinit var changeRidingStyle: Button
val changeRidingStyle: Button
private lateinit var speedSlider: SettingsSlider
private lateinit var accelerationSlider: SettingsSlider
private lateinit var skillSlider: SettingsSlider
private lateinit var jumpSlider: SettingsSlider
private lateinit var staminaSlider: SettingsSlider
val speedSlider: SettingsSlider
val accelerationSlider: SettingsSlider
val skillSlider: SettingsSlider
val jumpSlider: SettingsSlider
val staminaSlider: SettingsSlider
private lateinit var minSpeedInput: RidingStatInputWidget
private lateinit var maxSpeedInput: RidingStatInputWidget
private lateinit var minAccelerationInput: RidingStatInputWidget
private lateinit var maxAccelerationInput: RidingStatInputWidget
private lateinit var minSkillInput: RidingStatInputWidget
private lateinit var maxSkillInput: RidingStatInputWidget
private lateinit var minJumpInput: RidingStatInputWidget
private lateinit var maxJumpInput: RidingStatInputWidget
private lateinit var minStaminaInput: RidingStatInputWidget
private lateinit var maxStaminaInput: RidingStatInputWidget
val minSpeedInput: RidingStatInputWidget
val maxSpeedInput: RidingStatInputWidget
val minAccelerationInput: RidingStatInputWidget
val maxAccelerationInput: RidingStatInputWidget
val minSkillInput: RidingStatInputWidget
val maxSkillInput: RidingStatInputWidget
val minJumpInput: RidingStatInputWidget
val maxJumpInput: RidingStatInputWidget
val minStaminaInput: RidingStatInputWidget
val maxStaminaInput: RidingStatInputWidget
public override fun init() {
super.init()
clearWidgets()
init {
ridingStyle = vehicle.ifRidingAvailableSupply(RidingStyle.LAND) { behaviour, settings, state ->
behaviour.getRidingStyle(settings, state)
}
@@ -96,15 +94,6 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
}.bounds(getScaledWidth() - 210, 220, 200, 20).build()
)
addRenderableWidget(
Button.builder("Edit Ride Settings".text()) { button ->
// Grab the ride settings for the currently being configured style
val rideSettings = vehicle.ridingController?.behaviours?.get(ridingStyle) ?: return@builder
// Open up GUI for editing the given settings
this.minecraft?.setScreen(RideSettingsEditorGUI(this, vehicle, ridingStyle, rideSettings))
}.bounds(10, 240, 200, 20).build()
)
refresh()
}
@@ -266,7 +266,6 @@ class PCGUI(
tooltipKey = "ui.sort.${typeName}",
label = "sort_${typeName}"
) {
storageWidget.resetSelected()
SortPCBoxPacket(pc.uuid, storageWidget.box, sortType, hasShiftDown()).sendToServer()
}.also {
it.visible = displayOptions
@@ -720,7 +719,7 @@ class PCGUI(
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
}
if (storageWidget.isHovered && mouseX < (storageWidget.x + StorageWidget.SCREEN_WIDTH)) this.storageWidget.box -= verticalAmount.toInt()
if (::wallpaperWidget.isInitialized && wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
if (wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
val infoBoxX = ((width - BASE_WIDTH) / 2) + 9
val infoBoxY = ((height - BASE_HEIGHT) / 2) + 128
@@ -492,7 +492,7 @@ class StorageWidget(
}
}
fun resetSelected() {
private fun resetSelected() {
selectedPosition = null
grabbedSlot = null
pcGui.setPreviewPokemon(null)
@@ -82,6 +82,9 @@ class PokedexGUI private constructor(
private val arrowUpIcon = cobblemonResource("textures/gui/pokedex/arrow_up.png")
private val arrowDownIcon = cobblemonResource("textures/gui/pokedex/arrow_down.png")
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
private val tabSelectArrow = cobblemonResource("textures/gui/pokedex/select_arrow.png")
private val tabIcons = arrayOf(
cobblemonResource("textures/gui/pokedex/tab_info.png"),
@@ -322,15 +325,17 @@ class PokedexGUI private constructor(
// Search type tooltip
if (searchByTypeButton.isButtonHovered(mouseX, mouseY)) {
matrices.pushPose()
matrices.translate(0.0, 0.0, 1000.0)
val searchTypeText = lang("ui.pokedex.search.search_by", lang("ui.pokedex.search.type.${selectedSearchByType.name.lowercase()}")).bold()
renderTooltip(
context,
searchTypeText,
mouseX,
mouseY,
delta,
-14
)
val searchTypeTextWidth = Minecraft.getInstance().font.width(searchTypeText.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = searchTypeTextWidth + 6
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY - 16, width = 1, height = 11)
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY - 16, width = tooltipWidth, height = 11)
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY - 16, width = 1, height = 11)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = searchTypeText, x = mouseX, y = mouseY - 15, shadow = true, centered = true)
matrices.popPose()
}
}
@@ -476,8 +481,6 @@ class PokedexGUI private constructor(
fun displaytabInfoElement(tabIndex: Int, update: Boolean = true) {
val showActiveTab = selectedEntry?.let { selectedForm in CobblemonClient.clientPokedexData.getCaughtForms(it) } == true
var statSubIndex = 0
if (tabButtons.isNotEmpty() && tabButtons.size > tabIndex) {
tabButtons.forEachIndexed { index, tab -> tab.isWidgetActive = showActiveTab && index == tabIndex
}
@@ -488,14 +491,6 @@ class PokedexGUI private constructor(
removeWidget((tabInfoElement as AbilitiesWidget).rightButton)
}
if (tabInfoIndex == TAB_STATS && tabInfoElement is StatsWidget) {
statSubIndex = (tabInfoElement as StatsWidget).selectedStatTypeIndex
removeWidget((tabInfoElement as StatsWidget).leftButton)
removeWidget((tabInfoElement as StatsWidget).rightButton)
removeWidget((tabInfoElement as StatsWidget).leftSubButton)
removeWidget((tabInfoElement as StatsWidget).rightSubButton)
}
tabInfoIndex = tabIndex
if (::tabInfoElement.isInitialized) removeWidget(tabInfoElement)
@@ -513,9 +508,7 @@ class PokedexGUI private constructor(
tabInfoElement = SizeWidget(x + 180, y + 135)
}
TAB_STATS -> {
tabInfoElement = StatsWidget(x + 180, y + 135).also {
it.selectedStatTypeIndex = statSubIndex
}
tabInfoElement = StatsWidget(x + 180, y + 135)
}
TAB_DROPS -> {
tabInfoElement = DropsScrollingWidget(x + 189, y + 135)
@@ -562,17 +555,6 @@ class PokedexGUI private constructor(
}
TAB_STATS -> {
(tabInfoElement as StatsWidget).baseStats = form.baseStats
(tabInfoElement as StatsWidget).rideProperties = form.riding
form.riding.behaviours?.let {
addRenderableWidget((tabInfoElement as StatsWidget).leftButton)
addRenderableWidget((tabInfoElement as StatsWidget).rightButton)
if (it.size > 1) {
addRenderableWidget((tabInfoElement as StatsWidget).leftSubButton)
addRenderableWidget((tabInfoElement as StatsWidget).rightSubButton)
}
}
}
TAB_DROPS -> {
(tabInfoElement as DropsScrollingWidget).dropTable = form.drops
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.MutableComponent
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
fun renderTooltip(context: GuiGraphics, text: MutableComponent, posX: Int, posY: Int, delta: Float, offsetY: Int = 0) {
val poseStack = context.pose()
val textWidth = Minecraft.getInstance().font.width(text.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = textWidth + 6
val tooltipHeight = 11
val tooltipTop = posY + offsetY
poseStack.pushPose()
poseStack.translate(0.0, 0.0, 1000.0)
Minecraft.getInstance().let {
it.mainRenderTarget.bindWrite(false)
context.enableScissor(
posX - (tooltipWidth / 2),
tooltipTop + 1,
(posX - (tooltipWidth / 2)) + tooltipWidth,
tooltipTop + 10
)
it.gameRenderer.processBlurEffect(delta)
context.disableScissor()
it.mainRenderTarget.bindWrite(true)
}
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX - (tooltipWidth / 2) - 1, y = tooltipTop, width = 1, height = tooltipHeight)
blitk(matrixStack = poseStack, texture = tooltipBackground, x = posX - (tooltipWidth / 2), y = tooltipTop, width = tooltipWidth, height = tooltipHeight)
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX + (tooltipWidth / 2), y = tooltipTop, width = 1, height = tooltipHeight)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = text, x = posX, y = tooltipTop + 1, shadow = true, centered = true)
poseStack.popPose()
}
@@ -31,7 +31,6 @@ class ScaledButton(
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
var isWidgetActive = false
var isVisible = true
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double) = false
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
@@ -39,7 +38,8 @@ class ScaledButton(
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val matrices = context.pose()
if (isVisible && resource != null) {
if (resource != null) {
blitk(
matrixStack = matrices,
texture = resource,
@@ -55,14 +55,14 @@ class ScaledButton(
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (isVisible && active && isButtonHovered(mouseX, mouseY)) {
if (active && isButtonHovered(mouseX, mouseY)) {
super.mouseClicked(mouseX, mouseY, button)
}
return false
}
override fun playDownSound(soundManager: SoundManager) {
if (isVisible && active && !this.silent) {
if (active && !this.silent) {
soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.POKEDEX_CLICK_SHORT, 1.0F))
}
}
@@ -71,4 +71,4 @@ class ScaledButton(
return mouseX.toFloat() in (buttonX..(buttonX + (buttonWidth.toFloat() * scale)))
&& mouseY.toFloat() in (buttonY..(buttonY + (buttonHeight.toFloat() * scale)))
}
}
}
@@ -48,11 +48,12 @@ class AbilitiesWidget(x: Int, y: Int): InfoTextScrollWidget(pX = x, pY = y) {
init { setAbility() }
private fun switchAbility(nextIndex: Boolean) {
val size = abilitiesList.size
selectedAbilitiesIndex = if (nextIndex) {
(selectedAbilitiesIndex + 1) % size
if (nextIndex) {
if (selectedAbilitiesIndex < abilitiesList.lastIndex) selectedAbilitiesIndex++
else selectedAbilitiesIndex = 0
} else {
(selectedAbilitiesIndex - 1 + size) % size
if (selectedAbilitiesIndex > 0) selectedAbilitiesIndex--
else selectedAbilitiesIndex = abilitiesList.lastIndex
}
setAbility()
}
@@ -33,7 +33,6 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_POKE_BALL_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
@@ -95,6 +94,9 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
private val buttonGenderFemale = cobblemonResource("textures/gui/pokedex/button_female.png")
private val buttonNone = cobblemonResource("textures/gui/pokedex/button_none.png")
private val buttonShiny = cobblemonResource("textures/gui/pokedex/button_shiny.png")
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
}
var currentEntry : PokedexEntry? = null
@@ -438,14 +440,13 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
// Tooltip
if (it.isVisible() && it.getWidget().isButtonHovered(mouseX, mouseY)) {
val variationText = it.variation.displayName.asTranslated().bold()
renderTooltip(
context,
variationText,
mouseX,
mouseY,
delta,
10
)
val variationTextWidth = Minecraft.getInstance().font.width(variationText.font(CobblemonResources.DEFAULT_LARGE))
val tooltipWidth = variationTextWidth + 6
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX - (tooltipWidth / 2) - 1, y = mouseY + 8, width = 1, height = 11)
blitk(matrixStack = matrices, texture = tooltipBackground, x = mouseX - (tooltipWidth / 2), y = mouseY + 8, width = tooltipWidth, height = 11)
blitk(matrixStack = matrices, texture = tooltipEdge, x = mouseX + (tooltipWidth / 2), y = mouseY + 8, width = 1, height = 11)
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = variationText, x = mouseX, y = mouseY + 9, shadow = true, centered = true)
}
}
@@ -11,24 +11,18 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_DESCRIPTION_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.util.FastColor
class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
pX,
@@ -39,18 +33,6 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
) {
companion object {
const val STAT_TYPE_BASE = 0
const val STAT_TYPE_RIDE = 1
const val MAX_BAR_WIDTH = 75
private val statTypes = listOf(STAT_TYPE_BASE, STAT_TYPE_RIDE)
private val divider = cobblemonResource("textures/gui/pokedex/info_stat_divider.png")
private val arrowLeft = cobblemonResource("textures/gui/pokedex/info_arrow_left.png")
private val arrowRight = cobblemonResource("textures/gui/pokedex/info_arrow_right.png")
private val subArrowLeft = cobblemonResource("textures/gui/pokedex/info_sub_arrow_left.png")
private val subArrowRight = cobblemonResource("textures/gui/pokedex/info_sub_arrow_right.png")
private val statLabels = arrayOf(
lang("ui.stats.hp"),
lang("ui.stats.atk"),
@@ -70,92 +52,13 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
)
}
val leftButton: ScaledButton = ScaledButton(
pX + 2.5F,
pY - 8F,
7,
10,
arrowLeft,
clickAction = { switchStatType(false) }
)
val rightButton: ScaledButton = ScaledButton(
pX + 133F,
pY - 8F,
7,
10,
arrowRight,
clickAction = { switchStatType(true) }
)
val leftSubButton: ScaledButton = ScaledButton(
pX + 2.5F,
pY + 18.5F,
7,
10,
subArrowLeft,
clickAction = { switchRideBehaviour(false) }
)
val rightSubButton: ScaledButton = ScaledButton(
pX + 133F,
pY + 18.5F,
7,
10,
subArrowRight,
clickAction = { switchRideBehaviour(true) }
)
var selectedRideBehavioursIndex: Int = 0
var selectedStatTypeIndex: Int = STAT_TYPE_BASE
set(value) {
field = value
val isRideStatVisible = value == STAT_TYPE_RIDE
leftSubButton.visible = isRideStatVisible
rightSubButton.visible = isRideStatVisible
}
var baseStats: Map<Stat, Int>? = null
var rideProperties: RidingProperties? = null
private fun switchStatType(nextIndex: Boolean) {
val size = statTypes.size
selectedStatTypeIndex = if (nextIndex) {
(selectedStatTypeIndex + 1) % size
} else {
(selectedStatTypeIndex - 1 + size) % size
}
val showSubArrowButtons = selectedStatTypeIndex == STAT_TYPE_RIDE
leftSubButton.visible = showSubArrowButtons
rightSubButton.visible = showSubArrowButtons
}
private fun switchRideBehaviour(nextIndex: Boolean) {
val size = rideProperties?.behaviours?.size ?: 0
selectedRideBehavioursIndex = if (nextIndex) {
(selectedRideBehavioursIndex + 1) % size
} else {
(selectedRideBehavioursIndex - 1 + size) % size
}
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
when (selectedStatTypeIndex) {
STAT_TYPE_BASE -> {
renderBaseStats(context)
}
STAT_TYPE_RIDE -> {
renderRideStats(context, mouseX, mouseY, delta)
}
}
}
private fun renderBaseStats(context: GuiGraphics) {
if (baseStats == null) return
val matrices = context.pose()
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
@@ -165,9 +68,18 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
shadow = true
)
val matrices = context.pose()
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 4, height = 33.5, width = 1)
// Divider
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = pX + 50.5,
y = pY + 4,
height = 33.5,
width = 1,
red = 0.71F,
green = 0.95F,
blue = 0.95F
)
for (i in statLabels.indices) {
drawScaledText(
@@ -195,10 +107,10 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = pX + 55,
x = pX + 54.5,
y = pY + 4 + (6 * i),
height = 3.5F,
width = (statValue / 255F) * MAX_BAR_WIDTH,
width = (statValue / 255F) * 77,
red = red,
green = green,
blue = blue
@@ -207,119 +119,6 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
}
}
private fun renderRideStats(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
rideProperties?.behaviours?.let { behaviours ->
val selectedBehaviour = behaviours.entries.toList()[selectedRideBehavioursIndex]
val behaviourStats = selectedBehaviour.value.stats
val matrices = context.pose()
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride").bold(),
x = pX + 9,
y = pY - 10,
shadow = true
)
rideProperties?.seats?.size?.let { seats ->
drawScaledTextJustifiedRight(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = Component.translatable("cobblemon.ui.pokedex.info.stats_ride.seats", seats).bold(),
x = pX + 130,
y = pY - 10,
shadow = true
)
}
blitk(matrixStack = matrices, texture = divider, x = pX + 51, y = pY + 10, height = 27.5, width = 1)
blitk(matrixStack = matrices, texture = divider, x = pX + 9, y = pY + 2, height = 6, width = 121)
drawScaledText(
context = context,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
x = pX + 69.5,
y = pY + 3,
colour = 0x3A96B6,
scale = SCALE,
centered = true
)
RidingStat.entries.forEachIndexed { index, stat ->
drawScaledText(
context = context,
text = lang("ui.stats.ride.${stat.name.lowercase()}").bold(),
x = pX + 9,
y = pY + 10 + (6 * index),
colour = 0x606B6E,
scale = SCALE
)
behaviourStats[stat]?.let { range ->
val startPosY = pY + 10 + (6F * index)
val red = FastColor.ARGB32.red(stat.flavour.colour) / 255F
val green = FastColor.ARGB32.green(stat.flavour.colour) / 255F
val blue = FastColor.ARGB32.blue(stat.flavour.colour) / 255F
val barStart = pX + 55F
val baseStatWidth = (range.start / 100F) * MAX_BAR_WIDTH
val boostedStatWidth = ((range.endInclusive / 100F) * MAX_BAR_WIDTH) - baseStatWidth
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = barStart,
y = startPosY,
height = 3.5F,
width = baseStatWidth,
red = red,
green = green,
blue = blue
)
val isBoostedStatHovered = mouseX.toFloat() in ((barStart + baseStatWidth)..(barStart + baseStatWidth + boostedStatWidth)) && mouseY.toFloat() in (startPosY..(startPosY + 3.5F))
drawScaledTextJustifiedRight(
context = context,
text = if (isBoostedStatHovered) range.endInclusive.toString().text().bold() else range.start.toString().text(),
x = pX + 48,
y = startPosY,
colour = 0x3A96B6,
scale = SCALE
)
// Boostable range
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = barStart + baseStatWidth,
y = startPosY,
height = 3.5F,
width = boostedStatWidth,
red = red,
green = green,
blue = blue,
alpha = if (isBoostedStatHovered) 1F else 0.5F
)
if (isBoostedStatHovered) renderTooltip(
context,
lang("ui.stats.ride.boost_maximum").bold(),
mouseX,
mouseY,
delta,
-16
)
}
}
} ?: run {
// If ride properties null, reset index to base stats
selectedStatTypeIndex = STAT_TYPE_BASE
}
}
private fun getStatValueRGB(value: Int): Triple<Float, Float, Float> {
when {
value >= 150 -> return Triple(0.647F, 0.929F, 0.647F)
@@ -329,4 +128,4 @@ class StatsWidget(val pX: Int, val pY: Int) : SoundlessWidget(
}
return Triple(0.964F, 0.64F, 0.502F)
}
}
}
@@ -53,7 +53,7 @@ class SummaryButton(
override fun defaultButtonNarrationText(builder: NarrationElementOutput) {
}
public override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
if (!this.renderRequirement.invoke(this)) {
return
}
@@ -28,91 +28,83 @@ import net.minecraft.world.phys.Vec3
* @author Hiroku
* @since November 13th, 2023
*/
open class BarSummarySpeciesFeatureRenderer(
class BarSummarySpeciesFeatureRenderer(
override val name: String,
val displayName: MutableComponent,
val underlay: ResourceLocation,
val overlay: ResourceLocation,
val pokemon: Pokemon,
val min: Int,
val max: Int,
val value: Int = 0,
val colour: Vec3? = null
val colour: Vec3,
val underlay: ResourceLocation,
val overlay: ResourceLocation,
val pokemon: Pokemon
) : SummarySpeciesFeatureRenderer<IntSpeciesFeature> {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
renderElement(guiGraphics, x, y, pokemon, feature.value)
}
open fun renderElement(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, barValue: Int = value) {
val barRatio = (barValue - min) / (max - min).toFloat()
val barWidth = Mth.ceil(barRatio * 110)
override fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
val value = feature.value
val barRatio = (value - min) / (max - min).toFloat()
val barWidth = Mth.ceil(barRatio * 108)
blitk(
matrixStack = guiGraphics.pose(),
matrixStack = GuiGraphics.pose(),
texture = underlay,
x = x,
y = y,
height = 24,
width = 116
height = 28,
width = 124
)
renderBar(guiGraphics, x, y, barValue, barRatio, barWidth)
val red = colour.x / 255F
val green = colour.y / 255F
val blue = colour.z / 255F
blitk(
matrixStack = guiGraphics.pose(),
texture = overlay,
x = (x + 3) / StatWidget.SCALE,
y = (y + 13) / StatWidget.SCALE,
height = 20,
width = 220,
scale = StatWidget.SCALE
)
// Label
drawScaledText(
context = guiGraphics,
font = CobblemonResources.DEFAULT_LARGE,
text = displayName.bold(),
x = x + 58,
y = y + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = guiGraphics,
text = barValue.toString().text(),
x = x + 9,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
drawScaledText(
context = guiGraphics,
text = "${Mth.floor(barRatio * 100)}%".text(),
x = x + 107,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
}
open fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val red = (colour?.x ?: 255.0) / 255.0
val green = (colour?.y ?: 255.0) / 255.0
val blue = (colour?.z ?: 255.0) / 255.0
blitk(
matrixStack = guiGraphics.pose(),
matrixStack = GuiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
x = x + 8,
y = y + 16,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = GuiGraphics.pose(),
texture = overlay,
x = x / StatWidget.SCALE,
y = (y + 16) / StatWidget.SCALE,
height = 20,
width = 248,
scale = StatWidget.SCALE
)
// Label
drawScaledText(
context = GuiGraphics,
font = CobblemonResources.DEFAULT_LARGE,
text = displayName.bold(),
x = x + 62,
y = y + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = GuiGraphics,
text = value.toString().text(),
x = x + 11,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
drawScaledText(
context = GuiGraphics,
text = "${Mth.floor(barRatio * 100)}%".text(),
x = x + 113,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
}
}
}
@@ -23,10 +23,10 @@ interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
val name: String
/** Draws it at a particular position. */
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon, feature: T)
fun render(GuiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
val feature = pokemon.getFeature<T>(name) ?: return false
render(guiGraphics, x, y, pokemon, feature)
render(GuiGraphics, x, y, pokemon, feature)
return true
}
}
}
@@ -14,16 +14,12 @@ import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.italicise
import com.cobblemon.mod.common.api.text.onHover
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.SummaryButton
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FriendshipFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FullnessFeatureRenderer
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
@@ -43,6 +39,8 @@ import net.minecraft.util.Mth.ceil
import net.minecraft.util.Mth.floor
import net.minecraft.world.phys.Vec2
import org.joml.Vector3f
import kotlin.math.cos
import kotlin.math.sin
class StatWidget(
pX: Int, pY: Int,
@@ -53,129 +51,63 @@ class StatWidget(
companion object {
// Stat tab options
private const val STATS = "stats"
private const val IV = "ivs"
private const val EV = "evs"
private const val RIDE = "ride"
private const val IV = "iv"
private const val EV = "ev"
private const val OTHER = "other"
private const val OTHER_STAT_BARS_PER_PAGE = 4
private const val STAT_TAB_WIDTH_6 = 22
private const val STAT_TAB_WIDTH_5 = 24
private const val MARKER_WIDTH = 8
private val statOptions = listOf(STATS, IV, EV, OTHER)
private const val statTabWidth = 24
private const val WIDTH = 134
private const val HEIGHT = 148
const val SCALE = 0.5F
private const val WHITE = 0x00FFFFFF
private const val GREY = 0x008F8F8F
private const val GREY = 0x00AAAAAA
private const val BLUE = 0x00548BFB
private const val RED = 0x00FB5454
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
private val statsChartPentagonResource = cobblemonResource("textures/gui/summary/summary_stats_chart_pentagon.png")
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
private val statsOtherSidebarsResource = cobblemonResource("textures/gui/summary/summary_stats_other_sidebars.png")
private val statsOtherSidebarArrowLeft = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_left.png")
private val statsOtherSidebarArrowRight = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_right.png")
private val statsOtherBarTemplate = cobblemonResource("textures/gui/summary/summary_stats_other_bar.png")
private val friendshipOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png")
private val fullnessOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png")
private val tabMarkerResource = cobblemonResource("textures/gui/summary/summary_stats_tab_marker.png")
private val statIncreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_increase.png")
private val statDecreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_decrease.png")
private val statLabels: Map<MutableComponent, Stats> = mapOf(
lang("ui.stats.hp").bold() to Stats.HP,
lang("ui.stats.atk").bold() to Stats.ATTACK,
lang("ui.stats.def").bold() to Stats.DEFENCE,
lang("ui.stats.speed").bold() to Stats.SPEED,
lang("ui.stats.sp_def").bold() to Stats.SPECIAL_DEFENCE,
lang("ui.stats.sp_atk").bold() to Stats.SPECIAL_ATTACK
)
private val statsLabel = lang("ui.stats")
private val ivLabel = lang("ui.stats.ivs")
private val evLabel = lang("ui.stats.evs")
private val otherLabel = lang("ui.stats.other")
private val hexagonVerticesOffset = listOf(
Pair(67.0, 10.5), // 12 o'clock
Pair(122.0, 42.5), // 2 o'clock
Pair(122.0, 93.5), // 4 o'clock
Pair(67.0, 124.5), // 6 o'clock
Pair(12.0, 93.5), // 8 o'clock
Pair(12.0, 42.5) // 10 o'clock
)
private val pentagonVerticesOffset = listOf(
Pair(67.0, 10.5), // 12 o'clock
Pair(123.0, 47.5), // 2 o'clock
Pair(103.0, 112.5), // 5 o'clock
Pair(31.0, 112.5), // 7 o'clock
Pair(11.0, 47.5), // 10 o'clock
)
private val hpLabel = lang("ui.stats.hp")
private val spAtkLabel = lang("ui.stats.sp_atk")
private val atkLabel = lang("ui.stats.atk")
private val spDefLabel = lang("ui.stats.sp_def")
private val defLabel = lang("ui.stats.def")
private val speedLabel = lang("ui.stats.speed")
}
private val statOptions = if (pokemon.form.riding.behaviours != null) listOf(STATS, IV, EV, RIDE, OTHER) else listOf(STATS, IV, EV, OTHER)
private var _statTabIndex: Int = tabIndex
var statTabIndex: Int
get() = _statTabIndex.coerceIn(0, statOptions.size - 1)
set(value) {
_statTabIndex = value.coerceIn(0, statOptions.size - 1)
}
private var _rideBehaviourIndex: Int = 0
var rideBehaviourIndex: Int
get() = _rideBehaviourIndex.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
set(value) {
_rideBehaviourIndex = value.coerceIn(0, (pokemon.form.riding.behaviours?.size ?: 1) - 1)
}
val universalFeatures = listOf(
FriendshipFeatureRenderer(pokemon),
FullnessFeatureRenderer(pokemon)
)
var effectiveBattleIVs: Map<Stat, Int> = Stats.PERMANENT.associateWith { stat ->
pokemon.ivs.getEffectiveBattleIV(stat)
}
var statTabIndex = tabIndex
val renderableFeatures = SpeciesFeatures
.getFeaturesFor(pokemon.species)
.filterIsInstance<SynchronizedSpeciesFeatureProvider<*>>()
.mapNotNull { it.getRenderer(pokemon) }
var otherStatsPageIndex: Int = 0
var otherStatLeftButton: SummaryButton? = null
var otherStatRightButton: SummaryButton? = null
init {
if (renderableFeatures.size + universalFeatures.size > OTHER_STAT_BARS_PER_PAGE) {
otherStatLeftButton = SummaryButton(
x - 1F,
y + 67F,
14,
14,
resource = statsOtherSidebarArrowLeft,
scale = SCALE,
clickAction = { switchOtherStatsPage(false) }
).also { addWidget(it) }
otherStatRightButton = SummaryButton(
x + 128F,
y + 67F,
14,
14,
resource = statsOtherSidebarArrowRight,
scale = SCALE,
clickAction = { switchOtherStatsPage(true) }
).also { addWidget(it) }
}
}
private fun switchOtherStatsPage(nextIndex: Boolean) {
val pages = ceil((renderableFeatures.size + universalFeatures.size).toDouble() / OTHER_STAT_BARS_PER_PAGE.toDouble())
otherStatsPageIndex = if (nextIndex) {
(otherStatsPageIndex + 1) % pages
} else {
(otherStatsPageIndex - 1 + pages) % pages
}
}
private fun drawTriangle(colour: Vector3f, v1: Vec2, v2: Vec2, v3: Vec2, opacity: Float = 0.6F) {
private fun drawTriangle(
colour: Vector3f,
v1: Vec2,
v2: Vec2,
v3: Vec2
) {
CobblemonResources.WHITE.let { RenderSystem.setShaderTexture(0, it) }
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, opacity)
RenderSystem.setShaderColor(colour.x, colour.y, colour.z, 0.6F)
val bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.TRIANGLES, DefaultVertexFormat.POSITION)
bufferBuilder.addVertex(v1.x, v1.y, 10F)
bufferBuilder.addVertex(v2.x, v2.y, 10F)
@@ -184,325 +116,396 @@ class StatWidget(
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
}
/**
* Render a 5 or 6 sided polygon
*
* @param ratios list of ratios for how far each vertex is from the center, starting from top vertex going clockwise
* @param colour the colour to render
*/
private fun drawStatPolygon(ratios: List<Float>, colour: Vector3f) {
val sides = ratios.size
if (sides !in 5 .. 6) return
private fun drawStatHexagon(stats: Map<Stat, Int>, colour: Vector3f, maximum: Int) {
val hexLeftX = x + 25.5
val hexTopY = y + 22
val hexAttackY = hexTopY + 24.5
val hexDefenceY = hexAttackY + 47.0
val hexBottomY = hexDefenceY + 24.5
val hexRightX = x + 108.5
val hexCenterX = (hexLeftX + hexRightX) / 2
val hexCenterY = (hexTopY + hexBottomY) / 2
val minTriangleSize = 8F
val minXTriangleLen = sin(Math.toRadians(61.0)).toFloat() * minTriangleSize * 0.95F
val minYTriangleLen = cos(Math.toRadians(60.0)).toFloat() * minTriangleSize
val topVertexY = y + 22F
val centerX = x + 67F
val centerY = topVertexY + if (sides > 5) 48F else 49F
val triangleLongEdge = (hexCenterY - hexTopY - minTriangleSize).toFloat()
val triangleMediumEdge = (triangleLongEdge * sin(Math.toRadians(61.0))).toFloat()
val triangleShortEdge = (triangleLongEdge * cos(Math.toRadians(61.0))).toFloat()
val radius = centerY - topVertexY
val hpRatio = (stats.getOrDefault(Stats.HP, 0).toFloat() / maximum).coerceIn(0F, 1F)
val atkRatio = (stats.getOrDefault(Stats.ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
val defRatio = (stats.getOrDefault(Stats.DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spAtkRatio = (stats.getOrDefault(Stats.SPECIAL_ATTACK, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spDefRatio = (stats.getOrDefault(Stats.SPECIAL_DEFENCE, 0).toFloat() / maximum).coerceIn(0F, 1F)
val spdRatio = (stats.getOrDefault(Stats.SPEED, 0).toFloat() / maximum).coerceIn(0F, 1F)
val startAngleDeg = -90.0
val angleStepDeg = 360.0 / sides
val hpPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat() - minTriangleSize - hpRatio * triangleLongEdge
)
// Generate the angles for each vertex
val anglesDeg = List(sides) { i -> startAngleDeg + i * angleStepDeg }
val anglesRad = anglesDeg.map { Math.toRadians(it) }
val attackPoint = Vec2(
hexCenterX.toFloat() + minXTriangleLen + atkRatio * triangleMediumEdge,
hexCenterY.toFloat() - minYTriangleLen - atkRatio * triangleShortEdge
)
// Calculate maximum points on the circle
val maxPoints = anglesRad.map { angle ->
Vec2(
centerX + radius * Math.cos(angle).toFloat(),
centerY + radius * Math.sin(angle).toFloat()
)
val defencePoint = Vec2(
hexCenterX.toFloat() + minXTriangleLen + defRatio * triangleMediumEdge,
hexCenterY.toFloat() + minYTriangleLen + defRatio * triangleShortEdge
)
val specialAttackPoint = Vec2(
hexCenterX.toFloat() - minXTriangleLen - spAtkRatio * triangleMediumEdge,
hexCenterY.toFloat() - minYTriangleLen - spAtkRatio * triangleShortEdge
)
val specialDefencePoint = Vec2(
hexCenterX.toFloat() - minXTriangleLen - spDefRatio * triangleMediumEdge,
hexCenterY.toFloat() + minYTriangleLen + spDefRatio * triangleShortEdge
)
val speedPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat() + minTriangleSize + spdRatio * triangleLongEdge
)
val centerPoint = Vec2(
hexCenterX.toFloat(),
hexCenterY.toFloat()
)
// 1-o'clock
drawTriangle(colour, hpPoint, centerPoint, attackPoint)
// 3-o'clock
drawTriangle(colour, attackPoint, centerPoint, defencePoint)
// 5-o'clock
drawTriangle(colour, defencePoint, centerPoint, speedPoint)
// 7-o'clock
drawTriangle(colour, speedPoint, centerPoint, specialDefencePoint)
// 9-o'clock
drawTriangle(colour, specialDefencePoint, centerPoint, specialAttackPoint)
// 11-o'clock
drawTriangle(colour, specialAttackPoint, centerPoint, hpPoint)
}
private fun drawFriendship(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, friendship: Int) {
val barRatio = friendship / 255F
val barWidth = ceil(barRatio * 108)
blitk(
matrixStack = matrices,
texture = statsOtherBarTemplate,
x = moduleX,
y = moduleY,
height = 28,
width = 124
)
val red = 1
val green: Number = if (pokemon.friendship >= 160) 0.28 else 0.56
val blue: Number = if (pokemon.friendship >= 160) 0.4 else 0.64
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = matrices,
texture = friendshipOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
// Label
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.stats.friendship").bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = context,
text = friendship.toString().text(),
x = moduleX + 11,
y = moduleY + 6,
scale = SCALE,
centered = true
)
drawScaledText(
context = context,
text = "${floor(barRatio * 100)}%".text(),
x = moduleX + 113,
y = moduleY + 6,
scale = SCALE,
centered = true
)
}
private fun drawFullness(moduleX: Int, moduleY: Int, matrices: PoseStack, context: GuiGraphics, pokemon: Pokemon) {
val currentFullness = pokemon.currentFullness
val maxFullness = pokemon.getMaxFullness()
val barRatio = currentFullness.toFloat() / maxFullness.toFloat()
val barWidth = ceil(barRatio * 108)
blitk(
matrixStack = matrices,
texture = statsOtherBarTemplate,
x = moduleX,
y = moduleY,
height = 28,
width = 124
)
val (red, green, blue) = when {
barRatio <= 0.33 -> Triple(0f, 1f, 0f) // Green (full green)
barRatio <= 0.66 -> Triple(1f, 1f, 0f) // Yellow (red + green)
else -> Triple(1f, 0f, 0f) // Red (full red)
}
// Coerce values as 0.0 will prevent triangles from rendering
val coercedRatios = ratios.map { it.coerceIn(5 / radius, 1F) }
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
// Interpolate vertices based on ratios
val vertices = maxPoints.mapIndexed { index, maxPoint ->
Vec2(
centerX + (maxPoint.x - centerX) * coercedRatios[index],
centerY + (maxPoint.y - centerY) * coercedRatios[index]
)
}
// Draw the overlay
blitk(
matrixStack = matrices,
texture = fullnessOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
val centerPoint = Vec2(centerX, centerY)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = "Fullness".text().bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
// Draw triangles between each vertex and the next clockwise starting from top vertex
RenderSystem.disableDepthTest()
for (i in vertices.indices) {
val nextIndex = (i + 1) % vertices.size
drawTriangle(colour, vertices[i], centerPoint, vertices[nextIndex])
}
RenderSystem.enableDepthTest()
drawScaledText(
context = context,
text = currentFullness.toString().text(),
x = moduleX + 11,
y = moduleY + 6,
scale = SCALE,
centered = true
)
drawScaledText(
context = context,
text = "${floor(barRatio * 100)}%".text(),
x = moduleX + 113,
y = moduleY + 6,
scale = SCALE,
centered = true
)
}
override fun renderWidget(context: GuiGraphics, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val renderOtherStats = statOptions.get(statTabIndex) == OTHER
val renderPentagonStats = statOptions.get(statTabIndex) == RIDE
val renderChart = statOptions.get(statTabIndex) != OTHER
val matrices = context.pose()
// Background
blitk(
matrixStack = matrices,
texture = if (renderOtherStats) statsOtherBaseResource else statsBaseResource,
texture = if (renderChart) statsBaseResource else statsOtherBaseResource,
x= x,
y = y,
width = width,
height = height
)
if (renderOtherStats) {
val barOffsetY = 29
val barPosX = x + 9F
var drawY = y + 15F
val featuresList: MutableList<Any> = renderableFeatures.toMutableList()
featuresList.addAll(0, universalFeatures)
val pageFeatures = featuresList.subList(
otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE,
minOf((otherStatsPageIndex * OTHER_STAT_BARS_PER_PAGE) + OTHER_STAT_BARS_PER_PAGE, featuresList.size)
// Chart
if (renderChart) {
blitk(
matrixStack = matrices,
texture = statsChartResource,
x= (x + 25.5) / SCALE,
y = (y + 22) / SCALE,
width = 166,
height = 192,
scale = SCALE
)
for (feature in pageFeatures) {
if (feature is BarSummarySpeciesFeatureRenderer) {
val rendered = feature.render(
guiGraphics = context,
x = barPosX,
y = drawY,
pokemon = pokemon
)
if (rendered) drawY += barOffsetY
}
}
if (featuresList.size > 4) {
blitk(
matrixStack = matrices,
texture = statsOtherSidebarsResource,
x= x,
y = y + 13,
width = 134,
height = 115
)
otherStatLeftButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
otherStatRightButton?.renderWidget(context, pMouseX, pMouseY, pPartialTicks)
}
} else {
if (renderPentagonStats) {
blitk(
matrixStack = matrices,
texture = statsChartPentagonResource,
x= (x + 20.5) / SCALE,
y = (y + 22) / SCALE,
width = 186,
height = 176,
scale = SCALE
)
pokemon.form.riding.behaviours?.let {
val behaviours = it.entries.toList()
val selectedBehaviour = behaviours[rideBehaviourIndex]
val pentagonColour = when (selectedBehaviour.key) {
RidingStyle.AIR -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(70F/255F, 235F/225F, 195F/255F)
else Vector3f(40F/255F, 205F/255F, 165F/255F)
}
RidingStyle.LIQUID -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(95F/255F, 165F/255F, 1F)
else Vector3f(65F/255F, 135F/255F, 1F)
}
else -> {
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
Vector3f(1F, 195F/255F, 30F/255F)
else Vector3f(1F, 165F/255F, 0F)
}
}
drawStatPolygon(
ratios = RidingStat.entries.map { pokemon.getRideStat(selectedBehaviour.key, it) / 100F },
colour = pentagonColour
)
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.ride_style.${selectedBehaviour.key.name.lowercase()}").bold(),
x = x + (WIDTH / 2),
y = y + 66,
shadow = true,
centered = true
)
// Stat Labels
renderPolygonLabels(
context = context,
labels = RidingStat.entries.toList().map { stat ->
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
"${floor(pokemon.getRideStat(selectedBehaviour.key, stat))}/${selectedBehaviour.value.stats[stat]?.endInclusive}".text()
else lang("ui.stats.ride.${stat.name.lowercase()}").bold()
},
verticesOffset = pentagonVerticesOffset
)
// Stat Values
renderPolygonLabels(
context = context,
labels = RidingStat.entries.toList().map { stat ->
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
"${floor((pokemon.getRideStat(selectedBehaviour.key, stat) / (selectedBehaviour.value.stats[stat]?.endInclusive ?: 1)) * 100)}%".text()
else floor(pokemon.getRideStat(selectedBehaviour.key, stat)).toString().text()
},
verticesOffset = pentagonVerticesOffset,
offsetY = 5.5
)
}
} else {
blitk(
matrixStack = matrices,
texture = statsChartResource,
x= (x + 25.5) / SCALE,
y = (y + 22) / SCALE,
width = 166,
height = 192,
scale = SCALE
)
when (statOptions.get(statTabIndex)) {
STATS -> drawStatPolygon(
listOf(
pokemon.maxHealth,
pokemon.attack,
pokemon.defence,
pokemon.speed,
pokemon.specialDefence,
pokemon.specialAttack
).map { it / 400F },
colour = Vector3f(50F/255F, 215F/255F, 1F)
)
IV -> drawStatPolygon(
statLabels.values.map { pokemon.ivs.getEffectiveBattleIV(it) / 31F },
colour = Vector3f(216F/255, 100F/255, 1F)
)
EV -> drawStatPolygon(
statLabels.values.map { (pokemon.evs[it]?.toFloat() ?: 0F) / 252F },
colour = Vector3f(1F, 1F, 100F/255F)
)
}
val labelsHovered = statLabelsHovered(hexagonVerticesOffset, pMouseX, pMouseY)
val labels = statLabels.map { stat ->
if (labelsHovered) {
when (statOptions.get(statTabIndex)) {
IV -> {
"${
if (pokemon.ivs.isHyperTrained(stat.value)) "${pokemon.ivs[stat.value]} (${pokemon.ivs.hyperTrainedIVs[stat.value]})"
else pokemon.ivs[stat.value].toString()
}/31".text()
}
EV -> "${pokemon.evs.getOrDefault(stat.value)}/252".text()
else -> stat.key
}
} else { stat.key }
}
// Stat Labels
renderPolygonLabels(
context = context,
labels = labels,
verticesOffset = hexagonVerticesOffset,
enableColour = true
)
// Stat Values
renderPolygonLabels(
context = context,
labels = statValuesAsText(
statLabels.values.toList(),
labelsHovered
),
verticesOffset = hexagonVerticesOffset,
offsetY = 5.5
)
// Nature-modified Stat Icons
if (statOptions.get(statTabIndex) == STATS) {
val nature = pokemon.effectiveNature
renderModifiedStatIcon(matrices, nature.increasedStat, true)
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
}
}
}
// Stat type select bar
val hasRideBehaviour = pokemon.form.riding.behaviours != null
val startPosX = x + if (hasRideBehaviour) 23 else 31
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
statOptions.forEachIndexed { index, stat ->
val statKey = if (stat == STATS) "" else ".$stat"
drawScaledText(
context = context,
text = lang("ui.stats$statKey").bold(),
x = startPosX + (statTabWidth * index),
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == stat) WHITE else GREY,
centered = true
when (statOptions.get(statTabIndex)) {
STATS -> drawStatHexagon(
mapOf(
Stats.HP to pokemon.maxHealth,
Stats.ATTACK to pokemon.attack,
Stats.DEFENCE to pokemon.defence,
Stats.SPECIAL_ATTACK to pokemon.specialAttack,
Stats.SPECIAL_DEFENCE to pokemon.specialDefence,
Stats.SPEED to pokemon.speed
),
colour = Vector3f(50F/255, 215F/255F, 1F),
maximum = 400
)
IV -> drawStatHexagon(
effectiveBattleIVs,
colour = Vector3f(216F/255, 100F/255, 1F),
maximum = 31
)
EV -> drawStatHexagon(
pokemon.evs.associate { it.key to it.value },
colour = Vector3f(1F, 1F, 100F/255),
maximum = 252
)
}
drawScaledText(
context = context,
text = statsLabel.bold(),
x = x + 31,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == STATS) WHITE else GREY,
centered = true
)
drawScaledText(
context = context,
text = ivLabel.bold(),
x = x + 55,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == IV) WHITE else GREY,
centered = true
)
drawScaledText(
context = context,
text = evLabel.bold(),
x = x + 79,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == EV) WHITE else GREY,
centered = true
)
drawScaledText(
context = context,
text = otherLabel.bold(),
x = x + 103,
y = y + 143,
scale = SCALE,
colour = if (statOptions.get(statTabIndex) == OTHER) WHITE else GREY,
centered = true
)
val paddingLeft = (WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2
blitk(
matrixStack = context.pose(),
texture = tabMarkerResource,
x= (startPosX + (statTabIndex * statTabWidth) - (MARKER_WIDTH / 4)) / SCALE,
x= ((x + paddingLeft + ((statTabIndex + 1) * statTabWidth)) / SCALE) - 2,
y = (y + 140) / SCALE,
width = MARKER_WIDTH,
width = 8,
height = 4,
scale = SCALE
scale = SCALE,
)
if (renderChart) {
// Stat Labels
renderTextAtVertices(
context = context,
hp = hpLabel.bold(),
spAtk = spAtkLabel.bold(),
atk = atkLabel.bold(),
spDef = spDefLabel.bold(),
def = defLabel.bold(),
speed = speedLabel.bold()
)
// Stat Values
renderTextAtVertices(
context = context,
offsetY = 5.5,
enableColour = false,
hp = getStatValueAsText(Stats.HP),
spAtk = getStatValueAsText(Stats.SPECIAL_ATTACK),
atk = getStatValueAsText(Stats.ATTACK),
spDef = getStatValueAsText(Stats.SPECIAL_DEFENCE),
def = getStatValueAsText(Stats.DEFENCE),
speed = getStatValueAsText(Stats.SPEED)
)
// Nature-modified Stat Icons
if (statOptions.get(statTabIndex) == STATS) {
val nature = pokemon.effectiveNature
renderModifiedStatIcon(matrices, nature.increasedStat, true)
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
}
} else {
var drawY = y + 11
drawFriendship(x + 5, drawY, matrices, context, pokemon.friendship)
drawY += 30
drawFullness(x + 5, drawY, matrices, context, pokemon)
drawY += 30
for (renderableFeature in renderableFeatures) {
val rendered = renderableFeature.render(
GuiGraphics = context,
x = x + 5F,
y = drawY.toFloat(),
pokemon = pokemon
)
if (rendered) {
drawY += 30
}
}
}
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
val index = getTabIndexFromPos(pMouseX, pMouseY)
// Only play sound here as the rest of the widget is meant to be silent
if (index in 0 until statOptions.size && statTabIndex != index) {
if (index in 0..4 && statTabIndex != index) {
statTabIndex = index
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
if (statOptions.get(statTabIndex) == RIDE && ((pokemon.riding.behaviours?.size ?: 0) > 1) && centerHovered(pMouseX, pMouseY)) {
rideBehaviourIndex = (rideBehaviourIndex + 1) % (pokemon.riding.behaviours?.size ?: 1)
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.GUI_CLICK, 1.0F))
}
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
private fun statValuesAsText(stats: List<Stat>, asPercent: Boolean): List<MutableComponent> {
return stats.map { stat ->
when (statOptions.get(statTabIndex)) {
STATS -> (if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()).text()
IV -> {
(if (asPercent) {
"${floor((((if (pokemon.ivs.isHyperTrained(stat)) pokemon.ivs.hyperTrainedIVs[stat] else pokemon.ivs[stat]) ?: 0) / 31.0) * 100)}%"
} else {
if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})"
else pokemon.ivs[stat].toString()
}).text()
}
EV -> {
(if (asPercent) "${floor((pokemon.evs.getOrDefault(stat) / 252.0) * 100)}%"
else pokemon.evs.getOrDefault(stat).toString()).text()
}
else -> "0".text()
}
private fun getStatValueAsText(stat: Stat): MutableComponent {
val value = when (statOptions.get(statTabIndex)) {
STATS -> if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()
IV -> if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})" else pokemon.ivs[stat].toString()
EV -> pokemon.evs.getOrDefault(stat).toString()
else -> "0"
}
return value.text()
}
private fun renderModifiedStatIcon(pPoseStack: PoseStack, stat: Stat?, increasedStat: Boolean) {
@@ -531,7 +534,7 @@ class StatWidget(
}
}
private fun getModifiedStatColour(stat: Stat?, enableColour: Boolean): Int {
private fun getModifiedStatColour(stat: Stat, enableColour: Boolean): Int {
if (statOptions.get(statTabIndex) == STATS && enableColour) {
val nature = pokemon.effectiveNature
@@ -541,38 +544,81 @@ class StatWidget(
return WHITE
}
private fun renderPolygonLabels(context: GuiGraphics, labels: List<MutableComponent>, verticesOffset: List<Pair<Double, Double>>, offsetY: Double = 0.0, enableColour: Boolean = false) {
if (labels.size != verticesOffset.size) return
private fun renderTextAtVertices(
context: GuiGraphics,
offsetY: Double = 0.0,
enableColour: Boolean = true,
hp: MutableComponent,
spAtk: MutableComponent,
atk: MutableComponent,
spDef: MutableComponent,
def: MutableComponent,
speed: MutableComponent
) {
drawScaledText(
context = context,
text = hp,
x = x + 67,
y = y + 10.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.HP, enableColour),
centered = true
)
labels.forEachIndexed { index, label ->
drawScaledText(
context = context,
text = label,
x = x + verticesOffset[index].first,
y = y + verticesOffset[index].second + offsetY,
scale = SCALE,
colour = getModifiedStatColour(statLabels[label], enableColour),
centered = true
)
}
}
drawScaledText(
context = context,
text = spAtk,
x = x + 12,
y = y + 42.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPECIAL_ATTACK, enableColour),
centered = true
)
private fun centerHovered(pMouseX: Number, pMouseY: Number): Boolean {
return (pMouseX.toInt() in (x + 44)..(x + 90)) && (pMouseY.toInt() in (y + 47)..(y + 93))
}
drawScaledText(
context = context,
text = atk,
x = x + 122,
y = y + 42.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.ATTACK,enableColour),
centered = true
)
private fun statLabelsHovered(labelOffsets: List<Pair<Double, Double>>, mouseX: Int, mouseY: Int): Boolean {
return labelOffsets.any { pos ->
(mouseX.toDouble() in (x + pos.first - 12)..(x + pos.first + 12))
&& (mouseY.toDouble() in (y + pos.second - 1)..(y + pos.second + 10))
}
drawScaledText(
context = context,
text = spDef,
x = x + 12,
y = y + 93.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPECIAL_DEFENCE, enableColour),
centered = true
)
drawScaledText(
context = context,
text = def,
x = x + 122,
y = y + 93.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.DEFENCE, enableColour),
centered = true
)
drawScaledText(
context = context,
text = speed,
x = x + 67,
y = y + 124.5 + offsetY,
scale = SCALE,
colour = getModifiedStatColour(Stats.SPEED, enableColour),
centered = true
)
}
private fun getTabIndexFromPos(mouseX: Double, mouseY: Double): Int {
val hasRideBehaviour = pokemon.form.riding.behaviours != null
val left = x + if (hasRideBehaviour) ((23 + 1) / 2).toFloat() else ((31 + 1) / 2).toFloat()
val statTabWidth = if (hasRideBehaviour) STAT_TAB_WIDTH_6 else STAT_TAB_WIDTH_5
val paddingLeft = ((WIDTH - ((statOptions.size + 1) * statTabWidth)) / 2.0) + (statTabWidth / 2)
val left = x + paddingLeft
val top = y + 140.0
if (mouseX in left..(left + (statTabWidth * (statOptions.size + 1))) && mouseY in top..(top + 9.0)) {
var startX = left
@@ -585,4 +631,4 @@ class StatWidget(
}
return -1
}
}
}
@@ -1,53 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
class FriendshipFeatureRenderer(
val selectedPokemon: Pokemon
) : BarSummarySpeciesFeatureRenderer(
"friendship",
lang("ui.stats.friendship"),
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png"),
selectedPokemon,
0,
255,
selectedPokemon.friendship
) {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
renderElement(guiGraphics, x, y, pokemon)
return true
}
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val red = 1
val green: Number = if (barValue >= 160) 0.28 else 0.56
val blue: Number = if (barValue >= 160) 0.4 else 0.64
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -1,55 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
class FullnessFeatureRenderer(
val selectedPokemon: Pokemon
) : BarSummarySpeciesFeatureRenderer(
"fullness",
lang("ui.stats.fullness"),
cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
cobblemonResource("textures/gui/summary/summary_stats_fullness_overlay.png"),
selectedPokemon,
0,
selectedPokemon.getMaxFullness(),
selectedPokemon.currentFullness
) {
override fun render(guiGraphics: GuiGraphics, x: Float, y: Float, pokemon: Pokemon): Boolean {
renderElement(guiGraphics, x, y, pokemon)
return true
}
override fun renderBar(guiGraphics: GuiGraphics, x: Float, y: Float, barValue: Int, barRatio: Float, barWidth: Int) {
val (red, green, blue) = when {
barRatio <= 0.33 -> Triple(120F/255F, 200F/255F, 80F/255F) // Green
barRatio <= 0.66 -> Triple(240F/255F, 200F/255F, 65F/255F) // Yellow
else -> Triple(230F/255F, 80F/255F, 65F/255F) // Red
}
blitk(
matrixStack = guiGraphics.pose(),
texture = CobblemonResources.WHITE,
x = x + 3,
y = y + 13,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.keybind.keybinds
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
import com.cobblemon.mod.common.api.riding.RidingStyle
import com.cobblemon.mod.common.battles.BattleFormat
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.ClientBattle
@@ -85,7 +84,7 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
if (isRidingPokemon(player) && canAttemptDismount(player, selectedPartyPokemon)) {
sendToServer(DismountPokemonPacket())
}
else if (!isRidingSelectedPokemon(player, selectedPartyPokemon)){
else {
checkForTargetInteractions(player, selectedPartyPokemon)
}
}
@@ -146,11 +145,6 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
if (player != vehicle.controllingPassenger) {
return true
}
val isAirRide = vehicle.ridingController?.context?.style == RidingStyle.AIR
val hasLandRide = vehicle.rideProp.behaviours?.get(RidingStyle.LAND) != null
if (isAirRide && hasLandRide) {
return false
}
else {
return vehicle.pokemon.uuid == selectedPartyPokemon.uuid
}
@@ -163,14 +157,6 @@ object PartySendBinding : CobblemonBlockingKeyBinding(
return true
}
private fun isRidingSelectedPokemon(player: LocalPlayer, selectedPartyPokemon: Pokemon, ignoreControlling: Boolean = false): Boolean {
if (!player.isPassenger) return false
if (player.vehicle !is PokemonEntity) return false
val vehicle = player.vehicle as PokemonEntity
if (ignoreControlling && player.vehicle!!.controllingPassenger == player) return false
return vehicle.pokemon.uuid == selectedPartyPokemon.uuid
}
override fun onPress() {
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.data
import com.cobblemon.mod.common.CobblemonRideSettings
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object RideSettingsSyncHandler : ServerNetworkPacketHandler<RideSettingsSyncPacket> {
override fun handle(packet: RideSettingsSyncPacket, server: MinecraftServer, player: ServerPlayer) {
CobblemonRideSettings.bird = packet.bird
CobblemonRideSettings.glider = packet.glider
CobblemonRideSettings.helicopter = packet.helicopter
CobblemonRideSettings.hover = packet.hover
CobblemonRideSettings.jet = packet.jet
CobblemonRideSettings.rocket = packet.rocket
CobblemonRideSettings.horse = packet.horse
CobblemonRideSettings.minekart = packet.minekart
CobblemonRideSettings.vehicle = packet.vehicle
CobblemonRideSettings.boat = packet.boat
CobblemonRideSettings.burst = packet.burst
CobblemonRideSettings.dolphin = packet.dolphin
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.data
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import net.minecraft.client.Minecraft
internal object UnlockReloadPacketHandler : ClientNetworkPacketHandler<UnlockReloadPacket> {
override fun handle(packet: UnlockReloadPacket, client: Minecraft) {
CobblemonDataProvider.canReload = true
}
}
Loaded 100 of 770 files, more files were not shown because too many files have changed in this diff. Show more