Compare commits

..
Author SHA1 Message Date
JPAKx4 9163046b45 Merge branch 'main' into irl_date_time
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
2025-08-08 21:03:49 -05:00
Paul 325d1af484 Fixed swim wanders not working 2025-08-09 10:44:55 +10:00
Polymeta 6ae71f28e6 Merge branch 'spicy-molang' into 'main'
Added string utility related MoLang functions

See merge request cable-mc/cobblemon!1754
2025-08-08 23:29:38 +00:00
SpicyandPolymeta 5687a71de4 Added string utility related MoLang functions 2025-08-08 23:29:38 +00:00
Polymeta 8199f8d7c2 Merge branch 'josh/helditem-render-fix' into 'main'
Fixed locators being flipped for shoulder mounts and model widgets

See merge request cable-mc/cobblemon!1764
2025-08-08 21:41:09 +00:00
Polymeta 6b39251d65 Merge branch 'Shipwreck-Loot-Table-Adjustments' into 'main'
Shipwreck loot table adjustments

See merge request cable-mc/cobblemon!1765
2025-08-08 21:40:50 +00:00
Slippy cd03e6f8c4 Replaced the Phantoms with Breezes in the Lush Shipwreck Cove 2025-08-08 00:48:19 -05:00
Slippy a1b53f90ad Adjusting the loot table to trial spawners in shipwreck coves a little bit. 2025-08-08 00:40:54 -05:00
Josh c0677c6353 Fixed item rendering being flipped for shoulder mounts and model widgets 2025-08-05 13:11:14 -04:00
JPAK 06732e89fd Update Changelog and error handling. 2025-04-22 14:27:40 -05:00
JPAK 9e8d6a4f39 Add an option for String timezones to "date_of" and "date_local_time" functions. 2025-04-22 12:33:45 -05:00
JPAK 662efa55c6 Fix imports. 2025-04-22 12:10:39 -05:00
JPAK efb9e538f3 Merge branch 'main' into irl_date_time_molang
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/molang/MoLangFunctions.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/Category.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2025-04-22 12:03:37 -05:00
JPAK 42c711cfc7 Fix config and add langs for config. 2025-02-07 14:35:59 -06:00
JPAK 681d5f8bcc Merge remote-tracking branch 'origin/irl_date_time' into irl_date_time_molang
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/config/Category.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2025-02-07 12:27:49 -06:00
JordanandJPAK a52d8452b4 Revert "Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition."
This reverts commit b0be0b2dca
2025-02-07 12:23:13 -06:00
JPAK e65298b587 Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition. 2025-02-07 12:23:13 -06:00
JPAK f8c09b8e26 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2025-02-07 12:23:11 -06:00
JPAK 5d1019afd5 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2025-02-07 12:22:28 -06:00
JPAK eb9bd116b2 Merge remote-tracking branch 'origin/irl_date_time' into irl_date_time_molang
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/config/CobblemonConfig.kt
2024-12-29 14:28:06 -06:00
JPAK 70bf4d6654 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2024-12-29 14:14:43 -06:00
Jordan 080a5b9c74 Revert "Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition."
This reverts commit b0be0b2dca
2024-12-27 19:45:08 +00:00
JPAK b0be0b2dca Add "irlDateRange" and "irlTimeRange" as a pokemon spawning condition. 2024-12-27 13:35:41 -06:00
JPAK d8300436c9 Add config for time zones and add an optional parameter to MoLangFunction's "date_local_time" and "date_of" to use the config time zone instead of the system time zone. 2024-12-26 22:25:15 -06:00
81 changed files with 1765 additions and 12895 deletions
+12 -1
View File
@@ -194,6 +194,17 @@
### Changes
- Changed pokemon caught and seen count to update based on the current pokedex being looked
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
- Added config setting `maxPokedexScanningDetectionRange` to control from what distance the player can scan Pokémon using the Pokédex.
- Added the `is_standing_on_blocks(depth, blocks...)` MoLang function, which returns whether the specified entity is standing on a specific block or set of blocks. Example usage: `q.is_standing_on_blocks(2, minecraft:sand)`
- Added config setting `hideUnimplementedPokemonInThePokedex` which hides unimplemented Pokémon from the Pokédex when set to true.
- Added molang particle queries for getting distance to targeted entities
- Added debug renderer for poseable entity locators
- Added time zone as a config option
- Added an optional MoValue to either use system timezone when set as true, or use the supplied string as a timezone for MoLang functions `date_local_time` and `date_of`. If the timezone supplied fails to parse this function now returns `0.0`.
### Additions
- Added time zone as a config option
- Added optional boolean to use time zone for MoLang function "date_local_time" and "date_of"
- Made Blocks of Gold count as Big Nuggets when held by a Pokémon (for Fling functionality)
- Players can now eat Sweet and Tart Apples, Whipped Dreams, and the Alcremie Sweets.
- Updated Sweet and Tart Apple sprites
@@ -240,7 +251,6 @@
- Offset in EmitterShape now ignores scale to be more like Blockbench by default. You can get this behaviour back by adding `"scale": true` in the `attachment_options` property in most EmitterShapes.
- Not specifying a dex in `/pokedex printcalculations {player} {dex}` will now print the National Dex statistics instead of showing all dexes. `/pokedex printcalculations {player} all` is how to view all dex statistics in one command.
- Removed Braised Vivichoke
- Updated Pokémon state icons shown in party interfaces
### Fixes
- Fixed game crashing when removing national pokedex using datapacks
@@ -399,6 +409,7 @@
- Added `q.has_argument(<argument_name>, [argument_value])` MoLang function to several battle-related action effect contexts.
- Added `q.has_argument_at(<index>, [argument_value])` MoLang function to several battle-related action effect contexts.
- Added `q.hit_count` MoLang function to move action effect contexts.
- Added `is_included`, `to_lower`, `to_upper`, and `string_length` as available Molang functions.
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -1,15 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.duck;
import org.joml.Vector3f;
public interface ServerEntityDuck {
void setDriverInput(Vector3f driverInput);
}
@@ -12,11 +12,9 @@ import com.cobblemon.mod.common.CobblemonNetwork;
import com.cobblemon.mod.common.OrientationControllable;
import com.cobblemon.mod.common.api.net.NetworkPacket;
import com.cobblemon.mod.common.api.orientation.OrientationController;
import com.cobblemon.mod.common.duck.ServerEntityDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.mixin.accessor.ChunkMapAccessor;
import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -28,7 +26,6 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.world.entity.Entity;
import org.joml.Matrix3f;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -41,7 +38,7 @@ import java.util.Set;
import java.util.function.Consumer;
@Mixin(ServerEntity.class)
public abstract class ServerEntityMixin implements ServerEntityDuck {
public abstract class ServerEntityMixin {
@Shadow
@Final
@@ -53,16 +50,11 @@ public abstract class ServerEntityMixin implements ServerEntityDuck {
private Matrix3f cobblemon$lastSentOrientation;
@Unique
private boolean cobblemon$lastSentActive;
@Unique
private Vector3f cobblemon$driverInput;
@Unique
private Vector3f cobblemon$lastSentDriverInput;
@Inject(method = "sendChanges", at = @At("TAIL"))
private void cobblemon$sendChanges(CallbackInfo ci) {
cobblemon$sendOrientationChanges();
cobblemon$sendRidingStateChanges();
cobblemon$sendDriverInput();
}
private void cobblemon$sendRidingStateChanges() {
@@ -90,17 +82,6 @@ public abstract class ServerEntityMixin implements ServerEntityDuck {
cobblemon$broadcast(new ClientboundUpdateOrientationPacket(currOrientation, currActive, entity.getId()));
}
private void cobblemon$sendDriverInput() {
if (!(this.entity instanceof ServerPlayer serverPlayer)) return;
if(!(serverPlayer.getVehicle() instanceof PokemonEntity)) return;
if(cobblemon$driverInput == null) return;
// If no change in input is detected then don't send a new packet
if (cobblemon$driverInput.equals(cobblemon$lastSentDriverInput)) return;
cobblemon$lastSentDriverInput = cobblemon$driverInput;
cobblemon$broadcast(new ClientboundUpdateDriverInputPacket(cobblemon$driverInput, entity.getId()));
}
private void cobblemon$broadcast(NetworkPacket<?> packet) {
if (!(entity.level() instanceof ServerLevel level)) return;
ChunkMap chunkMap = level.getChunkSource().chunkMap;
@@ -116,9 +97,5 @@ public abstract class ServerEntityMixin implements ServerEntityDuck {
}
}
@Override
public void setDriverInput(Vector3f driverInput) {
cobblemon$driverInput = driverInput;
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.mixin.accessor;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.network.ServerPlayerConnection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@@ -19,7 +18,4 @@ import java.util.Set;
public interface TrackedEntityAccessor {
@Accessor("seenBy")
Set<ServerPlayerConnection> getSeenBy();
@Accessor("serverEntity")
ServerEntity getServerEntity();
}
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
import com.mojang.authlib.GameProfile;
import net.minecraft.client.CameraType;
import net.minecraft.client.Minecraft;
@@ -84,13 +83,6 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
}
@Inject(method = "rideTick", at = @At("HEAD"))
private void cobblemon$updateDriverInputRideTick(CallbackInfo ci) {
if (Minecraft.getInstance().player != (Object)this) return;
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(this.xxa, this.zza, this.jumping, this.isShiftKeyDown()));
}
@Unique
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
var playerVehicle = player.getVehicle();
@@ -135,7 +127,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
return super.pick(hitDistance, partialTicks, hitFluids);
}
Vec3 locatorOffset = locator != null ? new Vec3(locator.getMatrix().getTranslation(new Vector3f())) : Vec3.ZERO;
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
OrientationController controller = this.getOrientationController();
@@ -15,7 +15,6 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Entity.class)
@@ -30,19 +29,4 @@ public abstract class EntityMixin {
}
}
@Redirect(
method = "collide(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/Entity;onGround()Z"
)
)
private boolean cobblemon$forceOnGroundForStepUp(Entity entity) {
if (entity instanceof PokemonEntity pokemon && entity.hasControllingPassenger()) {
return true;
}
return entity.onGround();
}
}
@@ -41,7 +41,6 @@ import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHand
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
import com.cobblemon.mod.common.client.net.riding.ClientboundUpdateDriverInputHandler
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
@@ -84,7 +83,6 @@ import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityP
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
@@ -175,7 +173,6 @@ import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPa
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
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
@@ -226,7 +223,6 @@ import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkH
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
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
import com.cobblemon.mod.common.net.serverhandling.storage.BenchMoveHandler
@@ -469,7 +465,6 @@ object CobblemonNetwork {
// Riding
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
list.add(PacketRegisterInfo(ClientboundUpdateDriverInputPacket.ID, ClientboundUpdateDriverInputPacket::decode, ClientboundUpdateDriverInputHandler))
return list
}
@@ -576,7 +571,6 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
// Cooking
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
@@ -143,6 +143,12 @@ import net.minecraft.world.level.levelgen.Heightmap
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.*
/**
* Holds a bunch of useful MoLang trickery that can be used or extended in API
@@ -168,6 +174,20 @@ object MoLangFunctions {
val replace = params.getString(2)
return@Function StringValue(text.replace(search, replace))
},
"is_included" to java.util.function.Function { params ->
val text = params.getString(0)
val search = params.getString(1)
return@Function DoubleValue(text.contains(search))
},
"to_lower" to java.util.function.Function { params ->
return@Function StringValue(params.getString(0).lowercase())
},
"to_upper" to java.util.function.Function { params ->
return@Function StringValue(params.getString(0).uppercase())
},
"string_length" to java.util.function.Function { params ->
return@Function DoubleValue(params.getString(0).length)
},
"is_blank" to java.util.function.Function { params ->
val arg = params.get<MoValue>(0)
return@Function DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
@@ -275,16 +295,44 @@ object MoLangFunctions {
DoubleValue(System.currentTimeMillis())
},
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
"date_local_time" to java.util.function.Function { _ ->
val time = System.currentTimeMillis()
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
"date_local_time" to java.util.function.Function { params ->
val useTimeZone = params.get<MoValue>(0)
var timezone: ZoneId? = null
if (useTimeZone is DoubleValue){
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
} else if (useTimeZone is StringValue){
try {
timezone = ZoneId.of(useTimeZone.value)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
return@Function DoubleValue.ZERO
}
}
val date: LocalDateTime = if (timezone != null) LocalDateTime.now(timezone) else LocalDateTime.now()
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(date)
StringValue(formatted)
},
"date_of" to java.util.function.Function { params ->
val time = params.getDouble(0).toLong()
val useTimeZone = params.get<MoValue>(1)
var timezone: ZoneId? = null
if (useTimeZone is DoubleValue){
timezone = if (useTimeZone.value == 1.0) Cobblemon.config.timeZoneId else null
} else if (useTimeZone is StringValue){
try {
timezone = ZoneId.of(useTimeZone.value)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Error parsing timezone: ${useTimeZone.value}")
return@Function DoubleValue.ZERO
}
}
val date = java.util.Date(time)
val formatted = java.text.SimpleDateFormat("DD/MM/YYYY").format(date)
val instant: Instant = date.toInstant()
val formatted = DateTimeFormatter.ofPattern("dd/MM/uuuu").format(LocalDateTime.ofInstant(instant,
timezone ?: ZoneOffset.systemDefault()
))
StringValue(formatted)
},
"date_is_after" to java.util.function.Function { params ->
@@ -646,7 +694,7 @@ object MoLangFunctions {
map
}
)
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val itemStackFunctions = mutableListOf<(ItemStack, RegistryAccess) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ stack, registryAccess ->
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
val holder = itemRegistry.wrapAsHolder(stack.item)
@@ -665,7 +713,7 @@ object MoLangFunctions {
}
)
val entityFunctions: MutableList<(Entity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val entityFunctions: MutableList<(Entity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("uuid") { _ -> StringValue(entity.uuid.toString()) }
@@ -903,7 +951,7 @@ object MoLangFunctions {
}
)
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
val livingEntityFunctions: MutableList<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>>(
{ entity ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
map.put("is_living_entity") { DoubleValue.ONE }
@@ -1645,8 +1693,6 @@ object MoLangFunctions {
map.put("get_alt_pose") { StringValue(pokemonEntity.getAltPose()) }
map.put("is_gliding") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("gliding"))) }
map.put("is_sprinting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("sprinting"))) }
map.put("is_drifting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("drifting"))) }
map.put("is_powered_drifting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("powered_drifting"))) }
map.put("in_air") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("in_air"))) }
map.put("is_wild") { DoubleValue(pokemonEntity.ownerUUID == null) }
map.put("is_in_party") { DoubleValue(pokemonEntity.pokemon.storeCoordinates.get()?.store is PartyStore) }
@@ -1974,7 +2020,7 @@ object MoLangFunctions {
}
)
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val pokemonPropertiesFunctions: MutableList<(PokemonProperties) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ props ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -2032,7 +2078,7 @@ object MoLangFunctions {
}
)
val evolutionFunctions: MutableList<(Evolution) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
val evolutionFunctions: MutableList<(Evolution) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
{ evolution ->
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
@@ -23,19 +22,15 @@ object RidingBehaviours {
init {
register(BirdBehaviour.KEY, BirdBehaviour())
register(BirdNoRollBehaviour.KEY, BirdNoRollBehaviour())
register(DolphinBehaviour.KEY, DolphinBehaviour())
register(HorseBehaviour.KEY, HorseBehaviour())
register(BoatBehaviour.KEY, BoatBehaviour())
register(GliderBehaviour.KEY, GliderBehaviour())
register(HelicopterBehaviour.KEY, HelicopterBehaviour())
register(JetBehaviour.KEY, JetBehaviour())
register(JetNoRollBehaviour.KEY, JetNoRollBehaviour())
register(BurstBehaviour.KEY, BurstBehaviour())
register(VehicleBehaviour.KEY, VehicleBehaviour())
register(MinekartBehaviour.KEY, MinekartBehaviour())
register(HoverBehaviour.KEY, HoverBehaviour())
register(RocketBehaviour.KEY, RocketBehaviour())
register(CompositeBehaviour.KEY, CompositeBehaviour())
}
@@ -107,6 +107,14 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
upForce -= 0.3
}
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
//Only limit altitude if altitude is not infinite
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
return velocity
@@ -121,7 +129,12 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
var glideSpeedChange = 0.0
val currSpeed = state.rideVelocity.get().length()
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
@@ -456,7 +469,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
state: BirdState,
vehicle: PokemonEntity
): Boolean {
return false
return true
}
override fun getRideSounds(
@@ -1,518 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.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.OrientationControllable
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
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
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.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import org.joml.Vector3f
import kotlin.math.*
class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState> {
companion object {
val KEY = cobblemonResource("air/birdnoroll")
}
override val key: ResourceLocation = KEY
override fun getRidingStyle(settings: BirdNoRollSettings, state: BirdNoRollState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<BirdNoRollSettings, BirdNoRollState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
override fun isActive(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun pose(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
return state.rideVelocity.get().length().toFloat()
}
override fun rotation(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
var yVel = 0.0
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
//Perform ride velocity update
calculateRideSpaceVel(settings, state, vehicle, driver)
//Translate ride space velocity to world space velocity.
val controller = (driver as? OrientationControllable)?.orientationController
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
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
}
//Bring the ride out of the sky when stamina is depleted.
if (state.stamina.get() <= 0.0) {
upForce -= 0.3
}
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
return velocity
}
/*
* Calculates the change in the ride space vector due to player input and ride state
*/
fun calculateRideSpaceVel(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity, driver: Player) {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
var glideSpeedChange = 0.0
val currSpeed = state.rideVelocity.get().length()
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
//make sure it can't exceed top speed
val forwardInput = when {
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
activeInput = true
}
val controller = (driver as? OrientationControllable)?.orientationController
if (controller != null) {
//Base glide speed change on current pitch of the ride.
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
glideSpeedChange = glideSpeedChange * 0.25
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
if (glideSpeedChange <= 0.0) {
//Ensures that a propelling force is still able to be applied when
//climbing in height
if (driver.zza <= 0) {
//speed decrease should be 2x speed increase?
//state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
lerp( newVelocity.z, 0.0,glideSpeedChange * -0.0166 * 2 )
)
}
} else {
// only add to the speed if it hasn't exceeded the current
//glide angles maximum amount of speed that it can give.
//state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
min(newVelocity.z + ((0.0166 * 2) * glideSpeedChange), glideTopSpeed)
)
}
}
//Vertical movement based on driver input.
val vertTopSpeed = topSpeed / 2.0
val vertInput = when {
driver.jumping && state.rideVelocity.get().length() < 0.3 -> 1.0
driver.isShiftKeyDown && state.rideVelocity.get().length() < 0.3 -> -1.0
else -> 0.0
}
if (vertInput != 0.0 && state.stamina.get() > 0.0) {
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
newVelocity.z)
activeInput = true
}
else {
newVelocity = Vec3(
newVelocity.x,
lerp(newVelocity.y, 0.0, vertTopSpeed / 20.0),
newVelocity.z)
}
//Check if the ride should be gliding
if (driver.isLocalPlayer) {
if (activeInput && state.stamina.get() > 0.0) {
state.gliding.set(false)
} else {
state.gliding.set(true)
}
}
//air resistance
if( abs(newVelocity.z) > 0) {
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.0015 * newVelocity.z.sign, newVelocity.z))
}
state.rideVelocity.set(newVelocity)
}
override fun angRollVel(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//limit rolling based on handling and current speed.
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
//TODO: Tie handling into yaw rate
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
val rotMin = 15.0
val rotLimit = max(handling * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
//val pitchedYawDelt = yawDeltaDeg - trueYawDelt
controller.applyGlobalYaw(trueYawDelt.toFloat())
//Calculate correcting roll force.
val correctionRate = 5.0
if(abs(controller.roll) > rotLimit && state.rideVelocity.get().length() < 0.2) {
state.currRollCorrectionForce.set(
state.currRollCorrectionForce.get() + ((abs(controller.roll) - rotLimit) / 180 - rotLimit) * controller.roll.sign * correctionRate * deltaTime * 0.01
)
} else {
state.currRollCorrectionForce.set(
//reduce rollCorrection gradually
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
)
}
//yaw, pitch, roll
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
}
override fun rotationOnMouseXY(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//TODO: hook up handling in parity with normal bird
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//Smooth out mouse input.
val smoothingSpeed = 4.0
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * ( 1.0 - min(sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
var pitchRot = yInput
// Pitch up globally
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
//yaw, pitch, roll
return Vec3.ZERO
}
/*
* Normalizes the given value between a min and a max.
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
* and 1.0 represents x is at or above it.
*/
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
require(max > min) { "max must be greater than min" }
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
}
override fun canJump(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
//Must I ensure that topspeed is greater than minimum?
val normalizedGlideSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
}
override fun useAngVelSmoothing(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
if (state.gliding.get()) {
return cobblemonResource("gliding")
}
return cobblemonResource("no_pose")
}
override fun inertia(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Double {
return 0.5
}
override fun shouldRoll(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun turnOffOnGround(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun getRideSounds(
settings: BirdNoRollSettings,
state: BirdNoRollState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: BirdNoRollSettings) = BirdNoRollState()
}
class BirdNoRollSettings : RidingBehaviourSettings {
override val key = BirdNoRollBehaviour.KEY
var infiniteAltitude: Expression = "false".asExpression()
private set
var infiniteStamina: Expression = "false".asExpression()
private set
//max y level for the ride
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(glidespeedExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
infiniteAltitude = buffer.readExpression()
infiniteStamina = buffer.readExpression()
glidespeedExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
}
}
class BirdNoRollState : RidingBehaviourState() {
var gliding = ridingState(false, Side.CLIENT)
var lastGlide = ridingState(-100L, Side.CLIENT)
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeFloat(stamina.get())
buffer.writeBoolean(gliding.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
gliding.set(buffer.readBoolean(), forced = true)
}
override fun reset() {
super.reset()
gliding.set(false, forced = true)
lastGlide.set(-100L, forced = true)
currRollCorrectionForce.set(0.0, forced = true)
}
override fun toString(): String {
return "BirdNoRollState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
}
override fun copy() = BirdNoRollState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.gliding.set(this.gliding.get(), forced = true)
it.lastGlide.set(this.lastGlide.get(), forced = true)
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is BirdNoRollState) return false
if (previous.gliding.get() != gliding.get()) return true
return super.shouldSync(previous)
}
}
@@ -138,6 +138,14 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
//If stamina has run out then initiate forced glide down.
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
//Only limit altitude if altitude is not infinite
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(0.0, upForce, forwardForce)
return velocity
@@ -154,11 +162,17 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
val speed = state.rideVelocity.get().length()
//Give no altitude limit if at max jump stat.
val pushingHeightLimit = if (vehicle.runtime.resolveBoolean(settings.infiniteStamina)) false
else (vehicle.y >= altitudeLimit && vehicle.xRot <= 0)
//speed up and slow down based on input
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f && !pushingHeightLimit) {
//modify acceleration to be slower when at closer speeds to top speed
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
state.rideVelocity.set(
@@ -168,7 +182,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
)
)
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f || pushingHeightLimit)) {
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
@@ -413,6 +427,9 @@ class JetSettings : RidingBehaviourSettings {
var infiniteStamina: Expression = "false".asExpression()
private set
var infiniteAltitude: Expression = "false".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
@@ -434,6 +451,7 @@ class JetSettings : RidingBehaviourSettings {
buffer.writeExpression(minSpeed)
buffer.writeExpression(handlingYawExpr)
buffer.writeExpression(infiniteStamina)
buffer.writeExpression(infiniteAltitude)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
buffer.writeExpression(speedExpr)
@@ -447,6 +465,7 @@ class JetSettings : RidingBehaviourSettings {
minSpeed = buffer.readExpression()
handlingYawExpr = buffer.readExpression()
infiniteStamina = buffer.readExpression()
infiniteAltitude = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
speedExpr = buffer.readExpression()
@@ -482,6 +501,8 @@ class JetState : RidingBehaviourState() {
it.currSpeed.set(currSpeed.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
// it.currMouseXForce.set(currMouseXForce.get(), forced = true)
// it.currMouseYForce.set(currMouseYForce.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
@@ -1,477 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.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.OrientationControllable
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
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.*
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.*
class JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
companion object {
val KEY = cobblemonResource("air/jetnoroll")
}
override val key = KEY
override fun getRidingStyle(settings: JetNoRollSettings, state: JetNoRollState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<JetNoRollSettings, JetNoRollState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
override fun isActive(settings: JetNoRollSettings, state: JetNoRollState, 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
}
}
override fun pose(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity, driver: Player): Float {
//retrieve stats
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
//retrieve minSpeed
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
state.stamina.set(1.0f)
return state.rideVelocity.get().length().toFloat()
}
//TODO: Move these functions to a riding util class.
/*
* Normalizes the current speed between minSpeed and maxSpeed.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
*/
private fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
}
override fun rotation(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
return Vec2(driver.xRot * 0.5f, driver.yRot)
}
override fun velocity(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
var upForce = 0.0
var forwardForce = 0.0
val controller = (driver as? OrientationControllable)?.orientationController
//Calculate ride space velocity
calculateRideSpaceVel(settings, state, vehicle, driver)
//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
}
//If stamina has run out then initiate forced glide down.
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
val velocity = Vec3(0.0, upForce, forwardForce)
return velocity
}
/*
* Calculates the change in the ride space vector due to player input and ride state
*/
private fun calculateRideSpaceVel(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
val speed = state.rideVelocity.get().length()
//speed up and slow down based on input
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
//modify acceleration to be slower when at closer speeds to top speed
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
)
)
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
state.rideVelocity.set(
Vec3(
state.rideVelocity.get().x,
state.rideVelocity.get().y,
max(state.rideVelocity.get().z - ((accel) / 4), minSpeed)
)
)
} else if (driver.zza < 0.0 && speed > minSpeed) {
//modify deccel to be slower when at closer speeds to minimum speed
val deccelMod = max((normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
//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 - ((accel * deccelMod) / 2), minSpeed)
)
)
}
}
override fun angRollVel(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
// //Cap at a rate of 5fps so frame skips dont lead to huge jumps
// val cappedDeltaTime = min(deltaTime, 0.2)
//
// //Get handling in degrees per second
// val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
//
// //Base the change off of deltatime.
// var handlingYaw = yawRotRate * (cappedDeltaTime)
//
// //apply stamina debuff if applicable
// handlingYaw *= if (state.stamina.get() > 0.0) 1.0 else 0.5
//
// //A+D to yaw
// val yawForce = driver.xxa * handlingYaw * -1
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
//TODO: figure out a cleaner solution to this issue of large jumps when skipping frames or lagging
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
val cappedDeltaTime = min(deltaTime, 0.2)
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
// Accumulate the mouse input
state.currMouseXForce.set((state.currMouseXForce.get() + (0.0015 * mouseX * invertRoll)).coerceIn(-1.0, 1.0))
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY * invertPitch)).coerceIn(-1.0, 1.0))
//Get handling in degrees per second
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
//convert it to delta time
handling *= (cappedDeltaTime)
//apply stamina debuff if applicable
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
var pitchRot = handling * state.currMouseYForce.get()
// Yaw
val yawRot = handling * 0.5 * state.currMouseXForce.get()
val controller = (driver as OrientationControllable).orientationController
controller.applyGlobalYaw(yawRot.toFloat())
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
pitchRot = 0.0
state.currMouseYForce.set(0.0)
mouseYSmoother.reset()
} else {
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
}
val mouseRotation = Vec3(0.0, pitchRot, 0.0)
// Have accumulated input begin decay when no input detected
if (abs(mouseX) == 0.0) {
// Have decay on roll be much stronger.
state.currMouseXForce.set(lerp(state.currMouseXForce.get(), 0.0, 0.02))
}
if (mouseY == 0.0) {
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.005))
}
//yaw, pitch, roll
return Vec3.ZERO
}
override fun canJump(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
//Must I ensure that topspeed is greater than minimum?
val normalizedSpeed = normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed)
//TODO: Determine if this should be based on max possible speed instead of top speed.
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
}
override fun useAngVelSmoothing(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun useRidingAltPose(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Double {
return 1.0
}
override fun shouldRoll(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return true
}
override fun turnOffOnGround(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun dismountOnShift(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun getRideSounds(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: JetNoRollSettings) = JetNoRollState()
override fun damageOnCollision(
settings: JetNoRollSettings,
state: JetNoRollState,
vehicle: PokemonEntity,
impactVec: Vec3
): Boolean {
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
}
}
class JetNoRollSettings : RidingBehaviourSettings {
override val key = JetNoRollBehaviour.KEY
var gravity: Expression = "0".asExpression()
private set
var minSpeed: Expression = "1.2".asExpression()
private set
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
private set
// Make configurable by json
var infiniteStamina: Expression = "false".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
private set
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.8, 1.0)".asExpression()
private set
var accelerationExpr: Expression =
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
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) {
rideSounds = RideSoundSettingsList.decode(buffer)
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()
}
}
class JetNoRollState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.CLIENT)
var currMouseXForce = ridingState(0.0, Side.CLIENT)
var currMouseYForce = ridingState(0.0, Side.CLIENT)
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeDouble(currSpeed.get())
buffer.writeFloat(stamina.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
currSpeed.set(buffer.readDouble(), forced = true)
}
override fun reset() {
super.reset()
currSpeed.set(0.0, forced = true)
currMouseXForce.set(0.0, forced = true)
currMouseYForce.set(0.0, forced = true)
}
override fun copy() = JetNoRollState().also {
it.currSpeed.set(currSpeed.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.rideVelocity.set(rideVelocity.get(), forced = true)
// it.currMouseXForce.set(currMouseXForce.get(), forced = true)
// it.currMouseYForce.set(currMouseYForce.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is JetNoRollState) return false
if (previous.currSpeed != currSpeed) return true
return super.shouldSync(previous)
}
}
@@ -1,566 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types.air
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
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
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
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 org.joml.Matrix3f
import kotlin.math.*
class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
companion object {
val KEY = cobblemonResource("air/rocket")
}
override val key = KEY
override fun getRidingStyle(settings: RocketSettings, state: RocketState): RidingStyle {
return RidingStyle.AIR
}
val poseProvider = PoseProvider<RocketSettings, RocketState>(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) { _, state, _ ->
return@PoseOption state.boosting.get()
})
override fun isActive(
settings: RocketSettings,
state: RocketState,
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
}
}
override fun pose(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): PoseType {
return this.poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Float {
// Use this as a "tick" function and check to see if the driver is "boosting"
if(vehicle.level().isClientSide) {
//If the forward key is not held then it cannot be boosting
if(Minecraft.getInstance().options.keyUp.isDown()) {
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
//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
state.boosting.set(!state.boosting.get())
}
//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)
state.boosting.set(false)
state.canSpeedBurst.set(true)
}
}
return state.rideVelocity.get().length().toFloat()
}
override fun updatePassengerRotation(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
return
}
override fun clampPassengerRotation(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: LivingEntity
) {
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
val lookYawLimit = 90.0f
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
val ticksBeforeCorrection = 40
if(state.prevCamYRot.get() == driver.yRot){
state.ticksNoInput.set(state.ticksNoInput.get() + 1)
} else {
state.ticksNoInput.set(0)
}
val correctionForce = if(state.ticksNoInput.get() >= ticksBeforeCorrection && f != 0.0f && state.boosting.get()) 1f * min(sqrt(lookYawLimit / (abs(f))), 10.0f) else 0.0f
driver.yRotO += g - f
driver.yRot = driver.yRot + g - f //- ( clamp(f.sign * correctionForce, -abs(f), abs(f)) )
//driver.yRot = driver.yRot + g - f - ( min )
state.prevCamYRot.set(driver.yRot)
driver.setYHeadRot(driver.yRot)
}
override fun rotation(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
var newMomentum = state.turnMomentum.get().toDouble()
// Grab turningAcceleration and divide 20 twice to get
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0f) / 20.0f * 2
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
val maxTurnMomentum = 80.0f / 20.0f
// Base boost stats off of normal turning stats
val boostMaxTurnMomentum = maxTurnMomentum * 0.1f
val boostTurnInput = turnInput * 0.15f
if(state.boosting.get()) {
if(driver.xxa != 0.0f && abs(newMomentum + turnInput) < (boostMaxTurnMomentum)) { //If max momentum will not be exceeded then modulate
newMomentum += boostTurnInput
} else {
newMomentum = lerp(newMomentum, 0.0, 0.05)
}
} else {
if(driver.xxa == 0.0f) { //If no turning input then lerp to 0
newMomentum = lerp(newMomentum, 0.0, 0.05)
} else if(abs(newMomentum + turnInput) < maxTurnMomentum) { //If max momentum will not be exceeded then modulate
newMomentum += turnInput
}
}
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
}
override fun velocity(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
val retVel = calculateRideSpaceVel(settings, state, vehicle, driver)
state.rideVelocity.set(retVel)
return retVel
}
private fun calculateRideSpaceVel(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 2
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.5 * 2
var newVelocity = vehicle.deltaMovement
// Align the direction of movement with the world coordinate space so as not to modify it through turning
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.boosting.get()) {
//make sure it can't exceed top speed
val forwardInput = when {
vehicle.deltaMovement.horizontalDistance() > topSpeed && (driver.zza.sign == newVelocity.z.sign.toFloat()) -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
} else if ((state.stamina.get() > 0.0 && state.boosting.get())) {
val boostSpeed = topSpeed * 5
val boostAccel = if(newVelocity.length() < boostSpeed) accel * 3 else 0.0
val forwardInput = 1.0f
var burst = 0.0f
if(state.canSpeedBurst.get()) {
burst = 0.25f
state.canSpeedBurst.set(false)
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
newVelocity.z + (boostAccel * forwardInput.toDouble()) + burst)
}
//Vertical movement based on driver input.
val maxVertSpeed = 0.5f
var vertInput = 0.0
//If boosting then don't lose altitude
//otherwise propel up or fall
if (state.boosting.get() && !driver.jumping && vehicle.deltaMovement.y > (-maxVertSpeed * 0.25)) {
vertInput = -0.7
newVelocity = Vec3(
newVelocity.x,
newVelocity.y + accel * vertInput,
newVelocity.z)
} else if(state.boosting.get() && vehicle.deltaMovement.y < maxVertSpeed) {
vertInput = when {
driver.jumping -> 2.0
else -> 0.0
}
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * 0.5 * vertInput)),
newVelocity.z)
} else {
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
vertInput = 1.0
// Reset falldistance if upward motion is detected
vehicle.resetFallDistance()
} else {
vertInput = -2.5
}
if (state.stamina.get() > 0.0) {
newVelocity = Vec3(
newVelocity.x,
(newVelocity.y + (accel * vertInput)),
newVelocity.z)
}
}
return newVelocity
}
/*
* Normalizes the current speed between minSpeed and maxSpeed.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
*/
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
}
override fun angRollVel(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
//Smooth out mouse input.
val smoothingSpeed = 4
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
//yaw, pitch, roll
return Vec3(0.0, yInput, xInput)
}
override fun canJump(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return false
}
override fun setRideBar(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Float {
//Retrieve stamina from state and use it to set the "stamina bar"
return (state.stamina.get() / 1.0f)
}
override fun jumpForce(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): Float {
if (state.boosting.get()) {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//val exceededSpeed = max(vehicle.deltaMovement.length() - topSpeed, 0.0)
//TODO: Remove this magic number and get a better comparison for boost top speed
val normalizedBoostSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, topSpeed * 3)
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
} else {
return 1.0f
}
}
override fun useAngVelSmoothing(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
return cobblemonResource("no_pose")
}
override fun inertia(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Double {
return 1.0
}
override fun shouldRoll(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun turnOffOnGround(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun dismountOnShift(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun getRideSounds(
settings: RocketSettings,
state: RocketState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: RocketSettings) = RocketState()
}
class RocketSettings : RidingBehaviourSettings {
override val key = RocketBehaviour.KEY
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3) * 0.25".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 =
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
private set
// Between 30 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
private set
//Between a one block jump and a ten block jump
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
private set
// Controls the acceleration of the turning speed since the max turning speed is locked at 60 degrees/second
// So this is a range of 60 degrees/second per second to 20. Meaning at max stats when holding a direction it
// takes a full second to be turning at max speed.
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 20.0)".asExpression()
private set
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(key)
rideSounds.encode(buffer)
buffer.writeExpression(speedExpr)
buffer.writeExpression(accelerationExpr)
buffer.writeExpression(staminaExpr)
buffer.writeExpression(jumpExpr)
buffer.writeExpression(handlingExpr)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
speedExpr = buffer.readExpression()
accelerationExpr = buffer.readExpression()
staminaExpr = buffer.readExpression()
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
}
}
class RocketState : RidingBehaviourState() {
var boosting = ridingState(false, Side.BOTH)
var boostIsToggleable = ridingState(false, Side.BOTH)
var canSpeedBurst = ridingState(false, Side.BOTH)
var prevCamYRot: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
var ticksNoInput: SidedRidingState<Int> = ridingState(0, Side.CLIENT)
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
override fun reset() {
super.reset()
boosting.set(false, forced = true)
boostIsToggleable.set(false, forced = true)
canSpeedBurst.set(true, forced = true)
prevCamYRot.set(0.0f, forced = true)
ticksNoInput.set(0, forced = true)
turnMomentum.set(0.0f, forced = true)
}
override fun copy() = RocketState().also {
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
it.stamina.set(this.stamina.get(), forced = true)
it.boosting.set(this.boosting.get(), forced = true)
it.boostIsToggleable.set(this.boosting.get(), forced = true)
it.canSpeedBurst.set(this.canSpeedBurst.get(), forced = true)
it.prevCamYRot.set(this.prevCamYRot.get(), forced = true)
it.ticksNoInput.set(this.ticksNoInput.get(), forced = true)
it.turnMomentum.set(this.turnMomentum.get(), forced = true)
}
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(boosting.get())
buffer.writeBoolean(boostIsToggleable.get())
buffer.writeBoolean(canSpeedBurst.get())
buffer.writeFloat(prevCamYRot.get())
buffer.writeInt(ticksNoInput.get())
buffer.writeFloat(turnMomentum.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
boosting.set(buffer.readBoolean(), forced = true)
boostIsToggleable.set(buffer.readBoolean(), forced = true)
canSpeedBurst.set(buffer.readBoolean(), forced = true)
prevCamYRot.set(buffer.readFloat(), forced = true)
ticksNoInput.set(buffer.readInt(), forced = true)
turnMomentum.set(buffer.readFloat(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is RocketState) return false
if (previous.boosting.get() != boosting.get()) return true
if (previous.boostIsToggleable.get() != boostIsToggleable.get()) return true
if (previous.canSpeedBurst.get() != canSpeedBurst.get()) return true
if (previous.prevCamYRot.get() != prevCamYRot.get()) return true
if (previous.ticksNoInput.get() != ticksNoInput.get()) return true
if (previous.turnMomentum.get() != turnMomentum.get()) return true
return super.shouldSync(previous)
}
}
@@ -45,7 +45,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
): Boolean {
if (state.activeBehaviour.get() == defaultSettings.key) return false
if (!vehicle.onGround()) return false
if (state.lastTransition.get() + 5 >= vehicle.level().gameTime) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
return defaultBehaviour.isActive(defaultSettings, state.defaultBehaviourState, vehicle)
}
@@ -58,7 +58,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
): Boolean {
if (state.activeBehaviour.get() == alternativeSettings.key) return false
if (!driver.jumping) return false
if (state.lastTransition.get() + 5 >= vehicle.level().gameTime) return false
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
return alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, vehicle)
}
@@ -83,15 +83,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
// Use this as a "tick" function and calculate sprinting and inAir state here
state.sprinting.set(driver.isSprinting)
inAirCheck(state, vehicle)
return state.rideVelocity.get().length().toFloat()
}
fun inAirCheck(
state: HorseState,
vehicle: PokemonEntity,
) {
// Check both vertical movement and if there are blocks below.
val posBelow = vehicle.blockPosition().below()
val blockStateBelow = vehicle.level().getBlockState(posBelow)
@@ -100,10 +92,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// val level = vehicle.level()
// val toesBox = vehicle.boundingBox.move(0.0, -0.1, 0.0)
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
return state.rideVelocity.get().length().toFloat()
}
override fun updatePassengerRotation(
@@ -111,7 +107,26 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
state: HorseState,
vehicle: PokemonEntity,
driver: LivingEntity
) { return }
) {
//Take the inverse so that it cancels out how
//much the entity rotates.
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val maxYawDiff = 90.0f
//Normalize the current rotation diff
val rotMod = Mth.wrapDegrees(driver.yRot - vehicle.yRot) / maxYawDiff
val rotAmount = 10.0f
//Take the inverse so that you turn more at higher speeds
val normSpeed = 1.0f - 0.5f*normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
}
override fun clampPassengerRotation(
settings: HorseSettings,
@@ -134,9 +149,9 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
var turnAmount = calcRotAmount(settings, state, vehicle, driver)
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount * 0.5f )
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount )
}
@@ -154,9 +169,9 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
): Float {
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * 3
val walkHandlingBoost = 5
val walkHandling = handling * walkHandlingBoost
//In degrees per second
val walkHandling = 140.0 * 2
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * 2
val maxYawDiff = 90.0f
@@ -170,14 +185,13 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
//Take the inverse so that you turn less at higher speeds
val normSpeed = 1.0f
val normSpeed = 1.0f // = 1.0f - 0.5f*normalizeVal(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
// TurnRate should always be quick if not sprinting
val invRelSpeed = (inverseLerp(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 1.0f) * -1.0f
val turnRate = ((handling.toFloat() / 20.0f) * max(walkHandlingBoost * invRelSpeed, 1.0)).toFloat()
val turnRate = if(state.sprinting.get()) (handling.toFloat() / 20.0f) else (walkHandling.toFloat() / 20.0f)
//Ensure you only ever rotate as much difference as there is between the angles.
val turnSpeed = turnRate * rotDiffMod * normSpeed
val turnSpeed = turnRate * rotDiffMod * normSpeed
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
return rotAmount
@@ -204,19 +218,16 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
// Check to see if the ride should be walking or sprinting
val walkSpeed = getWalkSpeed(vehicle)
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 1.3
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
val topSpeed = if(state.sprinting.get()) rideTopSpeed else walkSpeed
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 2
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
//Flag for determining if player is actively inputting
var activeInput = false
// Gather the previous velocity and set the horizontal fields of the velocity vector to the magnitude of the
// ones from deltaMovement. This is important as when a collision happens the Deltamovement will reflect it
// but rideVelocity won't.
var newVelocity = Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.horizontalDistance())
newVelocity = newVelocity.add(0.0, state.rideVelocity.get().y, 0.0)
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
@@ -235,14 +246,11 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
activeInput = true
}
// Gravity logic
//Gravity logic
if (vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
} else {
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
//to do some testing and get other's opinions
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.8
val gravity = (9.8 / ( 20.0)) * 0.2
val terminalVel = 2.0
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
@@ -254,34 +262,27 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.03 * newVelocity.z.sign, newVelocity.z))
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
//Jump the thang!
if (driver.jumping && vehicle.onGround() && canJump) {
if (driver.jumping && vehicle.onGround()) {
val jumpForce = 1.0
val velMag = newVelocity.length()
val horz = state.rideVelocity.get().horizontalDistance()
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
val test = 0.0
//Ensure this doesn't add unwanted forward velocity
//val mag = if(newVelocity.length() < rideTopSpeed) newVelocity.length() else rideTopSpeed
//newVelocity = newVelocity.normalize().scale(mag)
}
//Zero out lateral velocity possibly picked up from a controller transition
newVelocity = Vec3(0.0, newVelocity.y, newVelocity.z)
return newVelocity
}
/*
* lerps the current val between minVal and maxVal.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minVal and 1.0 represents maxVal.
* Normalizes the current speed between minSpeed and maxSpeed.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
*/
private fun inverseLerp(currVal: Double, minVal: Double, maxVal: Double): Double {
require(maxVal > minVal) { "minVal must be greater than maxVal" }
return ((currVal - minVal) / (maxVal - minVal)).coerceIn(0.0, 1.0)
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
}
private fun getWalkSpeed( vehicle: PokemonEntity ): Double {
@@ -495,6 +496,7 @@ class HorseSettings : RidingBehaviourSettings {
jumpExpr = buffer.readExpression()
handlingExpr = buffer.readExpression()
}
}
class HorseState : RidingBehaviourState() {
@@ -1,563 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.riding.behaviour.types.land
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.MoLangMath.lerp
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.*
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.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.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec2
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import 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 {
val KEY = cobblemonResource("land/minekart")
}
override val key = KEY
override fun getRidingStyle(settings: MinekartSettings, state: MinekartState): RidingStyle {
return RidingStyle.LAND
}
val poseProvider = PoseProvider<MinekartSettings, MinekartState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
})
// val poseProvider = PoseProvider<MinekartSettings, MinekartState>(PoseType.STAND)
// .with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: MinekartSettings, state: MinekartState, 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
}
}
override fun pose(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): PoseType {
return poseProvider.select(settings, state, vehicle)
}
override fun speed(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Float {
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
if (driver.jumping && driver.zza > 0.0f) {
if (!state.drifting.get()) {
if (driver.xxa != 0.0f) {
state.clockwiseDrift.set(driver.xxa.sign > 0.0)
state.turnMomentum.set(0.0f)
state.drifting.set(true)
state.boost.set(0.0f)
} else {
//TODO: Add jumping logic
}
} else {
state.boost.set((state.boost.get() + boostPerSecond / 20.0f).coerceIn(0.0f, boostMax))
}
} else {
if(driver.zza <= 0.0 && state.boost.get() != 0.0f){
state.boost.set(0.0f)
} else if( state.boost.get() > 0.0f ) {
state.boost.set(max(state.boost.get() - boostLoss, 0.0f))
}
state.drifting.set(false)
}
inAirCheck(state, vehicle)
return driver.deltaMovement.length().toFloat()
}
fun inAirCheck(
state: MinekartState,
vehicle: PokemonEntity,
) {
// Check both vertical movement and if there are blocks below.
val posBelow = vehicle.blockPosition().below()
val blockStateBelow = vehicle.level().getBlockState(posBelow)
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
}
override fun rotation(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
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)
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
val lowSpeedTurnBoost = (inverseLerp(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 2.0f) * -1.0f
val forcedDriftTurnMomentum = (80.0f / 20.0f) * lowSpeedTurnBoost
val maxTurnMomentum = (60.0f / 20.0f) * lowSpeedTurnBoost
val maxDriftMomentum = forcedDriftTurnMomentum * 0.6f
if (!state.drifting.get()){
// perform non drift turning
if(abs(newMomentum) <= maxTurnMomentum && turnInput != 0.0 ) {
newMomentum += turnInput
newMomentum = min(max(newMomentum, maxTurnMomentum * -1.0), maxTurnMomentum.toDouble())
} else {
newMomentum = lerp(newMomentum, 0.0, 0.15)
}
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
} else {
// perform drift turning
if(abs(newMomentum) <= maxDriftMomentum && driftInput != 0.0 ) {
newMomentum += driftInput
newMomentum = min(max(newMomentum, maxDriftMomentum * -1.0), maxDriftMomentum.toDouble())
} else {
newMomentum = lerp(newMomentum, 0.0, 0.15)
}
val signedDriftTurn = if(state.clockwiseDrift.get()) forcedDriftTurnMomentum * -1.0f else forcedDriftTurnMomentum
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat() + signedDriftTurn.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat() + signedDriftTurn.toFloat())
}
}
override fun velocity(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
input: Vec3
): Vec3 {
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
return state.rideVelocity.get()
}
private fun calculateRideSpaceVel(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
// Check to see if the ride should be walking or sprinting
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) * 0.3 * 3 * 2.0
val driftAngle = 20.0f
//Align the direction of this vector with the vehicles current heading.
var newVelocity = vehicle.deltaMovement
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
// rotate the angle of force if drifting
if (state.drifting.get()) {
val signedDriftAngle = if(state.clockwiseDrift.get()) driftAngle else driftAngle * -1.0f
newVelocity = newVelocity.yRot(signedDriftAngle.toRadians())
}
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.inAir.get()) {
//make sure it can't exceed top speed
var forwardInput = when {
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z <= 0.0 -> 0.0
else -> driver.zza.sign
}
if (!state.drifting.get() && state.boost.get() != 0.0f) {
forwardInput = 2.0;
}
// If drifting we need more acceleration due to the constant turn
val driftAccelBoost = if (state.drifting.get()) 1.5 else 1.0
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble() * driftAccelBoost)))
}
// Gravity logic
if (vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
} else {
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
//to do some testing and get other's opinions
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.25 * 3.0
val terminalVel = 2.0
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
}
//ground Friction
var friction = 0.003 * 2.0
if (state.drifting.get()) {friction *= 0.5}
if (!state.inAir.get()) {
newVelocity = newVelocity.subtract(
min(friction , abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
min(friction , abs(newVelocity.z)) * newVelocity.z.sign
)
// TEST FOR LATERAL SLIP OR WHATEVER
//TODO: calc lateral slip and friction better!
if (!state.drifting.get()) {
newVelocity = newVelocity.subtract(
min(friction * 10, abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
0.0,
)
}
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
// val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
// //Jump the thang!
// if (driver.jumping && vehicle.onGround() && canJump) {
// val jumpForce = 0.5
//
// newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
//
// }
// unrotate the angle of force if drifting
if (state.drifting.get()) {
val inverseSignedDriftAngle = if(state.clockwiseDrift.get()) driftAngle * -1.0f else driftAngle
newVelocity = newVelocity.yRot(inverseSignedDriftAngle.toRadians())
}
return newVelocity
}
/*
* lerps the current val between minVal and maxVal.
* The result is clamped between 0.0 and 1.0, where 0.0 represents minVal and 1.0 represents maxVal.
*/
private fun inverseLerp(currVal: Double, minVal: Double, maxVal: Double): Double {
require(maxVal > minVal) { "minVal must be greater than maxVal" }
return ((currVal - minVal) / (maxVal - minVal)).coerceIn(0.0, 1.0)
}
override fun angRollVel(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
deltaTime: Double
): Vec3 {
return Vec3.ZERO
}
override fun rotationOnMouseXY(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
mouseY: Double,
mouseX: Double,
mouseYSmoother: SmoothDouble,
mouseXSmoother: SmoothDouble,
sensitivity: Double,
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
//Might need to add the smoothing here for default.
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
}
override fun canJump(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Boolean {
return vehicle.runtime.resolveBoolean(settings.canJump)
}
override fun setRideBar(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Float {
return 0.0f
}
override fun jumpForce(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
}
override fun gravity(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
}
override fun rideFovMultiplier(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): Float {
var fov = 1.0f
if(!state.drifting.get() && state.boost.get() != 0.0f) {
//TODO: is this too abrupt? Possibly caluclate differently?
fov = 1.2f
}
return fov
}
override fun useAngVelSmoothing(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun useRidingAltPose(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
when {
state.drifting.get() -> return cobblemonResource("drifting")
}
return cobblemonResource("no_pose")
}
override fun inertia(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Double {
return 1.0
}
override fun shouldRoll(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Boolean {
return false
}
override fun turnOffOnGround(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun dismountOnShift(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePokemonHead(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return false
}
override fun shouldRotatePlayerHead(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): Boolean {
return true
}
override fun getRideSounds(
settings: MinekartSettings,
state: MinekartState,
vehicle: PokemonEntity
): RideSoundSettingsList {
return settings.rideSounds
}
override fun createDefaultState(settings: MinekartSettings) = MinekartState()
}
class MinekartSettings : RidingBehaviourSettings {
override val key = MinekartBehaviour.KEY
var canJump = "true".asExpression()
private set
var boostLimit = "100.0f".asExpression()
private set
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 =
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
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 = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
private set
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)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(boostLimit)
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
boostLimit = buffer.readExpression()
}
}
class MinekartState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.BOTH)
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
var drifting = ridingState(false, Side.CLIENT)
var clockwiseDrift = ridingState(false, Side.CLIENT)
val boost: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
override fun reset() {
super.reset()
currSpeed.set(0.0, forced = true)
deltaRotation.set(Vec2.ZERO, forced = true)
drifting.set(false, forced = true)
clockwiseDrift.set(false, forced = true)
boost.set(0.0f, forced = true)
turnMomentum.set(0.0f, forced = true)
inAir.set(false, forced = true)
}
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(drifting.get())
buffer.writeBoolean(clockwiseDrift.get())
buffer.writeFloat(boost.get())
buffer.writeFloat(turnMomentum.get())
buffer.writeBoolean(inAir.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
drifting.set(buffer.readBoolean(), forced = true)
clockwiseDrift.set(buffer.readBoolean(), forced = true)
boost.set(buffer.readFloat(), forced = true)
turnMomentum.set(buffer.readFloat(), forced = true)
inAir.set(buffer.readBoolean(), forced = true)
}
override fun toString(): String {
return "MinekartState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
}
override fun copy() = MinekartState().also {
it.rideVelocity.set(rideVelocity.get(), forced = true)
it.stamina.set(stamina.get(), forced = true)
it.currSpeed.set(currSpeed.get(), forced = true)
it.deltaRotation.set(deltaRotation.get(), forced = true)
it.drifting.set(drifting.get(), forced = true)
it.clockwiseDrift.set(clockwiseDrift.get(), forced = true)
it.boost.set(boost.get(), forced = true)
it.turnMomentum.set(turnMomentum.get(), forced = true)
it.inAir.set(inAir.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is MinekartState) return false
if (previous.drifting.get() != drifting.get()) return true
if (previous.clockwiseDrift.get() != clockwiseDrift.get()) return true
if (previous.boost.get() != boost.get()) return true
if (previous.turnMomentum.get() != turnMomentum.get()) return true
if (previous.inAir.get() != inAir.get()) return true
return super.shouldSync(previous)
}
}
@@ -8,8 +8,7 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.land
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.OrientationControllable
import com.cobblemon.mod.common.api.riding.RidingStyle
@@ -17,12 +16,10 @@ 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.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.core.Direction
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -31,15 +28,29 @@ 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.absoluteValue
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sign
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
companion object {
val KEY = cobblemonResource("land/vehicle")
val MAX_TOP_SPEED = 1.0 // 20 bl/s
val MIN_TOP_SPEED = 0.35 // 7 bl/s
//val MIN_SPEED = 0.25 // 5 bl/s
//Accel will lie between 1.0 second and 5.0 seconds
val MAX_ACCEL = (MAX_TOP_SPEED) / (20*3) //3.0 second to max speed
val MIN_ACCEL = (MAX_TOP_SPEED) / (20*8) // 8 seconds to max speed
//Can rotate 90 degrees
val MAX_HANDLING = 90.0
val MIN_HANDLING = 30.0
val MAX_YAW_HANDLING = 16.0
val MIN_YAW_HANDLING = 8.0
}
override val key = KEY
@@ -49,12 +60,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
}
val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
.with(PoseOption(PoseType.WALK) { _, state, _ ->
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
})
// val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
// .with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
.with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
@@ -81,38 +87,18 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: Player
): Float {
val topSpeed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.AIR, MIN_TOP_SPEED, MAX_TOP_SPEED)
val accel = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.AIR, MIN_ACCEL, MAX_ACCEL)
if (driver.isShiftKeyDown) {
state.drifting.set(true)
if(driver.zza != 0.0f) {
state.poweredDrifting.set(true)
} else {
state.poweredDrifting.set(false)
}
} else {
state.drifting.set(false)
state.poweredDrifting.set(false)
//speed up and slow down based on input
if (driver.zza > 0.0 && state.currSpeed.get() < topSpeed) {
state.currSpeed.set(min(state.currSpeed.get() + accel , topSpeed))
} else if (driver.zza < 0.0 && state.currSpeed.get() > 0.0) {
//Decelerate is now always a constant half of max acceleration.
state.currSpeed.set(max(state.currSpeed.get() - (MAX_ACCEL / 2), 0.0))
}
inAirCheck(state, vehicle)
return driver.deltaMovement.length().toFloat()
}
fun inAirCheck(
state: VehicleState,
vehicle: PokemonEntity,
) {
// Check both vertical movement and if there are blocks below.
val posBelow = vehicle.blockPosition().below()
val blockStateBelow = vehicle.level().getBlockState(posBelow)
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
val standingOnSolid = canSupportEntity && !isAirOrLiquid
// inAir if not on the ground
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
state.inAir.set(inAir)
return state.currSpeed.get().toFloat()
}
override fun rotation(
@@ -121,38 +107,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: LivingEntity
): Vec2 {
var newMomentum = state.turnMomentum.get().toDouble()
// Grab turningAcceleration and divide 20 twice to get
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
// Base boost stats off of normal turning stats
val driftMaxTurnMomentum = maxTurnMomentum * 3.0f
var driftTurnInput = turnInput * 0.4f
if(state.drifting.get() || state.inAir.get()) {
if( newMomentum < driftMaxTurnMomentum) {
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr))
newMomentum += driftTurnInput
}
newMomentum = lerp(newMomentum, 0.0, 0.03)
if( abs(newMomentum) < 0.05 ) {newMomentum = 0.0}
} else {
if(abs(newMomentum) <= maxTurnMomentum && turnInput != 0.0 ) {
newMomentum += turnInput
newMomentum.coerceIn(-maxTurnMomentum.toDouble(), maxTurnMomentum.toDouble())
} else {
newMomentum = lerp(newMomentum, 0.0, 0.15)
}
}
state.turnMomentum.set(newMomentum.toFloat())
driver.yRot += newMomentum.toFloat()
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
val rotationDegrees = driver.xxa * vehicle.runtime.resolveFloat(settings.rotationSpeed)
val rotation = Vec2(driver.xRot, vehicle.yRot - rotationDegrees)
state.deltaRotation.set(Vec2(rotation.x - driver.rotationVector.x, rotation.y - vehicle.rotationVector.y))
return rotation
}
override fun velocity(
@@ -162,106 +120,18 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
driver: Player,
input: Vec3
): Vec3 {
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
return state.rideVelocity.get()
}
private fun calculateRideSpaceVel(
settings: VehicleSettings,
state: VehicleState,
vehicle: PokemonEntity,
driver: Player
): Vec3 {
// Check to see if the ride should be walking or sprinting
//val walkSpeed = getWalkSpeed(vehicle)
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) * 0.3 * 3 * 2.0
//Flag for determining if player is actively inputting
var activeInput = false
var newVelocity = Vec3.ZERO
if (!state.drifting.get()) {
newVelocity = vehicle.deltaMovement
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
} else {
state
newVelocity = vehicle.deltaMovement
// Align the direction of movement with the world coordinate space so as not to modify it through turning
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
val runtime = vehicle.runtime
val driveFactor = runtime.resolveFloat(settings.driveFactor)
var g = driver.zza * driveFactor
if (g <= 0.0f) {
g *= runtime.resolveFloat(settings.reverseDriveFactor)
}
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.inAir.get()) {
//make sure it can't exceed top speed
val forwardInput = when {
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
driver.zza < 0 && newVelocity.z <= 0.0 -> 0.0
else -> driver.zza.sign
}
newVelocity = Vec3(
newVelocity.x,
newVelocity.y,
(newVelocity.z + (accel * forwardInput.toDouble())))
activeInput = true
if (driver.xxa != 0F && g.absoluteValue < runtime.resolveFloat(settings.minimumSpeedToTurn)) {
driver.xxa = 0F
}
val velocity = Vec3(0.0 /* Maybe do drifting here later */, 0.0, g.toDouble() * state.currSpeed.get())
// Gravity logic
if (vehicle.onGround()) {
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
} else {
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
//to do some testing and get other's opinions
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.25 * 3.0
val terminalVel = 2.0
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
}
//ground Friction
var friction = 0.003 * 2.0
if (state.drifting.get()) {friction *= 0.1}
if (!state.inAir.get()) {
newVelocity = newVelocity.subtract(
min(friction , abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
min(friction , abs(newVelocity.z)) * newVelocity.z.sign
)
// TEST FOR LATERAL SLIP OR WHATEVER
//TODO: calc lateral slip and friction better!
if (!state.drifting.get()) {
newVelocity = newVelocity.subtract(
min(friction * 10, abs(newVelocity.x)) * newVelocity.x.sign,
0.0,
0.0,
)
}
}
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
//Jump the thang!
if (driver.jumping && vehicle.onGround() && canJump) {
val jumpForce = 0.5
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
}
return newVelocity
return velocity
}
override fun angRollVel(
@@ -319,7 +189,10 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
driver: Player,
jumpStrength: Int
): Vec3 {
return Vec3.ZERO
val runtime = vehicle.runtime
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
val jumpVector = settings.jumpVector.map { runtime.resolveFloat(it) }
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
}
override fun gravity(
@@ -328,7 +201,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
regularGravity: Double
): Double {
return 0.0
return regularGravity
}
override fun rideFovMultiplier(
@@ -354,15 +227,11 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
vehicle: PokemonEntity,
driver: Player
): ResourceLocation {
when {
state.poweredDrifting.get() -> return cobblemonResource("powered_drifting")
state.drifting.get() -> return cobblemonResource("drifting")
}
return cobblemonResource("no_pose")
}
override fun inertia(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Double {
return 1.0
return 0.5
}
override fun shouldRoll(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
@@ -398,7 +267,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
state: VehicleState,
vehicle: PokemonEntity
): Boolean {
return true
return false
}
override fun getRideSounds(
@@ -418,6 +287,9 @@ class VehicleSettings : RidingBehaviourSettings {
var canJump = "true".asExpression()
private set
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
private set
var speed = "0.3".asExpression()
private set
@@ -427,40 +299,28 @@ class VehicleSettings : RidingBehaviourSettings {
var reverseDriveFactor = "0.25".asExpression()
private set
var minimumSpeedToTurn = "0.1".asExpression()
private set
var rotationSpeed = "45/20".asExpression()
private set
var lookYawLimit = "101".asExpression()
private set
var 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 =
"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 = "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 = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
private set
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)
rideSounds.encode(buffer)
buffer.writeExpression(canJump)
buffer.writeExpression(jumpVector[0])
buffer.writeExpression(jumpVector[1])
buffer.writeExpression(jumpVector[2])
buffer.writeExpression(speed)
buffer.writeExpression(driveFactor)
buffer.writeExpression(reverseDriveFactor)
buffer.writeExpression(minimumSpeedToTurn)
buffer.writeExpression(rotationSpeed)
buffer.writeExpression(lookYawLimit)
}
@@ -468,9 +328,15 @@ class VehicleSettings : RidingBehaviourSettings {
override fun decode(buffer: RegistryFriendlyByteBuf) {
rideSounds = RideSoundSettingsList.decode(buffer)
canJump = buffer.readExpression()
jumpVector = listOf(
buffer.readExpression(),
buffer.readExpression(),
buffer.readExpression()
)
speed = buffer.readExpression()
driveFactor = buffer.readExpression()
reverseDriveFactor = buffer.readExpression()
minimumSpeedToTurn = buffer.readExpression()
rotationSpeed = buffer.readExpression()
lookYawLimit = buffer.readExpression()
}
@@ -479,36 +345,11 @@ class VehicleSettings : RidingBehaviourSettings {
class VehicleState : RidingBehaviourState() {
var currSpeed = ridingState(0.0, Side.BOTH)
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
var drifting = ridingState(false, Side.CLIENT)
var poweredDrifting = ridingState(false, Side.CLIENT)
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
var inAir = ridingState(false, Side.CLIENT)
override fun reset() {
super.reset()
currSpeed.set(0.0, forced = true)
deltaRotation.set(Vec2.ZERO, forced = true)
drifting.set(false, forced = true)
poweredDrifting.set(false, forced = true)
turnMomentum.set(0.0f, forced = true)
inAir.set(false, forced = true)
}
override fun encode(buffer: FriendlyByteBuf) {
super.encode(buffer)
buffer.writeBoolean(drifting.get())
buffer.writeBoolean(poweredDrifting.get())
buffer.writeFloat(turnMomentum.get())
buffer.writeBoolean(inAir.get())
}
override fun decode(buffer: FriendlyByteBuf) {
super.decode(buffer)
drifting.set(buffer.readBoolean(), forced = true)
poweredDrifting.set(buffer.readBoolean(), forced = true)
turnMomentum.set(buffer.readFloat(), forced = true)
inAir.set(buffer.readBoolean(), forced = true)
}
override fun toString(): String {
@@ -520,18 +361,5 @@ class VehicleState : RidingBehaviourState() {
it.stamina.set(stamina.get(), forced = true)
it.currSpeed.set(currSpeed.get(), forced = true)
it.deltaRotation.set(deltaRotation.get(), forced = true)
it.drifting.set(drifting.get(), forced = true)
it.poweredDrifting.set(poweredDrifting.get(), forced = true)
it.turnMomentum.set(turnMomentum.get(), forced = true)
it.inAir.set(inAir.get(), forced = true)
}
override fun shouldSync(previous: RidingBehaviourState): Boolean {
if (previous !is VehicleState) return false
if (previous.drifting.get() != drifting.get()) return true
if (previous.poweredDrifting.get() != poweredDrifting.get()) return true
if (previous.turnMomentum.get() != turnMomentum.get()) return true
if (previous.inAir.get() != inAir.get()) return true
return super.shouldSync(previous)
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.OrientationControllable
@@ -20,7 +19,6 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
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.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.SmoothDouble
@@ -31,9 +29,7 @@ import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.Shapes
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin
import kotlin.math.sqrt
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
companion object {
@@ -47,7 +43,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
}
val poseProvider = PoseProvider<DolphinSettings, DolphinState>(PoseType.FLOAT)
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.deltaMovement.length() > 0.1 })
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
@@ -165,59 +161,11 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
deltaTime: Double
): Vec3 {
if (driver !is OrientationControllable) return Vec3.ZERO
val controller = (driver as OrientationControllable).orientationController
//TODO:hook up handling
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
//Interpret mouse input
val smoothingSpeed = 4.0
//Might need to add the smoothing here for default.
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
val rollRot = xInput
val pitchRot = yInput
// Roll
// if (abs(controller.pitch + rollRot) >= 89.5 ) {
// mouseXSmoother.reset()
// } else {
//
// }
controller.rotateRoll(rollRot.toFloat())
// Yaw
// TODO:move all magic numbers to handling expression
val yawSpeed = handling * deltaTime * 0.25
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 1.0 - min(sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), 0.5))
//Apply yaw globally as we don't want roll or pitch changes due to local yaw when looking up or down.
controller.applyGlobalYaw(yawForce.toFloat())
// Pitch
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
mouseYSmoother.reset()
} else {
//controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
controller.rotatePitch(pitchRot.toFloat())
}
//yaw, pitch, roll
return Vec3.ZERO
}
/*
* Normalizes the given value between a min and a max.
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
* and 1.0 represents x is at or above it.
*/
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
require(max > min) { "max must be greater than min" }
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
}
override fun canJump(
@@ -347,12 +295,6 @@ class DolphinSettings : RidingBehaviourSettings {
var strafeFactor = "0.2".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
override fun encode(buffer: RegistryFriendlyByteBuf) {
@@ -50,7 +50,7 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
this.looping = true
this.delay = 0
this.volume = 0.1f
this.attenuation = SoundInstance.Attenuation.LINEAR // This class performs its own attenuation
this.attenuation = SoundInstance.Attenuation.NONE // This class performs its own attenuation
if (this.isPassenger) this.relative = true
}
@@ -16,7 +16,6 @@ import net.minecraft.world.phys.Vec3
import org.joml.Matrix3f
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.sign
/**
* Class to handle the updating and containment of ride data used in animation.
@@ -34,7 +33,6 @@ class RidingAnimationData(val ride: PokemonEntity)
var rotSpring: Vec3Spring = Vec3Spring()
var rotDeltaSpring: Vec3Spring = Vec3Spring()
var localVelocitySpring: Vec3Spring = Vec3Spring()
var driverInputSpring: Vec3Spring = Vec3Spring()
fun update() {
val activeRide = ride.hasControllingPassenger()
@@ -138,14 +136,6 @@ class RidingAnimationData(val ride: PokemonEntity)
/******************************************************
* Update springs
*****************************************************/
val driver = ride.controllingPassenger
val vertInput = when {
driver != null && driver.jumping-> 1.0
driver != null && driver.isShiftKeyDown -> -1.0
else -> 0.0
}
val input = if(driver != null) Vec3(driver.xxa.toDouble() * -1, vertInput, driver.zza.toDouble()) else Vec3.ZERO
driverInputSpring.update(input, stiffness * 2, damping)
rotDeltaSpring.update(angDelta, stiffness, damping)
velocitySpring.update(currentVelocity, stiffness, damping)
rotSpring.update(currRot, stiffness, damping)
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
@@ -134,34 +133,25 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
// Configure what controls to show for each behaviour
when (currentBehaviourKey) {
BirdBehaviour.KEY -> {}
BoatBehaviour.KEY -> {}
BurstBehaviour.KEY -> {}
DolphinBehaviour.KEY -> {}
GliderBehaviour.KEY -> {}
HelicopterBehaviour.KEY -> {}
HorseBehaviour.KEY -> {
showVerticalMouse = false
disableHorizontalMovementKeys = true
showSneakKey = false
}
HoverBehaviour.KEY -> {
showVerticalMouse = false
}
BoatBehaviour.KEY -> {}
GliderBehaviour.KEY -> {}
HelicopterBehaviour.KEY -> {}
JetBehaviour.KEY -> {
showMovementKeys = false
showSneakKey = false
showJumpKey = false
}
MinekartBehaviour.KEY -> {
showVerticalMouse = false
showHorizontalMouse = false
showSneakKey = false
}
RocketBehaviour.KEY -> {
showVerticalMouse = false
showHorizontalMouse = false
}
BurstBehaviour.KEY -> {}
VehicleBehaviour.KEY -> {}
HoverBehaviour.KEY -> {
showVerticalMouse = false
}
}
val centerX = minecraft.window.guiScaledWidth / 2
@@ -1,29 +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.riding
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
import net.minecraft.client.Minecraft
import net.minecraft.client.player.RemotePlayer
object ClientboundUpdateDriverInputHandler : ClientNetworkPacketHandler<ClientboundUpdateDriverInputPacket> {
override fun handle(packet: ClientboundUpdateDriverInputPacket, client: Minecraft) {
client.executeIfPossible {
val level = client.level ?: return@executeIfPossible
val entity = level.getEntity(packet.entityId)
if (entity is RemotePlayer) {
entity.xxa = packet.driverInput.x
entity.zza = packet.driverInput.z
entity.jumping = packet.driverInput.y == 1.0f
entity.isShiftKeyDown = packet.driverInput.y == -1.0f
}
}
}
}
@@ -758,6 +758,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
yRot = Mth.lerp(state.getPartialTicks(), entity.yBodyRotO, entity.yBodyRot)
yRot = 180f - Mth.wrapDegrees(yRot)
}
//For some reason locators are positioned upside-down so this fixes that
matrixStack.mulPose(Axis.ZP.rotationDegrees(180f))
} else if (entity is EmptyPokeBallEntity) {
scale = 0.7F
yRot = Mth.lerp(state.getPartialTicks(), entity.yRot, entity.yRotO)
@@ -773,9 +775,6 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
matrixStack.scale(scale, scale, scale)
matrixStack.mulPose(Axis.YP.rotationDegrees(yRot))
//For some reason locators are positioned upside-down so this fixes that
matrixStack.mulPose(Axis.ZP.rotationDegrees(180f))
locatorAccess.update(matrixStack, entity, scale, state.locatorStates, isRoot = true)
}
@@ -255,36 +255,6 @@ abstract class PosableState : Schedulable {
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0))
}
.addFunction("input_right") { params ->
val lookBackTick = params.getInt(0)
val pokemon = getEntity() as? PokemonEntity ?: return@addFunction DoubleValue(0.0)
val partialTickZVel = pokemon.ridingAnimationData.driverInputSpring.getInterpolated(currentPartialTicks.toDouble(), lookBackTick).x
val topSpeed = 1.0//pokemon.riding.getController(pokemon)?.getStat(pokemon, RidingStat.SPEED)
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0) * -1.0)
}
.addFunction("input_up") { params ->
val lookBackTick = params.getInt(0)
val pokemon = getEntity() as? PokemonEntity ?: return@addFunction DoubleValue(0.0)
val partialTickZVel = pokemon.ridingAnimationData.driverInputSpring.getInterpolated(currentPartialTicks.toDouble(), lookBackTick).y
val topSpeed = 1.0//pokemon.riding.getController(pokemon)?.getStat(pokemon, RidingStat.SPEED)
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0))
}
.addFunction("input_forward") { params ->
val lookBackTick = params.getInt(0)
val pokemon = getEntity() as? PokemonEntity ?: return@addFunction DoubleValue(0.0)
val partialTickZVel = pokemon.ridingAnimationData.driverInputSpring.getInterpolated(currentPartialTicks.toDouble(), lookBackTick).z
val topSpeed = 1.0//pokemon.riding.getController(pokemon)?.getStat(pokemon, RidingStat.SPEED)
//?: return@addFunction DoubleValue(0.0)
return@addFunction DoubleValue((partialTickZVel / topSpeed).coerceIn(-1.0,1.0))
}
/** All of the MoLang functions that can be applied to something with this state. */
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class GolurkModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("golurk")
override val head = getPart("head")
override var portraitScale = 3.0F
override var portraitTranslation = Vec3(-0.3, 5.4, 0.0)
override var profileScale = 0.4F
override var profileTranslation = Vec3(0.0, 1.15, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
override val cryAnimation = CryProvider { bedrockStateful("golurk", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("golurk", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 20,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("golurk", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 20,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("golurk", "ground_walk")
)
)
sleep = registerPose(
poseName = "sleep",
transformTicks = 20,
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("golurk", "sleep"))
)
}
override fun getFaintAnimation(state: PosableState): ActiveAnimation? {
return if (state.isNotPosedIn(sleep)) bedrockStateful("golurk", "faint") else null
}
}
@@ -405,6 +405,7 @@ object VaryingModelRepository {
inbuilt("lombre", :: LombreModel)
inbuilt("ludicolo", :: LudicoloModel)
inbuilt("golett", :: GolettModel)
inbuilt("golurk", :: GolurkModel)
inbuilt("stantler", :: StantlerModel)
inbuilt("sneasel", :: SneaselModel)
inbuilt("weavile", :: WeavileModel)
@@ -18,6 +18,7 @@ enum class Category(val lang: String) {
Pokedex("pokedex"),
Storage("storage"),
Starter("starter"),
System("system"),
Interface("interface"),
Riding("riding"),
Debug("debug"),
@@ -16,8 +16,11 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
import com.cobblemon.mod.common.pokeball.catching.calculators.CobblemonCaptureCalculator
import com.cobblemon.mod.common.util.adapters.CaptureCalculatorAdapter
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
import com.cobblemon.mod.common.util.adapters.ZoneIdAdapter
import com.google.gson.GsonBuilder
import com.google.gson.annotations.SerializedName
import java.time.ZoneId
import java.time.ZoneOffset
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
@@ -29,6 +32,7 @@ class CobblemonConfig {
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(CaptureCalculator::class.java, CaptureCalculatorAdapter)
.registerTypeHierarchyAdapter(ZoneId::class.java, ZoneIdAdapter)
.create()
}
@@ -263,6 +267,9 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Pokedex, lang = "hide_unimplemented_pokemon_in_the_pokedex")
var hideUnimplementedPokemonInThePokedex = false
@CobblemonConfigField(Category.System, lang = "time_zone_id")
var timeZoneId: ZoneId = ZoneOffset.systemDefault()
@CobblemonConfigField(Category.Interface, lang = "summary_pokemon_follow_cursor")
var summaryPokemonFollowCursor = true
@@ -101,16 +101,9 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolut
import com.cobblemon.mod.common.pokemon.feature.StashHandler
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import com.mojang.serialization.Codec
import com.mojang.serialization.Dynamic
import java.util.Optional
import java.util.UUID
import java.util.concurrent.CompletableFuture
import kotlin.math.PI
import kotlin.math.ceil
import net.minecraft.client.Minecraft
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
@@ -165,7 +158,10 @@ import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
import kotlin.math.min
import java.util.*
import java.util.concurrent.CompletableFuture
import kotlin.math.PI
import kotlin.math.ceil
@Suppress("unused")
open class PokemonEntity(
@@ -305,7 +301,6 @@ open class PokemonEntity(
.also {
it.environment.query.addFunction("passenger_count") { DoubleValue(passengers.size.toDouble()) }
it.environment.query.addFunction("ride_velocity") { DoubleValue(getRideVelocity().length()) }
it.environment.query.addFunction("driver_input") { DoubleValue(min(ridingAnimationData.driverInputSpring.value.length(),1.0)) }
it.environment.query.addFunction("get_ride_stats") { params ->
val rideStat = RidingStat.valueOf(params.getString(0).uppercase())
val rideStyle = RidingStyle.valueOf(params.getString(1).uppercase())
@@ -565,12 +560,9 @@ open class PokemonEntity(
isPokemonFlying = flyDist - flyDistO > 0.005F
isPokemonWalking = walkDist - walkDistO > 0.005F
if (passengers.isNotEmpty() && level().isClientSide) {
if (passengers.isNotEmpty()) {
rideSoundManager.tick()
ridingAnimationData.update()
} else if (!passengers.isNotEmpty() && level().isClientSide)
{
rideSoundManager.stop()
}
flyDistO = flyDist
@@ -1774,8 +1766,8 @@ open class PokemonEntity(
}
// Rotate velocity vector to face the current y rotation
val f = Mth.sin(this.yRot.toRadians())
val g = Mth.cos(this.yRot.toRadians())
val f = Mth.sin(this.yRot * 0.017453292f)
val g = Mth.cos(this.yRot * 0.017453292f)
val v = Vec3(
inp.x * g.toDouble() - inp.z * f.toDouble(),
inp.y,
@@ -2136,9 +2128,8 @@ open class PokemonEntity(
}
}
this.yRotO = this.yRot
val rotation = behaviour.rotation(settings, state, this, driver)
this.yRotO = this.yRot
setRot(rotation.y, rotation.x)
this.yHeadRot = this.yRot
this.yBodyRot = this.yRot
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.orientation
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket.Companion.ID
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readMatrix3f
import com.cobblemon.mod.common.util.writeMatrix3f
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.world.entity.player.Player
import org.joml.Matrix3f
import org.joml.Vector3f
/**
* Packet sent from the server to update the current input from
* the driver of a ridden pokemon. This data is needed for client
* side animation data updates found in RidingAnimationData
*
* @author Jackowes
* @since August 1st, 2025
*/
class ClientboundUpdateDriverInputPacket internal constructor(
val driverInput: Vector3f,
val entityId: Int
) : NetworkPacket<ClientboundUpdateDriverInputPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVarInt(entityId)
buffer.writeVector3f(driverInput)
}
companion object {
val ID = cobblemonResource("s2c_update_driver_input")
fun decode(buffer: RegistryFriendlyByteBuf): ClientboundUpdateDriverInputPacket {
val entityId = buffer.readVarInt()
val driverInput = buffer.readVector3f()
return ClientboundUpdateDriverInputPacket(driverInput, entityId)
}
}
}
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.server.riding
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class ServerboundUpdateDriverInputPacket internal constructor(
val xxa: Float,
val zza: Float,
val jumping: Boolean,
val crouching: Boolean
) : NetworkPacket<ServerboundUpdateDriverInputPacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeFloat(xxa)
buffer.writeFloat(zza)
buffer.writeBoolean(jumping)
buffer.writeBoolean(crouching)
}
companion object {
val ID = cobblemonResource("c2s_update_driver_input")
fun decode(buffer: RegistryFriendlyByteBuf): ServerboundUpdateDriverInputPacket {
val xxa = buffer.readFloat()
val zza = buffer.readFloat()
val jumping = buffer.readBoolean()
val crouching = buffer.readBoolean()
return ServerboundUpdateDriverInputPacket(xxa, zza, jumping, crouching)
}
}
}
@@ -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.net.serverhandling.riding
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.duck.ServerEntityDuck
import com.cobblemon.mod.common.mixin.accessor.ChunkMapAccessor
import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import org.joml.Vector3f
import kotlin.math.sign
object DriverInputPacketHandler : ServerNetworkPacketHandler<ServerboundUpdateDriverInputPacket> {
override fun handle(
packet: ServerboundUpdateDriverInputPacket,
server: MinecraftServer,
player: ServerPlayer
) {
val chunkMap = player.serverLevel().chunkSource.chunkMap
val entityMap = (chunkMap as ChunkMapAccessor).entityMap
val trackedEntity = entityMap[player.id]
if (trackedEntity is TrackedEntityAccessor) {
val serverEntity = trackedEntity.getServerEntity()
if (serverEntity is ServerEntityDuck) {
val vertInput = when {
packet.jumping-> 1.0f
packet.crouching -> -1.0f
else -> 0.0f
}
val driverInput = Vector3f(packet.xxa.sign, vertInput, packet.zza.sign)
serverEntity.setDriverInput(driverInput)
}
}
}
}
@@ -93,13 +93,7 @@ class SentOutState() : ActivePokemonState() {
this.dimension = entity.level().dimension()
}
override fun getIcon(pokemon: Pokemon): ResourceLocation {
val isBeingRidden = (pokemon.entity?.countPlayerPassengers() ?: 0) > 0
val icon = if (isBeingRidden) "mounted" else "released"
return cobblemonResource("textures/gui/party/party_icon_${icon}.png")
}
override fun getIcon(pokemon: Pokemon) = cobblemonResource("textures/gui/party/party_icon_released.png")
override fun writeToNBT(nbt: CompoundTag) = null
override fun writeToJSON(json: JsonObject) = null
@@ -19,8 +19,6 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.RunStrategy
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.*
@@ -39,19 +37,15 @@ import java.lang.reflect.Type
object RidingBehaviourSettingsAdapter : JsonDeserializer<RidingBehaviourSettings?> {
val types: MutableMap<ResourceLocation, Class<out RidingBehaviourSettings>> = mutableMapOf(
BirdBehaviour.KEY to BirdSettings::class.java,
BirdNoRollBehaviour.KEY to BirdNoRollSettings::class.java,
DolphinBehaviour.KEY to DolphinSettings::class.java,
HorseBehaviour.KEY to HorseSettings::class.java,
BoatBehaviour.KEY to BoatSettings::class.java,
GliderBehaviour.KEY to GliderSettings::class.java,
HelicopterBehaviour.KEY to HelicopterSettings::class.java,
JetBehaviour.KEY to JetSettings::class.java,
JetNoRollBehaviour.KEY to JetNoRollSettings::class.java,
BurstBehaviour.KEY to BurstSettings::class.java,
VehicleBehaviour.KEY to VehicleSettings::class.java,
MinekartBehaviour.KEY to MinekartSettings::class.java,
HoverBehaviour.KEY to HoverSettings::class.java,
RocketBehaviour.KEY to RocketSettings::class.java,
CompositeBehaviour.KEY to CompositeSettings::class.java,
/*
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.util.adapters
import com.cobblemon.mod.common.Cobblemon
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
import java.time.ZoneId
import java.time.ZoneOffset
/**
* A type adapter for [ZoneOffset]s.
* This will never fail to deserialize and will instead default to the System's default time zone and log an error message.
*
* @author JPAK
* @since December 26, 2024
*/
object ZoneIdAdapter: JsonSerializer<ZoneId>, JsonDeserializer<ZoneId> {
override fun serialize(
src: ZoneId,
typeOfSrc: Type,
context: JsonSerializationContext
): JsonElement {
return JsonPrimitive(src.id)
}
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext
): ZoneId {
return try {
ZoneId.of(json.asString)
} catch (_: Exception) {
Cobblemon.LOGGER.error("Failed to load ZoneId from the ID {}, defaulting to the system time zone", json.asString)
ZoneOffset.systemDefault()
}
}
}
@@ -6,9 +6,9 @@
"bones": {
"body": {
"rotation": [
"45*math.clamp(q.riding.velocity_forward(0)* 0.5 + q.riding.input_forward(10), -1.0, 1.0)",
"45*q.riding.velocity_forward(0)",
"0 - Math.Sin(((q.anim_time + 0.1) * 180 * 0.5) * 1) * 5",
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 + 45*math.clamp(q.riding.velocity_right(0)* 0.5 + q.riding.input_right(10)* 0.5,-1.0,1.0)"
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 + 45*q.riding.velocity_right(0)"
],
"position": ["Math.Sin((q.anim_time * 180 * 0.5) * 1) * 1.5", "7+Math.Sin((q.anim_time * 90 * 0.5) * 1) * 1.5", 0]
},
@@ -50,9 +50,9 @@
"bones": {
"body": {
"rotation": [
"2.5 + 45*math.clamp(q.riding.velocity_forward(0) + q.riding.input_forward(5),-1.0,1.0)",
"2.5 + 45*q.riding.velocity_forward(0)",
"- Math.Sin(((q.anim_time + 0.1) * 180 * 0.5) * 1) * 5",
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 - 45*math.clamp(q.riding.velocity_right(0) + q.riding.right(5),-1.0,1.0)"
"Math.Sin(((q.anim_time + 0.4) * 180 * 0.5) * 1) * 5 - 45*q.riding.velocity_right(0)"
],
"position": ["Math.Sin((q.anim_time * 180 * 0.5) * 1) * 1.5", "7+Math.Sin((q.anim_time * 90 * 0.5) * 1) * 1.5", 0]
},
@@ -407,7 +407,7 @@
]
},
{
"name": "locator_seat_2",
"name": "locator_seat_1",
"parent": "body",
"pivot": [0, 0, 0],
"locators": {
@@ -4,11 +4,11 @@
{
"description": {
"identifier": "geometry.golurk",
"texture_width": 512,
"texture_height": 256,
"texture_width": 128,
"texture_height": 128,
"visible_bounds_width": 7,
"visible_bounds_height": 7.5,
"visible_bounds_offset": [0, 3.25, 0]
"visible_bounds_height": 4.5,
"visible_bounds_offset": [0, 1.75, 0]
},
"bones": [
{
@@ -18,743 +18,342 @@
{
"name": "body",
"parent": "golurk",
"pivot": [0, 61, 0]
},
{
"name": "shoulder_pivot",
"parent": "body",
"pivot": [0, 68.5, 0]
"pivot": [0, 38, 0]
},
{
"name": "torso",
"parent": "shoulder_pivot",
"pivot": [0, 44.5, 0],
"parent": "body",
"pivot": [0, 21.5, 0],
"cubes": [
{"origin": [-18, 45, -18], "size": [36, 32, 36], "uv": [0, 0]},
{"origin": [-18, 51, -18], "size": [36, 24, 7], "inflate": -0.25, "uv": [170, 194]}
]
},
{
"name": "chest_glow_red",
"parent": "torso",
"pivot": [0, 63, -14.5],
"cubes": [
{"origin": [-18, 51, -18], "size": [36, 24, 7], "inflate": -0.3, "uv": [170, 225]}
]
},
{
"name": "chest_glow_off",
"parent": "torso",
"pivot": [0, 63, -14.5],
"cubes": [
{"origin": [-18, 51, -18], "size": [36, 24, 7], "inflate": -0.3, "uv": [170, 163]}
]
},
{
"name": "waist",
"parent": "torso",
"pivot": [0, 45.25, 0]
},
{
"name": "skirt",
"parent": "waist",
"pivot": [0, 45.25, 0],
"cubes": [
{"origin": [-14, 41, -14], "size": [28, 8, 28], "uv": [0, 96]},
{"origin": [-14, 41, -0.5], "size": [28, 3, 1], "inflate": 0.01, "pivot": [0, 42.5, 0], "rotation": [0, -90, 0], "uv": [109, 192]},
{"origin": [-14, 41, -0.5], "size": [28, 3, 1], "inflate": 0.01, "uv": [109, 197]},
{"origin": [-17, 34, -17], "size": [34, 7, 34], "uv": [0, 212]},
{"origin": [-17, 34, -0.5], "size": [34, 7, 1], "inflate": 0.01, "pivot": [0, 37.5, 0], "rotation": [0, -90, 0], "uv": [177, 145]},
{"origin": [-17, 34, -0.5], "size": [34, 7, 1], "inflate": 0.01, "uv": [177, 154]}
]
},
{
"name": "fire_waist_base",
"parent": "skirt",
"pivot": [0, 34.05, 0],
"cubes": [
{"origin": [-12, 34.05, -12], "size": [24, 0, 24], "uv": [244, 1]}
]
},
{
"name": "fire_waist",
"parent": "skirt",
"pivot": [0, 38.25, 0]
},
{
"name": "fire_waist_interior",
"parent": "fire_waist",
"pivot": [0, 38.25, 0],
"cubes": [
{"origin": [-16, 4.25, 0], "size": [32, 34, 0], "pivot": [0, 32.25, 0], "rotation": [0, 45, 0], "uv": [447, 35]}
]
},
{
"name": "fire_waist_interior2",
"parent": "fire_waist",
"pivot": [0, 38.25, 0],
"cubes": [
{"origin": [0, 4.25, -16], "size": [0, 34, 32], "pivot": [0, 32.25, 0], "rotation": [0, 45, 0], "uv": [447, -31]}
]
},
{
"name": "fire_waist_front",
"parent": "fire_waist",
"pivot": [0, 34, -12],
"rotation": [-10, 0, 0],
"cubes": [
{"origin": [-16, 8.25, -12], "size": [32, 30, 0], "uv": [317, 1]}
]
},
{
"name": "fire_waist_back",
"parent": "fire_waist",
"pivot": [0, 34, 12],
"rotation": [10, 0, 0],
"cubes": [
{"origin": [-16, 8.25, 12], "size": [32, 30, 0], "uv": [317, 32]}
]
},
{
"name": "fire_waist_right",
"parent": "fire_waist",
"pivot": [-12, 34, 0],
"rotation": [0, 0, 10],
"cubes": [
{"origin": [-12, 8.25, -16], "size": [0, 30, 32], "uv": [382, -31]}
]
},
{
"name": "fire_waist_left",
"parent": "fire_waist",
"pivot": [12, 34, 0],
"rotation": [0, 0, -10],
"cubes": [
{"origin": [12, 8.25, -16], "size": [0, 30, 32], "uv": [382, 0]}
]
},
{
"name": "bandage",
"parent": "torso",
"pivot": [-3.8, 64.6, -19],
"rotation": [0, 0, 45],
"cubes": [
{"origin": [-11.8, 60.6, -20], "size": [16, 8, 2], "uv": [119, 132]}
{"origin": [-9, 19, -9], "size": [18, 5, 18], "uv": [0, 40]},
{"origin": [-7.5, 24, -7.5], "size": [15, 2, 15], "uv": [54, 40]},
{"origin": [-10, 26, -10], "size": [20, 20, 20], "uv": [0, 0]},
{"origin": [-8, 33, -11], "size": [11, 5, 1], "pivot": [-1.5, 35.5, -10.5], "rotation": [0, 0, 45], "uv": [98, 11]}
]
},
{
"name": "head",
"parent": "torso",
"pivot": [0, 76, 0],
"pivot": [0.5, 46, 0.5],
"cubes": [
{"origin": [-5, 74, -5], "size": [10, 10, 10], "uv": [136, 142]},
{"origin": [-1.5, 82, -1], "size": [4, 5, 2], "pivot": [1, 84.5, 0.5], "rotation": [0, 0, 10], "uv": [19, 20]},
{"origin": [-5.5, 84, 0], "size": [4, 3, 0], "pivot": [1, 84.5, 0.5], "rotation": [0, 0, 10], "uv": [26, 27]}
{"origin": [-3, 44.75, -3], "size": [6, 6, 6], "uv": [83, 99]},
{"origin": [-4.5, 49.5, 0.5], "size": [6, 5, 0], "pivot": [1, 50.5, 0.5], "rotation": [0, 0, 10], "uv": [47, 63]}
]
},
{
"name": "eyes",
"name": "eye_left",
"parent": "head",
"pivot": [0, 80, -5.01]
"pivot": [1.4, 47.5, -2.51],
"cubes": [
{"origin": [0.65, 46.5, -3.01], "size": [1, 3, 1], "uv": [15, 12]},
{"origin": [1.15, 46.5, -3.01], "size": [1, 1, 1], "uv": [4, 18]}
]
},
{
"name": "eye_left",
"parent": "eyes",
"pivot": [2, 80, -5.01],
"name": "eyelid_left",
"parent": "eye_left",
"pivot": [1.4, 47.5, -2.41],
"cubes": [
{"origin": [1, 78, -5.01], "size": [2, 4, 0], "uv": [32, 0]},
{"origin": [3, 78, -5.01], "size": [1, 2, 0], "uv": [24, 2]}
{"origin": [0.65, 46.5, -2.91], "size": [1, 3, 1], "inflate": 0.001, "uv": [15, 0]},
{"origin": [1.15, 46.5, -2.91], "size": [1, 1, 1], "inflate": 0.001, "uv": [0, 18]}
]
},
{
"name": "eye_right",
"parent": "eyes",
"pivot": [-2, 80, -5.01],
"cubes": [
{"origin": [-3, 78, -5.01], "size": [2, 4, 0], "uv": [28, 0]}
]
},
{
"name": "eyes_red",
"parent": "head",
"pivot": [0, 80, -4.91]
},
{
"name": "eye_red_left",
"parent": "eyes_red",
"pivot": [2, 80, -4.91],
"pivot": [-1.65, 48.25, -2.51],
"cubes": [
{"origin": [1, 78, -4.91], "size": [2, 4, 0], "uv": [32, 4]},
{"origin": [3, 78, -4.91], "size": [1, 2, 0], "uv": [2, 2]}
{"origin": [-1.65, 46.5, -3.01], "size": [1, 3, 1], "uv": [0, 12]}
]
},
{
"name": "eye_red_right",
"parent": "eyes_red",
"pivot": [-2, 80, -4.91],
"name": "eyelid_right",
"parent": "eye_right",
"pivot": [-1.15, 48, -2.41],
"cubes": [
{"origin": [-3, 78, -4.91], "size": [2, 4, 0], "uv": [28, 4]}
]
},
{
"name": "eyes_off",
"parent": "head",
"pivot": [0, 80, -4.91]
},
{
"name": "eye_off_left",
"parent": "eyes_off",
"pivot": [2, 80, -4.91],
"cubes": [
{"origin": [1, 78, -4.91], "size": [2, 4, 0], "uv": [32, 8]},
{"origin": [3, 78, -4.91], "size": [1, 2, 0], "uv": [2, 22]}
]
},
{
"name": "eye_off_right",
"parent": "eyes_off",
"pivot": [-2, 80, -4.91],
"cubes": [
{"origin": [-3, 78, -4.91], "size": [2, 4, 0], "uv": [28, 8]}
]
},
{
"name": "shoulder_left",
"parent": "torso",
"pivot": [25.6, 67, 0],
"cubes": [
{"origin": [19.1, 76.4, -2.5], "size": [9, 5, 5], "pivot": [25.6, 68.4, 0], "rotation": [0, 0, 45], "uv": [102, 236]},
{"origin": [22.1, 81.4, -2.5], "size": [6, 5, 5], "pivot": [25.6, 68.4, 0], "rotation": [0, 0, 45], "uv": [177, 132]},
{"origin": [16.1, 59.4, -8.5], "size": [17, 17, 17], "pivot": [25.6, 68.4, 0], "rotation": [0, 0, 45], "uv": [68, 132]},
{"origin": [19.1, 62.4, -10.5], "size": [11, 11, 2], "pivot": [25.6, 68.4, -2], "rotation": [0, 0, 45], "uv": [0, 109]},
{"origin": [19.1, 62.4, 8.5], "size": [11, 11, 2], "pivot": [25.6, 68.4, 17], "rotation": [0, 0, 45], "uv": [46, 191]}
]
},
{
"name": "shoulder_glow_red_left",
"parent": "shoulder_left",
"pivot": [25.6, 68.4, -10.4],
"cubes": [
{"origin": [20.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [25.6, 68.4, -1.9], "rotation": [0, 0, 45], "uv": [200, 122]}
]
},
{
"name": "shoulder_glow_off_left",
"parent": "shoulder_left",
"pivot": [25.6, 68.4, -10.4],
"cubes": [
{"origin": [20.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [25.6, 68.4, -1.9], "rotation": [0, 0, 45], "uv": [200, 131]}
{"origin": [-1.65, 46.5, -2.91], "size": [1, 3, 1], "inflate": 0.001, "uv": [0, 0]}
]
},
{
"name": "arm_left",
"parent": "shoulder_left",
"pivot": [24.1, 59.4, 0],
"cubes": [
{"origin": [20.1, 50.4, -3], "size": [8, 14, 6], "uv": [108, 0]}
]
"parent": "torso",
"pivot": [13.6, 44, 0]
},
{
"name": "forearm_left",
"name": "arm_left2",
"parent": "arm_left",
"pivot": [24.1, 52.4, 0],
"pivot": [16.1, 43.9, 0],
"cubes": [
{"origin": [16.6, 47.4, -8], "size": [15, 5, 16], "uv": [62, 191]},
{"origin": [13.6, 39.9, -10], "size": [21, 8, 20], "uv": [0, 68]}
]
},
{
"name": "fire_hand_base_left",
"parent": "forearm_left",
"pivot": [24.1, 39.95, 0],
"cubes": [
{"origin": [16.1, 39.95, -8], "size": [16, 0, 16], "uv": [326, 124], "mirror": true}
]
},
{
"name": "fire_hand_left",
"parent": "forearm_left",
"pivot": [24.1, 42.15, 0]
},
{
"name": "fire_hand_interior_left1",
"parent": "fire_hand_left",
"pivot": [24.1, 44.15, 0],
"cubes": [
{"origin": [12.6, 17.15, 0], "size": [23, 25, 0], "pivot": [24.1, 38.15, 0], "rotation": [0, -45, 0], "uv": [465, 150], "mirror": true}
]
},
{
"name": "fire_hand_interior_left2",
"parent": "fire_hand_left",
"pivot": [24.1, 44.15, 0],
"cubes": [
{"origin": [24.1, 17.15, -11.5], "size": [0, 25, 23], "pivot": [24.1, 38.15, 0], "rotation": [0, -45, 0], "uv": [465, 101], "mirror": true}
]
},
{
"name": "fire_hand_front_left",
"parent": "fire_hand_left",
"pivot": [24.1, 39.9, -8],
"rotation": [-10, 0, 0],
"cubes": [
{"origin": [13.1, 20.15, -8], "size": [22, 22, 0], "uv": [375, 124], "mirror": true}
]
},
{
"name": "fire_hand_back_left",
"parent": "fire_hand_left",
"pivot": [24.1, 39.9, 8],
"rotation": [10, 0, 0],
"cubes": [
{"origin": [13.1, 20.15, 8], "size": [22, 22, 0], "uv": [375, 147], "mirror": true}
]
},
{
"name": "fire_hand_outer_left",
"parent": "fire_hand_left",
"pivot": [32.1, 39.9, 0],
"rotation": [0, 0, -10],
"cubes": [
{"origin": [32.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 102], "mirror": true}
]
},
{
"name": "fire_hand_inner_left",
"parent": "fire_hand_left",
"pivot": [16.1, 39.9, 0],
"rotation": [0, 0, 10],
"cubes": [
{"origin": [16.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 125], "mirror": true}
{"origin": [11.1, 40.9, 4.5], "size": [7, 7, 2], "pivot": [13.6, 45.4, -1], "rotation": [0, 0, 45], "uv": [107, 17]},
{"origin": [4.6, 41.4, -2.5], "size": [5, 5, 5], "pivot": [13.6, 45.4, 0], "rotation": [0, 0, 45], "uv": [102, 106]},
{"origin": [11.1, 40.9, -6.5], "size": [7, 7, 2], "pivot": [13.6, 45.4, -1], "rotation": [0, 0, 45], "uv": [29, 63]},
{"origin": [9.6, 39.4, -4.5], "size": [10, 10, 9], "pivot": [13.6, 45.4, 0], "rotation": [0, 0, 45], "uv": [0, 63]},
{"origin": [16.6, 41.4, -2.5], "size": [9, 5, 5], "uv": [99, 42]}
]
},
{
"name": "hand_left",
"parent": "forearm_left",
"pivot": [24.1, 39.9, 0],
"parent": "arm_left2",
"pivot": [24.85, 44, 0],
"cubes": [
{"origin": [19.6, 27.9, -8], "size": [9, 12, 16], "inflate": 0.02, "uv": [186, 0]},
{"origin": [27.6, 28.9, -3.3], "size": [3, 9, 9], "pivot": [26.1, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [102, 218]}
{"origin": [26.6, 38.4, -5.5], "size": [5, 11, 11], "uv": [38, 68]},
{"origin": [23.6, 39.4, -4.5], "size": [3, 9, 9], "uv": [0, 96]}
]
},
{
"name": "hand_glow_red_left",
"name": "hand_left2",
"parent": "hand_left",
"pivot": [30.5, 34.9, 0.7],
"pivot": [31.85, 42.5, 0],
"cubes": [
{"origin": [30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [238, 99]}
{"origin": [32.25, 46.9, -2.75], "size": [5, 1, 5], "pivot": [34.5, 47.4, 0], "rotation": [0, -45, 0], "uv": [101, 99]},
{"origin": [31.1, 40.9, -4.5], "size": [7, 6, 9], "uv": [27, 90]}
]
},
{
"name": "hand_glow_off_left",
"parent": "hand_left",
"pivot": [30.5, 34.9, 0.7],
"name": "finger_left",
"parent": "hand_left2",
"pivot": [38.1, 46.4, -3.5],
"cubes": [
{"origin": [30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [238, 106]}
{"origin": [38.1, 44.4, -4.5], "size": [3, 2, 2], "inflate": 0.001, "uv": [92, 32]}
]
},
{
"name": "finger_thumb_left",
"parent": "hand_left",
"pivot": [19.6, 34.9, -8],
"rotation": [-12.5, 0, 25],
"name": "finger_left2",
"parent": "finger_left",
"pivot": [41.1, 46.4, -3.5],
"cubes": [
{"origin": [16.6, 29.9, -10.5], "size": [6, 9, 7], "uv": [108, 20]}
{"origin": [41.1, 44.4, -4.5], "size": [2, 2, 2], "uv": [60, 94]}
]
},
{
"name": "finger_thumb_left2",
"parent": "finger_thumb_left",
"pivot": [19.1, 29.9, -10.5],
"name": "finger_left3",
"parent": "hand_left2",
"pivot": [38.1, 46.4, -1.25],
"cubes": [
{"origin": [17.1, 23.9, -10.5], "size": [5, 6, 5], "uv": [0, 68]}
{"origin": [38.1, 44.4, -2.25], "size": [3, 2, 2], "inflate": 0.001, "uv": [86, 84]}
]
},
{
"name": "finger_index_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, -6],
"name": "finger_left4",
"parent": "finger_left3",
"pivot": [41.1, 46.4, -1.25],
"cubes": [
{"origin": [21.6, 21.9, -8], "size": [6, 6, 4], "inflate": 0.015, "uv": [132, 122]}
{"origin": [41.1, 44.4, -2.25], "size": [3, 2, 2], "uv": [27, 86]}
]
},
{
"name": "finger_index_left2",
"parent": "finger_index_left",
"pivot": [27.1, 21.9, -6],
"name": "finger_left5",
"parent": "hand_left2",
"pivot": [38.1, 46.4, 1],
"cubes": [
{"origin": [22.1, 15.9, -8], "size": [5, 6, 4], "uv": [154, 96]}
{"origin": [38.1, 44.4, 0], "size": [3, 2, 2], "inflate": 0.001, "uv": [27, 82]}
]
},
{
"name": "finger_middle_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, -2],
"name": "finger_left6",
"parent": "finger_left5",
"pivot": [41.1, 46.4, 1],
"cubes": [
{"origin": [21.6, 21.9, -4], "size": [6, 6, 4], "inflate": 0.01, "uv": [221, 29]}
{"origin": [41.1, 44.4, 0], "size": [2, 2, 2], "uv": [90, 36]}
]
},
{
"name": "finger_middle_left2",
"parent": "finger_middle_left",
"pivot": [27.1, 21.9, -2],
"name": "finger_left7",
"parent": "hand_left2",
"pivot": [38.1, 46.4, 3.25],
"cubes": [
{"origin": [22.1, 15.9, -4], "size": [5, 6, 4], "uv": [62, 78]}
{"origin": [38.1, 44.4, 2.25], "size": [2, 2, 2], "inflate": 0.001, "uv": [0, 86]}
]
},
{
"name": "finger_ring_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, 2],
"name": "finger_left8",
"parent": "finger_left7",
"pivot": [40.1, 46.4, 3.25],
"cubes": [
{"origin": [21.6, 21.9, 0], "size": [6, 6, 4], "inflate": 0.015, "uv": [172, 122]}
{"origin": [40.1, 44.4, 2.25], "size": [2, 2, 2], "uv": [0, 82]}
]
},
{
"name": "finger_ring_left2",
"parent": "finger_ring_left",
"pivot": [27.1, 21.9, 2],
"name": "finger_left9",
"parent": "hand_left2",
"pivot": [35.6, 43.15, -3.5],
"cubes": [
{"origin": [22.1, 15.9, 0], "size": [5, 6, 4], "uv": [62, 68]}
{"origin": [35.1, 41.65, -5.5], "size": [3, 3, 3], "inflate": 0.001, "uv": [50, 90]}
]
},
{
"name": "finger_pinky_left",
"parent": "hand_left",
"pivot": [26.6, 27.9, 6],
"name": "finger_left10",
"parent": "finger_left9",
"pivot": [38.1, 43.15, -5.5],
"cubes": [
{"origin": [21.6, 21.9, 4], "size": [6, 6, 4], "inflate": 0.01, "uv": [226, 96]}
]
},
{
"name": "finger_pinky_left2",
"parent": "finger_pinky_left",
"pivot": [27.1, 21.9, 6],
"cubes": [
{"origin": [22.1, 16.9, 4], "size": [5, 5, 4], "uv": [2, 79]}
]
},
{
"name": "hand_fire_left",
"parent": "forearm_left",
"pivot": [24, 40.25, 0]
},
{
"name": "shoulder_right",
"parent": "torso",
"pivot": [-25.6, 67, 0],
"cubes": [
{"origin": [-28.1, 76.4, -2.5], "size": [9, 5, 5], "pivot": [-25.6, 68.4, 0], "rotation": [0, 0, -45], "uv": [0, 236]},
{"origin": [-28.1, 81.4, -2.5], "size": [6, 5, 5], "pivot": [-25.6, 68.4, 0], "rotation": [0, 0, -45], "uv": [155, 132]},
{"origin": [-33.1, 59.4, -8.5], "size": [17, 17, 17], "pivot": [-25.6, 68.4, 0], "rotation": [0, 0, -45], "uv": [0, 132]},
{"origin": [-30.1, 62.4, -10.5], "size": [11, 11, 2], "pivot": [-25.6, 68.4, -2], "rotation": [0, 0, -45], "uv": [0, 96]},
{"origin": [-30.1, 62.4, 8.5], "size": [11, 11, 2], "pivot": [-25.6, 68.4, 17], "rotation": [0, 0, -45], "uv": [51, 132]}
]
},
{
"name": "shoulder_glow_red_right",
"parent": "shoulder_right",
"pivot": [-25.6, 68.4, -10.4],
"cubes": [
{"origin": [-29.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [-25.6, 68.4, -1.9], "rotation": [0, 0, -45], "uv": [218, 122]}
]
},
{
"name": "shoulder_glow_off_right",
"parent": "shoulder_right",
"pivot": [-25.6, 68.4, -10.4],
"cubes": [
{"origin": [-29.1, 63.4, -10.41], "size": [9, 9, 0], "pivot": [-25.6, 68.4, -1.9], "rotation": [0, 0, -45], "uv": [218, 131]}
{"origin": [38.1, 41.65, -5.5], "size": [3, 3, 3], "uv": [89, 0]}
]
},
{
"name": "arm_right",
"parent": "shoulder_right",
"pivot": [-24.1, 59.4, 0],
"cubes": [
{"origin": [-28.1, 50.4, -3], "size": [8, 14, 6], "uv": [0, 0]}
]
"parent": "torso",
"pivot": [-13.6, 44, 0]
},
{
"name": "forearm_right",
"name": "arm_right2",
"parent": "arm_right",
"pivot": [-24.1, 52.4, 0],
"pivot": [-16.1, 43.9, 0],
"cubes": [
{"origin": [-31.6, 47.4, -8], "size": [15, 5, 16], "uv": [0, 191]},
{"origin": [-34.6, 39.9, -10], "size": [21, 8, 20], "uv": [82, 68]}
]
},
{
"name": "fire_hand_base_right",
"parent": "forearm_right",
"pivot": [-24.1, 39.95, 0],
"cubes": [
{"origin": [-32.1, 39.95, -8], "size": [16, 0, 16], "uv": [326, 71]}
]
},
{
"name": "fire_hand_right",
"parent": "forearm_right",
"pivot": [-24.1, 42.15, 0]
},
{
"name": "fire_hand_interior_right1",
"parent": "fire_hand_right",
"pivot": [-24.1, 44.15, 0],
"cubes": [
{"origin": [-35.6, 17.15, 0], "size": [23, 25, 0], "pivot": [-24.1, 38.15, 0], "rotation": [0, 45, 0], "uv": [465, 97]}
]
},
{
"name": "fire_hand_interior_right2",
"parent": "fire_hand_right",
"pivot": [-24.1, 44.15, 0],
"cubes": [
{"origin": [-24.1, 17.15, -11.5], "size": [0, 25, 23], "pivot": [-24.1, 38.15, 0], "rotation": [0, 45, 0], "uv": [465, 48]}
]
},
{
"name": "fire_hand_front_right",
"parent": "fire_hand_right",
"pivot": [-24.1, 39.9, -8],
"rotation": [-10, 0, 0],
"cubes": [
{"origin": [-35.1, 20.15, -8], "size": [22, 22, 0], "uv": [375, 71]}
]
},
{
"name": "fire_hand_back_right",
"parent": "fire_hand_right",
"pivot": [-24.1, 39.9, 8],
"rotation": [10, 0, 0],
"cubes": [
{"origin": [-35.1, 20.15, 8], "size": [22, 22, 0], "uv": [375, 94]}
]
},
{
"name": "fire_hand_outer_right",
"parent": "fire_hand_right",
"pivot": [-32.1, 39.9, 0],
"rotation": [0, 0, 10],
"cubes": [
{"origin": [-32.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 49]}
]
},
{
"name": "fire_hand_inner_right",
"parent": "fire_hand_right",
"pivot": [-16.1, 39.9, 0],
"rotation": [0, 0, -10],
"cubes": [
{"origin": [-16.1, 20.15, -11], "size": [0, 22, 22], "uv": [420, 72]}
{"origin": [-25.6, 41.4, -2.5], "size": [9, 5, 5], "uv": [99, 32]},
{"origin": [-19.6, 39.4, -4.5], "size": [10, 10, 9], "pivot": [-13.6, 45.4, 0], "rotation": [0, 0, -45], "uv": [60, 0]},
{"origin": [-18.1, 40.9, -6.5], "size": [7, 7, 2], "pivot": [-13.6, 45.4, -1], "rotation": [0, 0, -45], "uv": [0, 40]},
{"origin": [-9.6, 41.4, -2.5], "size": [5, 5, 5], "pivot": [-13.6, 45.4, 0], "rotation": [0, 0, -45], "uv": [44, 105]},
{"origin": [-18.1, 40.9, 4.5], "size": [7, 7, 2], "pivot": [-13.6, 45.4, -1], "rotation": [0, 0, -45], "uv": [0, 49]}
]
},
{
"name": "hand_right",
"parent": "forearm_right",
"pivot": [-24.1, 39.9, 0],
"parent": "arm_right2",
"pivot": [-24.85, 44, 0],
"cubes": [
{"origin": [-28.6, 27.9, -8], "size": [9, 12, 16], "inflate": 0.02, "uv": [136, 0]},
{"origin": [-30.6, 28.9, -3.3], "size": [3, 9, 9], "pivot": [-26.1, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [0, 218]}
{"origin": [-31.6, 38.4, -5.5], "size": [5, 11, 11], "uv": [61, 57]},
{"origin": [-26.6, 39.4, -4.5], "size": [3, 9, 9], "uv": [59, 92]}
]
},
{
"name": "hand_glow_red_right",
"name": "hand_right2",
"parent": "hand_right",
"pivot": [-30.5, 34.9, 0.7],
"pivot": [-31.85, 42.5, 0],
"cubes": [
{"origin": [-30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [-26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [224, 99]}
{"origin": [-37.25, 46.9, -2.75], "size": [5, 1, 5], "pivot": [-34.5, 47.4, 0], "rotation": [0, 45, 0], "uv": [0, 12]},
{"origin": [-38.1, 40.9, -4.5], "size": [7, 6, 9], "uv": [88, 84]}
]
},
{
"name": "hand_glow_off_right",
"parent": "hand_right",
"pivot": [-30.5, 34.9, 0.7],
"name": "finger_right",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, -3.5],
"cubes": [
{"origin": [-30.51, 29.9, -2.3], "size": [0, 7, 7], "pivot": [-26, 34.9, 0.7], "rotation": [-45, 0, 0], "uv": [224, 106]}
{"origin": [-41.1, 44.4, -4.5], "size": [3, 2, 2], "inflate": 0.001, "uv": [80, 23]}
]
},
{
"name": "finger_thumb_right",
"parent": "hand_right",
"pivot": [-19.6, 34.9, -8],
"rotation": [-12.5, 0, -25],
"name": "finger_right2",
"parent": "finger_right",
"pivot": [-41.1, 46.4, -3.5],
"cubes": [
{"origin": [-22.6, 29.9, -10.5], "size": [6, 9, 7], "uv": [0, 20]}
{"origin": [-43.1, 44.4, -4.5], "size": [2, 2, 2], "uv": [0, 67]}
]
},
{
"name": "finger_thumb_right2",
"parent": "finger_thumb_right",
"pivot": [-19.1, 29.9, -10.5],
"name": "finger_right3",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, -1.25],
"cubes": [
{"origin": [-22.1, 23.9, -10.5], "size": [5, 6, 5], "uv": [82, 68]}
{"origin": [-41.1, 44.4, -2.25], "size": [3, 2, 2], "inflate": 0.001, "uv": [80, 19]}
]
},
{
"name": "finger_index_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, -6],
"name": "finger_right4",
"parent": "finger_right3",
"pivot": [-41.1, 46.4, -1.25],
"cubes": [
{"origin": [-27.6, 21.9, -8], "size": [6, 6, 4], "inflate": 0.015, "uv": [112, 122]}
{"origin": [-44.1, 44.4, -2.25], "size": [3, 2, 2], "uv": [38, 72]}
]
},
{
"name": "finger_index_right2",
"parent": "finger_index_right",
"pivot": [-27.1, 21.9, -6],
"name": "finger_right5",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, 1],
"cubes": [
{"origin": [-27.1, 15.9, -8], "size": [5, 6, 4], "uv": [136, 96]}
{"origin": [-41.1, 44.4, 0], "size": [3, 2, 2], "inflate": 0.001, "uv": [59, 63]}
]
},
{
"name": "finger_middle_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, -2],
"name": "finger_right6",
"parent": "finger_right5",
"pivot": [-41.1, 46.4, 1],
"cubes": [
{"origin": [-27.6, 21.9, -4], "size": [6, 6, 4], "inflate": 0.01, "uv": [177, 29]}
{"origin": [-43.1, 44.4, 0], "size": [2, 2, 2], "uv": [0, 63]}
]
},
{
"name": "finger_middle_right2",
"parent": "finger_middle_right",
"pivot": [-27.1, 21.9, -2],
"name": "finger_right7",
"parent": "hand_right2",
"pivot": [-38.1, 46.4, 3.25],
"cubes": [
{"origin": [-27.1, 15.9, -4], "size": [5, 6, 4], "uv": [144, 78]}
{"origin": [-40.1, 44.4, 2.25], "size": [2, 2, 2], "inflate": 0.001, "uv": [60, 4]}
]
},
{
"name": "finger_ring_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, 2],
"name": "finger_right8",
"parent": "finger_right7",
"pivot": [-40.1, 46.4, 3.25],
"cubes": [
{"origin": [-27.6, 21.9, 0], "size": [6, 6, 4], "inflate": 0.015, "uv": [152, 122]}
{"origin": [-42.1, 44.4, 2.25], "size": [2, 2, 2], "uv": [60, 0]}
]
},
{
"name": "finger_ring_right2",
"parent": "finger_ring_right",
"pivot": [-27.1, 21.9, 2],
"name": "finger_right9",
"parent": "hand_right2",
"pivot": [-35.6, 43.15, -3.5],
"cubes": [
{"origin": [-27.1, 15.9, 0], "size": [5, 6, 4], "uv": [144, 68]}
{"origin": [-38.1, 41.65, -5.5], "size": [3, 3, 3], "inflate": 0.001, "uv": [80, 32]}
]
},
{
"name": "finger_pinky_right",
"parent": "hand_right",
"pivot": [-26.6, 27.9, 6],
"name": "finger_right10",
"parent": "finger_right9",
"pivot": [-38.1, 43.15, -5.5],
"cubes": [
{"origin": [-27.6, 21.9, 4], "size": [6, 6, 4], "inflate": 0.01, "uv": [206, 96]}
{"origin": [-41.1, 41.65, -5.5], "size": [3, 3, 3], "uv": [54, 49]}
]
},
{
"name": "finger_pinky_right2",
"parent": "finger_pinky_right",
"pivot": [-27.1, 21.9, 6],
"cubes": [
{"origin": [-27.1, 16.9, 4], "size": [5, 5, 4], "uv": [84, 79]}
]
},
{
"name": "hand_fire_right",
"parent": "forearm_right",
"pivot": [-24, 40.25, 0]
},
{
"name": "seat_1_locator",
"parent": "torso",
"pivot": [0, 77, 9],
"locators": {
"seat_1": [0, 77, 10]
}
},
{
"name": "seat_2_locator",
"parent": "torso",
"pivot": [-14, 77, 13],
"locators": {
"seat_2": [-14, 77, 13]
}
},
{
"name": "seat_3_locator",
"parent": "torso",
"pivot": [14, 77, 13],
"locators": {
"seat_3": [14, 77, 13]
}
},
{
"name": "legs",
"parent": "shoulder_pivot",
"pivot": [0, 37, 0]
},
{
"name": "leg_left",
"parent": "legs",
"pivot": [9.5, 37, 0],
"parent": "body",
"pivot": [5, 20, 0],
"cubes": [
{"origin": [4, 27, -5.5], "size": [11, 11, 11], "uv": [188, 28]}
]
},
{
"name": "knee_left",
"parent": "leg_left",
"pivot": [9.5, 28, -0.25],
"cubes": [
{"origin": [6, 23, -3.5], "size": [7, 7, 7], "uv": [172, 50]}
{"origin": [2.5, 14, -2.5], "size": [5, 7, 5], "uv": [24, 105]}
]
},
{
"name": "leg_left2",
"parent": "knee_left",
"pivot": [9.5, 22.5, 0],
"parent": "leg_left",
"pivot": [5, 15, -0.25],
"cubes": [
{"origin": [3, 16, -6.5], "size": [13, 8, 13], "uv": [201, 56]},
{"origin": [0, 8, -8.5], "size": [19, 8, 17], "uv": [72, 166]}
{"origin": [3.5, 10, -2.25], "size": [3, 5, 4], "uv": [74, 92]}
]
},
{
"name": "foot_left",
"parent": "leg_left2",
"pivot": [9.5, 8, -2],
"pivot": [5, 9.5, 0],
"cubes": [
{"origin": [1, 0, -9.5], "size": [17, 8, 18], "uv": [154, 96]},
{"origin": [1, 0, -14.5], "size": [17, 6, 5], "uv": [208, 85]}
{"origin": [1.5, 7, -3.5], "size": [7, 4, 7], "uv": [98, 0]},
{"origin": [0.5, 2, -4.5], "size": [9, 5, 9], "uv": [86, 70]},
{"origin": [1, 0, -7.5], "size": [8, 2, 11], "uv": [80, 19]}
]
},
{
"name": "leg_right",
"parent": "legs",
"pivot": [-9.5, 37, 0],
"parent": "body",
"pivot": [-5, 20, 0],
"cubes": [
{"origin": [-15, 27, -5.5], "size": [11, 11, 11], "uv": [144, 28]}
]
},
{
"name": "knee_right",
"parent": "leg_right",
"pivot": [-9.5, 28, -0.25],
"cubes": [
{"origin": [-13, 23, -3.5], "size": [7, 7, 7], "uv": [144, 50]}
{"origin": [-7.5, 14, -2.5], "size": [5, 7, 5], "uv": [0, 0]}
]
},
{
"name": "leg_right2",
"parent": "knee_right",
"pivot": [-9.5, 22.5, 0],
"parent": "leg_right",
"pivot": [-5, 15, -0.25],
"cubes": [
{"origin": [-16, 16, -6.5], "size": [13, 8, 13], "uv": [162, 64]},
{"origin": [-19, 8, -8.5], "size": [19, 8, 17], "uv": [0, 166]}
{"origin": [-6.5, 10, -2.25], "size": [3, 5, 4], "uv": [54, 40]}
]
},
{
"name": "foot_right",
"parent": "leg_right2",
"pivot": [-9.5, 8, -2],
"pivot": [-5, 10.5, 0],
"cubes": [
{"origin": [-18, 0, -9.5], "size": [17, 8, 18], "uv": [84, 96]},
{"origin": [-18, 0, -14.5], "size": [17, 6, 5], "uv": [164, 85]}
{"origin": [-8.5, 7, -3.5], "size": [7, 4, 7], "uv": [82, 57]},
{"origin": [-9.5, 2, -4.5], "size": [9, 5, 9], "uv": [0, 82]},
{"origin": [-9, 0, -7.5], "size": [8, 2, 11], "uv": [59, 79]}
]
}
]
@@ -18,32 +18,19 @@
{
"name": "body",
"parent": "cyclizar",
"pivot": [0, 11, 3.5]
"pivot": [0, 0, 0]
},
{
"name": "hips",
"parent": "body",
"pivot": [0, 13.4, 5.75]
},
{
"name": "torso",
"parent": "hips",
"pivot": [0, 13.4, 5.75],
"cubes": [
{"origin": [-3, 10.75, -4], "size": [6, 7, 11], "inflate": 0.01, "uv": [40, 0]}
]
},
{
"name": "seat_1_locator",
"parent": "torso",
"pivot": [0, 17.75, 2],
"locators": {
"seat_1": [0, 17.75, 2]
}
},
{
"name": "seat",
"parent": "torso",
"parent": "hips",
"pivot": [0, 17.75, 4.5],
"rotation": [22.5, 0, 0],
"cubes": [
@@ -53,8 +40,8 @@
]
},
{
"name": "torso2",
"parent": "torso",
"name": "torso",
"parent": "hips",
"pivot": [0, 14, -3.05],
"cubes": [
{"origin": [-3.5, 10.75, -10.5], "size": [7, 7, 8], "uv": [40, 54]}
@@ -62,7 +49,7 @@
},
{
"name": "neck",
"parent": "torso2",
"parent": "torso",
"pivot": [0, 14, -9.35],
"rotation": [82.5, 0, 0],
"cubes": [
@@ -247,8 +234,184 @@
]
},
{
"name": "tail",
"name": "arm_left",
"parent": "torso",
"pivot": [3.25, 13.05, -4.75],
"cubes": [
{"origin": [1.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [13, 116]}
]
},
{
"name": "elbow_left",
"parent": "arm_left",
"pivot": [3.25, 7.05, -4.75],
"cubes": [
{"origin": [1.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [44, 81]}
]
},
{
"name": "hand_left",
"parent": "elbow_left",
"pivot": [3.25, 0.55, -5],
"cubes": [
{"origin": [1.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [74, 98]}
]
},
{
"name": "fingers_left",
"parent": "hand_left",
"pivot": [3.25, 0.05, -7.5]
},
{
"name": "finger_left_left2",
"parent": "fingers_left",
"pivot": [2, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [1, -0.95, -9.25], "size": [1, 2, 3], "uv": [54, 107]}
]
},
{
"name": "finger_left_left3",
"parent": "finger_left_left2",
"pivot": [1.5, 1.05, -9.25],
"cubes": [
{"origin": [1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [36, 112]}
]
},
{
"name": "middle_fingers_left",
"parent": "fingers_left",
"pivot": [3.25, 0.3, -7],
"cubes": [
{"origin": [2.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 33]}
]
},
{
"name": "middle_fingers_left2",
"parent": "middle_fingers_left",
"pivot": [3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [2.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [18, 111]}
]
},
{
"name": "middle_fingers_left3",
"parent": "middle_fingers_left2",
"pivot": [3.25, 1.05, -10.5],
"cubes": [
{"origin": [3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [40, 112]},
{"origin": [2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [112, 41]}
]
},
{
"name": "finger_left_left",
"parent": "fingers_left",
"pivot": [4.5, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [4.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [62, 107]}
]
},
{
"name": "finger_right_left4",
"parent": "finger_left_left",
"pivot": [5, 1.05, -9.25],
"cubes": [
{"origin": [5, -0.95, -11.25], "size": [0, 2, 2], "uv": [44, 112]}
]
},
{
"name": "arm_right",
"parent": "torso",
"pivot": [-3.25, 13.05, -4.75],
"cubes": [
{"origin": [-4.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [29, 116]}
]
},
{
"name": "elbow_right",
"parent": "arm_right",
"pivot": [-3.25, 7.05, -4.75],
"cubes": [
{"origin": [-4.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [0, 101]}
]
},
{
"name": "hand_right",
"parent": "elbow_right",
"pivot": [-3.25, 0.55, -5],
"cubes": [
{"origin": [-5.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [100, 22]}
]
},
{
"name": "fingers_right",
"parent": "hand_right",
"pivot": [-3.25, 0.05, -7.5]
},
{
"name": "finger_left_right2",
"parent": "fingers_right",
"pivot": [-2, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [-2, -0.95, -9.25], "size": [1, 2, 3], "uv": [106, 106]}
]
},
{
"name": "finger_left_right3",
"parent": "finger_left_right2",
"pivot": [-1.5, 1.05, -9.25],
"cubes": [
{"origin": [-1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [119, 93]}
]
},
{
"name": "middle_fingers_right",
"parent": "fingers_right",
"pivot": [-3.25, 0.3, -7],
"cubes": [
{"origin": [-4.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 29]}
]
},
{
"name": "middle_fingers_right2",
"parent": "middle_fingers_right",
"pivot": [-3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [-4.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [106, 66]}
]
},
{
"name": "middle_fingers_right3",
"parent": "middle_fingers_right2",
"pivot": [-3.25, 1.05, -10.5],
"cubes": [
{"origin": [-3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [28, 112]},
{"origin": [-2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [123, 93]}
]
},
{
"name": "finger_right_left",
"parent": "fingers_right",
"pivot": [-4.5, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [-5.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [28, 107]}
]
},
{
"name": "finger_right_left2",
"parent": "finger_right_left",
"pivot": [-5, 1.05, -9.25],
"cubes": [
{"origin": [-5, -0.95, -11.25], "size": [0, 2, 2], "uv": [32, 112]}
]
},
{
"name": "tail",
"parent": "hips",
"pivot": [0, 14.5, 6.75],
"rotation": [17.5, 0, 0],
"cubes": [
@@ -297,194 +460,13 @@
},
{
"name": "tail_wheel",
"parent": "torso",
"parent": "hips",
"pivot": [0, 14.5, 5.75],
"rotation": [7.5, 0, 0],
"cubes": [
{"origin": [-2, 1.5, 1.75], "size": [4, 16, 16], "inflate": 0.02, "uv": [0, 0]}
]
},
{
"name": "ftorso2",
"parent": "hips",
"pivot": [0, 14, -3.05]
},
{
"name": "arm_left",
"parent": "ftorso2",
"pivot": [3.25, 13.05, -4.75],
"cubes": [
{"origin": [1.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [13, 116]}
]
},
{
"name": "elbow_left",
"parent": "arm_left",
"pivot": [3.25, 7.05, -4.75],
"cubes": [
{"origin": [1.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [44, 81]}
]
},
{
"name": "hand_left",
"parent": "elbow_left",
"pivot": [3.25, 0.55, -5],
"cubes": [
{"origin": [1.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [74, 98]}
]
},
{
"name": "fingers_left",
"parent": "hand_left",
"pivot": [3.25, 0.05, -7.5]
},
{
"name": "finger_left_right",
"parent": "fingers_left",
"pivot": [2, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [1, -0.95, -9.25], "size": [1, 2, 3], "uv": [54, 107]}
]
},
{
"name": "finger_left_right2",
"parent": "finger_left_right",
"pivot": [1.5, 1.05, -9.25],
"cubes": [
{"origin": [1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [36, 112]}
]
},
{
"name": "middle_fingers_left",
"parent": "fingers_left",
"pivot": [3.25, 0.3, -7],
"cubes": [
{"origin": [2.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 33]}
]
},
{
"name": "middle_fingers_left2",
"parent": "middle_fingers_left",
"pivot": [3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [2.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [18, 111]}
]
},
{
"name": "middle_fingers_left3",
"parent": "middle_fingers_left2",
"pivot": [3.25, 1.05, -10.5],
"cubes": [
{"origin": [3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [40, 112]},
{"origin": [2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [112, 41]}
]
},
{
"name": "finger_left_left",
"parent": "fingers_left",
"pivot": [4.5, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [4.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [62, 107]}
]
},
{
"name": "finger_left_left2",
"parent": "finger_left_left",
"pivot": [5, 1.05, -9.25],
"cubes": [
{"origin": [5, -0.95, -11.25], "size": [0, 2, 2], "uv": [44, 112]}
]
},
{
"name": "arm_right",
"parent": "ftorso2",
"pivot": [-3.25, 13.05, -4.75],
"cubes": [
{"origin": [-4.75, 7.25, -6.75], "size": [3, 8, 4], "uv": [29, 116]}
]
},
{
"name": "elbow_right",
"parent": "arm_right",
"pivot": [-3.25, 7.05, -4.75],
"cubes": [
{"origin": [-4.75, 0.55, -6.25], "size": [3, 7, 3], "inflate": 0.25, "uv": [0, 101]}
]
},
{
"name": "hand_right",
"parent": "elbow_right",
"pivot": [-3.25, 0.55, -5],
"cubes": [
{"origin": [-5.25, -0.95, -7.75], "size": [4, 3, 4], "inflate": 0.01, "uv": [100, 22]}
]
},
{
"name": "fingers_right",
"parent": "hand_right",
"pivot": [-3.25, 0.05, -7.5]
},
{
"name": "finger_right_left",
"parent": "fingers_right",
"pivot": [-2, 1.05, -7.75],
"rotation": [0, -22.5, 0],
"cubes": [
{"origin": [-2, -0.95, -9.25], "size": [1, 2, 3], "uv": [106, 106]}
]
},
{
"name": "finger_right_left2",
"parent": "finger_right_left",
"pivot": [-1.5, 1.05, -9.25],
"cubes": [
{"origin": [-1.5, -0.95, -11.25], "size": [0, 2, 2], "uv": [119, 93]}
]
},
{
"name": "middle_fingers_right",
"parent": "fingers_right",
"pivot": [-3.25, 0.3, -7],
"cubes": [
{"origin": [-4.25, -0.7, -8.75], "size": [2, 2, 2], "inflate": 0.25, "uv": [108, 29]}
]
},
{
"name": "middle_fingers_right2",
"parent": "middle_fingers_right",
"pivot": [-3.25, 1.53333, -9.01667],
"cubes": [
{"origin": [-4.25, -0.7, -10.25], "size": [2, 2, 1], "inflate": 0.26, "uv": [106, 66]}
]
},
{
"name": "middle_fingers_right3",
"parent": "middle_fingers_right2",
"pivot": [-3.25, 1.05, -10.5],
"cubes": [
{"origin": [-3.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [28, 112]},
{"origin": [-2.75, -0.95, -12.5], "size": [0, 2, 2], "uv": [123, 93]}
]
},
{
"name": "finger_right_right",
"parent": "fingers_right",
"pivot": [-4.5, 1.05, -7.75],
"rotation": [0, 22.5, 0],
"cubes": [
{"origin": [-5.5, -0.95, -9.25], "size": [1, 2, 3], "uv": [28, 107]}
]
},
{
"name": "finger_right_right2",
"parent": "finger_right_right",
"pivot": [-5, 1.05, -9.25],
"cubes": [
{"origin": [-5, -0.95, -11.25], "size": [0, 2, 2], "uv": [32, 112]}
]
},
{
"name": "leg_left_rotate",
"parent": "body",
@@ -1,93 +0,0 @@
{
"portraitScale": 1.66,
"portraitTranslation": [-0.14, -0.74, 0],
"profileScale": 1,
"profileTranslation": [0.0, 0.24, 0.0],
"rootBone": "golurk",
"firstPersonCameraOffset": {
"seat_1": [0, 0.25, 0]
},
"thirdPersonCameraOffset": {
"seat_1": [0, 5, 8],
"seat_1_reverse": [0, 3, 8]
},
"animations": {
"faint": "q.bedrock_primary('golurk', 'faint', q.curve('one'))",
"cry": "q.bedrock_stateful('golurk', 'cry')",
"recoil": "q.bedrock_stateful('golurk', 'recoil')"
},
"poses": {
"battle-standing": {
"poseTypes": ["STAND"],
"isBattle": true,
"animations": ["q.look('head')", "q.bedrock('golurk', 'battle_idle')"]
},
"standing": {
"poseTypes": ["STAND", "NONE", "PORTRAIT", "PROFILE"],
"isBattle": false,
"animations": ["q.look('head')", "q.bedrock('golurk', 'ground_idle')"]
},
"walk": {
"poseName": "walk",
"poseTypes": ["WALK"],
"isBattle": false,
"condition": "!q.in_air && !q.is_sprinting",
"animations": [
"q.look('head')",
"q.bedrock('golurk', 'ground_walk')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_ground_run')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"run": {
"poseName": "run",
"poseTypes": ["WALK"],
"isBattle": false,
"condition": "!q.in_air && q.is_sprinting",
"animations": [
"q.look('head')",
"q.bedrock('golurk', 'ground_run')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_ground_run')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"hover": {
"poseTypes": ["HOVER"],
"isBattle": false,
"animations": [
"q.look('head')", "q.bedrock('golurk', 'air_idle')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_air_idle')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"fly": {
"poseTypes": ["FLY"],
"isBattle": false,
"animations": [
"q.look('head')", "q.bedrock('golurk', 'air_fly')",
{
"condition": "q.is_ridden",
"animation": "q.bedrock('golurk', 'ride_air_fly')"
}
],
"transformTicks": 5,
"transformToTicks": 5
},
"sleep": {
"poseTypes": ["SLEEP"],
"animations": ["q.bedrock('golurk', 'sleep')"]
}
}
}
@@ -1,106 +1,41 @@
{
"portraitScale": 1.5,
"portraitTranslation": [0, 0.5, 0],
"profileScale": 0.5,
"profileTranslation": [0, 0.25, -5],
"rootBone": "cyclizar",
"firstPersonCameraOffset": {
"seat_1": [0, 0.25, 0]
},
"thirdPersonCameraOffset": {
"seat_1": [0, 2.5, 5],
"seat_1_reverse": [0, 2.5, 5]
},
"portraitScale": 1.78,
"portraitTranslation": [-0.95,0.48,0],
"profileScale": 0.76,
"profileTranslation": [0,0.54,0],
"animations": {
"cry": "q.bedrock_stateful('cyclizar', 'cry')"
},
"poses": {
"portrait": {
"poseTypes": ["PORTRAIT", "PROFILE"],
"isBattle": false,
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
},
"ride_jump": {
"poseTypes": ["WALK","STAND", "FLOAT", "NONE"],
"isBattle": false,
"condition": "q.in_air && q.is_ridden",
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ride_jump')"],
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"standing": {
"poseTypes": ["STAND", "NONE"],
"isBattle": false,
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_idle')"],
"condition": "!q.is_ridden",
"quirks": [
"q.bedrock_quirk('cyclizar', 'blink')"
]
},
"walking": {
"poseTypes": ["WALK", "SWIM"],
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_walk')"],
"condition": "!q.is_ridden",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
},
"mount_idle": {
"poseTypes": ["STAND"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_drift')"
],
"condition": "q.is_ridden && !q.in_air",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
},
"mount_walk": {
"poseTypes": ["WALK"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_ground_run')",
"q.bedrock('cyclizar', 'ground_run')"
],
"condition": "q.is_ridden && !q.is_sprinting && !q.in_air && !q.is_drifting && !q.is_powered_drifting",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"mount_drift": {
"poseTypes": ["WALK"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_ground_run')",
"q.bedrock('cyclizar', 'ride_drift')"
],
"condition": "q.is_ridden && !q.is_sprinting && !q.in_air && q.is_drifting && !q.is_powered_drifting",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"mount_powered_drift": {
"poseTypes": ["WALK"],
"isBattle": false,
"animations": [
"q.look('head')",
"q.bedrock('cyclizar', 'ride_ground_run')",
"q.bedrock('cyclizar', 'ride_drift')",
"q.bedrock('cyclizar', 'ride_drift_legs')",
"q.bedrock('cyclizar', 'ride_drift_override')"
],
"condition": "q.is_ridden && !q.is_sprinting && !q.in_air && !q.is_drifting && q.is_powered_drifting",
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"],
"transformTicks": 5,
"transformToTicks": 5
},
"mount_run": {
"poseTypes": ["WALK"],
"isBattle": false,
"condition": "!q.in_air && q.is_sprinting",
"animations": ["q.look('head')", "q.bedrock('cyclizar', 'ground_run')"],
"quirks": ["q.bedrock_quirk('cyclizar', 'blink')"]
"poses": {
"standing": {
"poseTypes": [
"STAND",
"NONE",
"PORTRAIT",
"PROFILE"
],
"isBattle": false,
"animations": [
"q.look('head', 1, 1, 25, -45, 45, -45)",
"q.bedrock('cyclizar', 'grounded')"
],
"quirks": [
"q.bedrock_quirk('cyclizar', 'blink')"
]
},
"walking": {
"poseTypes": [
"WALK",
"SWIM"
],
"animations": [
"q.look('head', 1, 1, 25, -45, 45, -45)",
"q.bedrock('cyclizar', 'grounded')",
"q.quadruped_walk(0.66, 0.6, 'arm_left', 'arm_right', 'leg_left_rotate', 'leg_right_rotate')"
],
"quirks": [
"q.bedrock_quirk('cyclizar', 'blink')"
]
}
}
}
}
}
@@ -6,54 +6,22 @@
"aspects": [],
"poser": "cobblemon:golurk",
"model": "cobblemon:golurk.geo",
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk.png",
"layers": [
{
"name": "color",
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_emissive_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_emissive_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_emissive_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk_emissive.png",
"emissive": true
}
]
},
{
"aspects": ["shiny"],
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk_shiny.png",
"layers": [
{
"name": "color",
"texture": {
"frames": [
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive_1.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive_2.png",
"cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive_3.png"
],
"fps": 10,
"loop": true
},
"texture": "cobblemon:textures/pokemon/0623_golurk/golurk_shiny_emissive.png",
"emissive": true
}
]
@@ -2382,6 +2382,7 @@
"cobblemon.config.ui.category.interface": "Interface",
"cobblemon.config.ui.category.riding": "Riding",
"cobblemon.config.ui.category.debug": "Debug",
"cobblemon.config.ui.category.system": "System",
"cobblemon.config.ui.max_pokemon_level": "Max Pokémon Level",
"cobblemon.config.ui.max_pokemon_level.tooltip": "The maximum level Pokémon can get to.",
@@ -2564,6 +2565,8 @@
"cobblemon.config.ui.despawner_max_age_ticks.tooltip": "The age (in game ticks) a Pokémon must reach before it can be despawned at the near distance boundary.",
"cobblemon.config.ui.enable_debug_keys": "Enable Debug Keys",
"cobblemon.config.ui.enable_debug_keys.tooltip": "Enables additional commands and chat logging to assist with implementing Pokémon (see the wiki for a list of commands).",
"cobblemon.config.ui.time_zone_id": "Time Zone Id",
"cobblemon.config.ui.time_zone_id.tooltip": "Time Zone to use for some MoLangs and IRL time zone dates. Server Side only.",
"cobblemon.config.ui.allow_starter_on_join": "Allow Starter on Join",
"cobblemon.config.ui.prompt_starter_once_only": "Prompt Starter Once Only",
@@ -1175,14 +1175,6 @@
"sounds": [
{
"name": "cobblemon:ride/loop/saucer_flight_loop",
"attenuation_distance": 64.0
}
]
},
"ride.loop.rocket": {
"sounds": [
{
"name": "cobblemon:ride/loop/rocket_flight_loop",
"attenuation_distance": 48.0
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

@@ -2,7 +2,7 @@
"type": "minecraft:chest",
"pools": [
{
"rolls": 1,
"rolls": 3,
"bonus_rolls": 0.0,
"entries": [
{
@@ -12,8 +12,8 @@
"add": false,
"count": {
"type": "minecraft:uniform",
"max": 3.0,
"min": 9.0
"max": 1.0,
"min": 3.0
},
"function": "minecraft:set_count"
}
@@ -29,12 +29,28 @@
"count": {
"type": "minecraft:uniform",
"max": 1.0,
"min": 3.0
"min": 1.0
},
"function": "minecraft:set_count"
}
],
"name": "cobblemon:rare_candy",
"name": "cobblemon:exp_candy_m",
"weight": 10
},
{
"type": "minecraft:item",
"functions": [
{
"add": false,
"count": {
"type": "minecraft:uniform",
"max": 1.0,
"min": 1.0
},
"function": "minecraft:set_count"
}
],
"name": "minecraft:golden_apple",
"weight": 10
}
]
@@ -2,7 +2,7 @@
"type": "minecraft:chest",
"pools": [
{
"rolls": 1,
"rolls": 6,
"bonus_rolls": 0.0,
"entries": [
{
@@ -12,8 +12,8 @@
"add": false,
"count": {
"type": "minecraft:uniform",
"max": 6.0,
"min": 18.0
"max": 1.0,
"min": 3.0
},
"function": "minecraft:set_count"
}
@@ -28,13 +28,29 @@
"add": false,
"count": {
"type": "minecraft:uniform",
"max": 3.0,
"min": 5.0
"max": 1.0,
"min": 2.0
},
"function": "minecraft:set_count"
}
],
"name": "cobblemon:rare_candy",
"name": "cobblemon:exp_candy_m",
"weight": 10
},
{
"type": "minecraft:item",
"functions": [
{
"add": false,
"count": {
"type": "minecraft:uniform",
"max": 1.0,
"min": 2.0
},
"function": "minecraft:set_count"
}
],
"name": "minecraft:golden_apple",
"weight": 10
}
]
@@ -2,7 +2,7 @@ t.wander_control = q.entity.get_wander_control_memory();
t.wander_control.reset();
t.wander_control.set_allow_land((q.entity.behaviour.moving.walk.can_walk && !q.entity.behaviour.moving.walk.avoids_land) || (!q.entity.behaviour.moving.walk.can_walk && q.entity.behaviour.moving.fly.can_fly));
t.wander_control.set_allow_water(q.entity.behaviour.moving.swim.can_swim && !q.entity.behaviour.moving.swim.avoids_water);
t.wander_control.set_allow_water(q.entity.behaviour.moving.swim.can_swim_in_water && !q.entity.behaviour.moving.swim.avoids_water);
t.wander_control.set_allow_air(q.entity.behaviour.moving.fly.can_fly && !q.entity.behaviour.moving.walk.can_walk);
t.wander_control.set_walk_speed(0.35);
@@ -96,10 +96,24 @@
"key": "cobblemon:composite",
"transitionStrategy": "cobblemon:strategy/jump",
"defaultBehaviour": {
"key": "cobblemon:land/horse"
"key": "cobblemon:land/horse",
"gravity": "q.gravity * (0.1 + (q.passenger_count - 1) * 0.1)",
"speed": "Math.clamp(q.entity.velocity.horizontal_magnitude + 0.33, 0, 1)",
"jumpVector": [
0,
"0.3*q.jump_strength/50",
"-1 * 0.5*q.jump_strength/50"
]
},
"alternateBehaviour": {
"key": "cobblemon:air/bird",
"gravity": "q.gravity * (0.1 + (q.passenger_count - 1) * 0.1)",
"speed": "Math.clamp(q.entity.velocity.horizontal_magnitude + 0.33, 0, 1)",
"jumpVector": [
0,
"0.3*q.jump_strength/50",
0
],
"rideSounds": [
{
"soundLocation": "cobblemon:ride.loop.leather",
@@ -60,64 +60,6 @@
}
}
},
"riding": {
"stats": {
"SPEED": {
"displayName": "Speed",
"description": "Its top speed",
"ranges": {
"LAND": "50-100"
}
},
"ACCELERATION": {
"displayName": "Acceleration",
"description": "How fast it can speed up",
"ranges": {
"LAND": "50-100"
}
},
"SKILL": {
"displayName": "Handling",
"description": "How fast it can turn",
"ranges": {
"LAND": "50-100"
}
},
"JUMP": {
"displayName": "Altitude",
"description": "How high it can jump",
"ranges": {
"LAND": "50-100"
}
},
"STAMINA": {
"displayName": "Fuel/Stamina",
"description": "How long it can sprint",
"ranges": {
"LAND": "50-100"
}
}
},
"behaviour": {
"key": "cobblemon:liquid/dolphin"
},
"seats": [
{
"offset": {
"x": 0,
"y": 0.4,
"z": 0
}
},
{
"offset": {
"x": 0,
"y": 0.4,
"z": 1
}
}
]
},
"drops": {
"amount": 4,
"entries": [
@@ -266,5 +208,26 @@
"legacy:toxic"
],
"preEvolution": "seel",
"evolutions": []
"evolutions": [],
"riding": {
"stats": {
"SPEED": {
"ranges": "15-30"
},
"ACCELERATION": {
"ranges": "25-50"
},
"SKILL": {
"ranges": "15-30"
},
"JUMP": {
"ranges": "20-40"
},
"STAMINA": {
"ranges": "30-60"
}
},
"seats": [],
"behaviour": null
}
}
@@ -43,101 +43,16 @@
"catchRate": 90,
"eggCycles": 25,
"baseFriendship": 50,
"baseScale": 1.0,
"baseScale": 1.65,
"hitbox": {
"width": 1.4,
"height": 3.5,
"fixed": false
},
"riding": {
"stats": {
"SPEED": {
"displayName": "Speed",
"description": "Its top speed",
"ranges": {
"LAND": "10-50",
"AIR": "50-100"
}
},
"ACCELERATION": {
"displayName": "Acceleration",
"description": "How fast it can speed up",
"ranges": {
"LAND": "50-100",
"AIR": "50-100"
}
},
"SKILL": {
"displayName": "Handling",
"description": "How fast it can turn",
"ranges": {
"LAND": "50-100",
"AIR": "50-100"
}
},
"JUMP": {
"displayName": "Altitude",
"description": "How high it can fly",
"ranges": {
"LAND": "50-100",
"AIR": "50-100"
}
},
"STAMINA": {
"displayName": "Fuel/Stamina",
"description": "How long it can stay off the ground",
"ranges": {
"LAND": "50-100",
"AIR": "50-100"
}
}
},
"behaviour": {
"key": "cobblemon:composite",
"transitionStrategy": "cobblemon:strategy/jump",
"defaultBehaviour": {
"key": "cobblemon:land/horse",
"canJump": false
},
"alternateBehaviour": {
"key": "cobblemon:air/rocket",
"rideSounds": [
{
"soundLocation": "cobblemon:ride.loop.rocket",
"volumeExpr": "math.pow(math.min((q.driver_input() / 1.5) + (q.ride_velocity() / 1.5), 1.0),2)",
"pitchExpr": "math.max(1.0 ,0.2 + math.pow(math.min(q.driver_input() / 1.5, 1.0),2))",
"playForNonPassengers": true,
"muffleEnabled": true
}
]
}
},
"seats": [
{
"offset": {
"x": 0,
"y": 0.4,
"z": 0
},
"poseOffsets": [
{
"poseTypes": [
"FLY"
],
"offset": {
"x": 0,
"y": 0.0,
"z": 0
}
}
]
}
]
},
"behaviour": {
"moving": {
"walk": {
"walkSpeed": 0.35
"walkSpeed": 0.25
},
"swim": {
"avoidsWater": true,
@@ -47,64 +47,6 @@
"height": 1.3,
"fixed": false
},
"riding": {
"stats": {
"SPEED": {
"displayName": "Speed",
"description": "Its top speed",
"ranges": {
"LAND": "50-100"
}
},
"ACCELERATION": {
"displayName": "Acceleration",
"description": "How fast it can speed up",
"ranges": {
"LAND": "50-100"
}
},
"SKILL": {
"displayName": "Handling",
"description": "How fast it can turn",
"ranges": {
"LAND": "50-100"
}
},
"JUMP": {
"displayName": "Altitude",
"description": "How high it can jump",
"ranges": {
"LAND": "50-100"
}
},
"STAMINA": {
"displayName": "Fuel/Stamina",
"description": "How long it can sprint",
"ranges": {
"LAND": "50-100"
}
}
},
"behaviour": {
"key": "cobblemon:land/vehicle"
},
"seats": [
{
"offset": {
"x": 0,
"y": 0.4,
"z": 0
}
},
{
"offset": {
"x": 0,
"y": 0.4,
"z": 1
}
}
]
},
"drops": {
"amount": 1,
"entries": [
@@ -55,64 +55,6 @@
}
}
},
"riding": {
"stats": {
"SPEED": {
"displayName": "Speed",
"description": "Its top speed",
"ranges": {
"LAND": "50-100"
}
},
"ACCELERATION": {
"displayName": "Acceleration",
"description": "How fast it can speed up",
"ranges": {
"LAND": "50-100"
}
},
"SKILL": {
"displayName": "Handling",
"description": "How fast it can turn",
"ranges": {
"LAND": "50-100"
}
},
"JUMP": {
"displayName": "Altitude",
"description": "How high it can jump",
"ranges": {
"LAND": "50-100"
}
},
"STAMINA": {
"displayName": "Fuel/Stamina",
"description": "How long it can sprint",
"ranges": {
"LAND": "50-100"
}
}
},
"behaviour": {
"key": "cobblemon:land/minekart"
},
"seats": [
{
"offset": {
"x": 0,
"y": 0.4,
"z": 0
}
},
{
"offset": {
"x": 0,
"y": 0.4,
"z": 1
}
}
]
},
"drops": {
"amount": 4,
"entries": [