Compare commits

...
Author SHA1 Message Date
Polymeta c9d27d46b6 license header
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2025-03-26 11:39:18 +00:00
Polymeta b68756378c Merge branch 'Box-count-command' into 'main'
Added /boxcount

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

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

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

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

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

Closes #1410

See merge request cable-mc/cobblemon!1529
2025-03-23 22:05:38 +00:00
JordanandHiroku 60ae9e3a55 Pokemon hitboxes scale with attribute generic.scale 2025-03-23 22:05:37 +00:00
Josh 4bfe147730 Merge branch 'main' into josh/npc-item-rendering 2025-03-22 12:41:31 -04:00
Josh 963614f5fa Updated CHANGELOG.md 2025-03-21 11:51:18 -04:00
Josh 7fdb70e3c3 Updated trainer npcs for testing 2025-03-21 11:20:24 -04:00
Josh 37e8f0eab6 multiple items can render on a model per locator and the render_item() molang function now supports items with components 2025-03-20 16:14:16 -04:00
ShiroFenixlm a6d17659f8 Added removeListOfBoxes to PCStore 2025-03-19 23:31:43 -04:00
Josh 05edafa5c4 NPC Trainers send out/return animation are able to show the correct ball. 2025-03-19 22:35:00 -04:00
ShiroFenixlm 997a6de23b Remove and Set now only delete empty boxes unless forced 2025-03-19 21:52:08 -04:00
Josh cea600617e Added render_item() molang function for PosableState. Allows rendering dummy items for animations. 2025-03-19 18:47:11 -04:00
ShiroFenixlm c948b6d885 Added force remove button 2025-03-18 19:40:53 -04:00
ShiroFenixlm 5508980384 Final Cleanup, i hope 2025-03-17 15:24:02 -04:00
ShiroFenixlm e39dee01e5 Cleanup 2025-03-17 15:16:06 -04:00
ShiroFenixlm a81412e176 Merge remote-tracking branch 'origin/Box-count-command' into Box-count-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/command/ChangePCBoxesCommand.kt
2025-03-17 15:15:01 -04:00
ShiroFenixlm 09f9dd91aa Fixed some issues and added error handling when removing too many boxes 2025-03-17 15:08:07 -04:00
ShiroFenixlm 2efc3c72ac Fixed some issues and added error handling when removing too many boxes 2025-03-17 15:02:57 -04:00
ShiroFenix a92fb03093 Updated command logic to improve legibility 2025-03-17 12:56:15 -04:00
ShiroFenix ad5847983f Updated command logic to improve legibility 2025-03-17 12:55:59 -04:00
WhiteFenix19 bb25300513 Updated command logic to improve legibility 2025-03-17 12:32:46 -04:00
WhiteFenix19 de33804eb4 Updated command logic to improve legibility 2025-03-17 12:19:47 -04:00
WhiteFenix19 de042f71dd Updated command logic to improve legibility 2025-03-17 12:18:28 -04:00
WhiteFenix19 5a1e85bbf4 Added /boxcount command 2025-03-17 00:47:00 -04:00
ShiroFenix 3489e0df74 Revert "Added /boxcount"
This reverts commit afe8eec343
2025-03-17 03:59:02 +00:00
ShiroFenix afe8eec343 Added /boxcount 2025-03-17 03:43:32 +00:00
Conan 8913d3df11 Reworked SpectateBattleHandler.kt 2024-12-30 18:23:58 +01:00
Conan 3a55965704 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into spectate-command
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonCommands.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/permission/CobblemonPermissions.kt
2024-12-29 12:30:54 +01:00
Conan 7112c74bf2 Reworked spectate command. 2024-11-09 22:37:11 +01:00
Conan faa1906290 Added a spectate battle command. 2024-11-09 17:30:01 +01:00
36 changed files with 1208 additions and 505 deletions
+4
View File
@@ -2,6 +2,7 @@
## [1.7.0 (Month xth, 2025)](#1-7-0)
### Additions
- Added `/spectateBattle <player>` command to spectate battles without having to manually walk up to the target.
- Added an in-game configuration screen, allowing all settings from `main.json` to be edited directly in-game.
- Added `/cobblemonconfig reload` command to reload `main.json` configuration. **Note:** Some settings require a server restart to take effect; use this command cautiously.
- Added `blacklisted_items_to_hold` and `whitelisted_items_to_hold` tags to allow for controlling which items players can give to their Pokémon. If the whitelist is empty, it will consider all item as allowed (unless they are in the blacklist).
@@ -10,6 +11,7 @@
- Added cosmetic item functionality for Pokémon. Certain cosmetic items can be given to applicable Pokémon via the interact menu.
- Added the various log blocks as cosmetic items for Timburr and Komala.
- Added `visibility/hidden`, `visibility/hat` and `visibility/face` tags to control where and how certain items are rendered.
- Added `/boxcount` command to change PC boxes amount
### Changes
- Renamed `chargeGainedPerTick` config to `secondsToChargeHealingMachine`.
@@ -17,6 +19,7 @@
- Substantially optimised spawning checks mainly by front-loading biome filtering.
- When using the `cobblemon` or `generation_9` capture calculators a critical capture with a single shake will always play for successful captures when you've already registered the Pokémon as caught in your Pokédex.
- Improved the performance of saving Pokédex and player data.
- Pokémon hitbox now scales with entity attribute `generic.scale`.
### Fixes
- Fixed Particles sometimes facing the wrong direction (looking at you, Swords Dance)
@@ -71,6 +74,7 @@
- Added `interpolate` boolean property to animated textures to allow gradual colour changes between frames.
- Fixed species additions not being capable of changing implemented status.
- Added support for action effects that are triggered by `|-activate|` Showdown instructions. `activate_{effect_id}` is the syntax.
- Added Molang functions for rendering items `render_item(item_id, locator_name)` and `clear_items()`.
## [1.6.1 (January 26th, 2025)](#1-6-1)
@@ -60,8 +60,10 @@ object CobblemonCommands {
RenameBoxCommand.register(dispatcher)
ChangeBoxWallpaperCommand.register(dispatcher)
RunMolangScriptCommand.register(dispatcher)
SpectateBattleCommand.register(dispatcher)
CobblemonConfigCommand.register(dispatcher)
UnlockPCBoxWallpaperCommand.register(dispatcher)
ChangePCBoxesCommand.register(dispatcher)
// Possibly lock down registration if and only if under dev environment or running in an environment
// with a certain system environment variable set
@@ -71,6 +71,8 @@ import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.pokemon.riding.controllers.BirdAirController
import com.cobblemon.mod.common.pokemon.riding.states.BirdAirState
import com.cobblemon.mod.common.util.*
import com.mojang.datafixers.util.Either
import java.util.UUID
@@ -786,6 +788,7 @@ object MoLangFunctions {
map.put("in_battle") { DoubleValue(pokemonEntity.isBattling) }
map.put("is_moving") { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
map.put("is_flying") { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
map.put("is_gliding") { DoubleValue(pokemonEntity.useRidingAltPose()) }
map.put("has_aspect") { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
map.put("is_pokemon") { DoubleValue.ONE }
map.put("is_holding_item") { DoubleValue(!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).isEmpty) }
@@ -128,8 +128,12 @@ object CobblemonPermissions {
val RUN_MOLANG_SCRIPT = this.create("${COMMAND_PREFIX}runmolangscript", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val COBBLEMON_CONFIG_RELOAD = this.create("${COMMAND_PREFIX}cobblemonconfig.reload", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val CALCULATE_SEAT_POSITIONS = this.create("${COMMAND_PREFIX}calculateseatpositions", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val CHANGE_BOX_COUNT = this.create("${COMMAND_PREFIX}boxcount", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val SPECTATE_BATTLE = this.create("${COMMAND_PREFIX}spectatebattle", PermissionLevel.ALL_COMMANDS)
fun all(): Iterable<Permission> = this.permissions
@@ -134,6 +134,11 @@ data class RidingManager(val entity: PokemonEntity) {
return controller.jumpForce(entity, driver, jumpStrength)
}
fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean {
val controller = getController(entity) ?: return false
return controller.useRidingAltPose(entity, driver)
}
/**
* Used to supply the delta for the lerp() between velocity
* vectors. Called inertia for now as the way it feels when
@@ -111,6 +111,8 @@ interface RideController : Encodable, Decodable {
fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean = false
//If function is not overwritten by controllers then just perform the defualt
//rolling function which is roll on mousex and pitch on mousey
fun rotationOnMouseXY(
@@ -137,6 +137,12 @@ open class PCStore(
pcChangeObservable.emit(Unit)
}
fun removeListOfBoxes(boxList: List<PCBox>,lockNewSize: Boolean = false, overflowHandler: (Pokemon) -> Unit = ::relocateEvictedBoxPokemon){
this.lockedSize = lockNewSize
boxes.removeAll(boxList)
boxList.flatMap { it.asIterable() }.forEach(overflowHandler)
pcChangeObservable.emit(Unit)
}
fun tryRestoreBackedUpPokemon() {
var newPosition = getFirstAvailablePosition()
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
import org.joml.Quaternionf
import org.joml.Vector3f
@@ -115,9 +116,10 @@ class ModelWidget(
heldItemRenderer.renderOnModel(
state.currentModel!!,
heldItem?: ItemStack.EMPTY,
state.locatorStates,
state,
matrices,
context.bufferSource()
context.bufferSource(),
Vec3(0.0,-90.0,-90.0)
)
matrices.popPose()
@@ -11,24 +11,24 @@ package com.cobblemon.mod.common.client.render.item
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_FACE_ITEMS
import com.cobblemon.mod.common.api.tags.CobblemonItemTags.WEARABLE_HAT_ITEMS
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.models.blockbench.NullObjectParser
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.LightTexture
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemDisplayContext
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class HeldItemRenderer() {
private val itemRenderer = Minecraft.getInstance().itemRenderer
private var displayContext = ItemDisplayContext.FIXED
private var scale = 1.0f
class HeldItemRenderer {
private val itemRenderer: ItemRenderer = Minecraft.getInstance().itemRenderer
private var displayContext: ItemDisplayContext = ItemDisplayContext.FIXED
companion object {
const val ITEM_FACE = "item_face"
@@ -37,99 +37,130 @@ class HeldItemRenderer() {
}
fun render(
entity: PokemonEntity?,
entity: LivingEntity?,
model: PosableModel,
item: ItemStack,
locators: Map<String, MatrixWrapper>,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int,
seed: Int
seed: Int,
rotation: Vec3
) {
if (item.isEmpty) return
displayContext = ItemDisplayContext.FIXED
poseStack.pushPose()
when {
//item_face locator
(locators.containsKey(ITEM_FACE) && item.`is`(WEARABLE_FACE_ITEMS)) -> {
displayContext = model.getLocatorDisplayContext(ITEM_FACE) ?: ItemDisplayContext.HEAD
poseStack.mulPose(locators[ITEM_FACE]!!.matrix)
poseStack.translate(0f, 0f, .28f * scale)
poseStack.scale(0.7f * scale, 0.7f * scale, 0.7f * scale)
poseStack.translate(0f, 0f, .28f)
poseStack.scale(0.7f, 0.7f, 0.7f)
}
(locators.containsKey("item_hat") && item.`is`(WEARABLE_HAT_ITEMS)) -> {
//item_hat locator
(locators.containsKey(ITEM_HAT) && item.`is`(WEARABLE_HAT_ITEMS)) -> {
displayContext = model.getLocatorDisplayContext(ITEM_HAT) ?: ItemDisplayContext.HEAD
poseStack.mulPose(locators["item_hat"]!!.matrix)
poseStack.translate(0f, -0.26f * scale, 0f)
poseStack.scale(.68f * scale, .68f * scale, .68f * scale)
poseStack.mulPose(locators[ITEM_HAT]!!.matrix)
poseStack.translate(0f, -0.26f, 0f)
poseStack.scale(.68f, .68f, .68f)
}
(locators.containsKey("item")) -> {
updateModifiers("item",locators)
poseStack.mulPose(locators["item"]!!.matrix)
//item locator
(locators.containsKey(ITEM)) -> {
displayContext = model.getLocatorDisplayContext(ITEM) ?: ItemDisplayContext.FIXED
when (displayContext) {
ItemDisplayContext.FIXED -> {
poseStack.translate(0f, 0.01666f * scale, 0f)
poseStack.scale(.5f * scale, .5f * scale, .5f * scale)
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
}
ItemDisplayContext.THIRD_PERSON_RIGHT_HAND -> {
poseStack.translate(0.075f* scale, 0f * scale, -0.05f* scale)
poseStack.scale(scale, scale, scale)
poseStack.mulPose(Axis.XP.rotationDegrees(-90f))
poseStack.mulPose(Axis.YP.rotationDegrees(-90f))
}
else -> {
poseStack.scale(scale, scale, scale)
}
}
poseStack.mulPose(locators[ITEM]!!.matrix)
applyContextTransformation(poseStack, rotation)
}
else -> { // Don't render any item
// Don't render any item
else -> {
poseStack.popPose()
return
}
}
itemRenderer.renderStatic(entity, item, displayContext, false, poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, seed)
itemRenderer.renderStatic(entity, item, displayContext, (displayContext==ItemDisplayContext.THIRD_PERSON_LEFT_HAND), poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, seed)
poseStack.popPose()
}
private fun renderAnimationItem(
model: PosableModel,
state: PosableState,
locators: Map<String, MatrixWrapper>,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int,
entity: LivingEntity?,
rotation: Vec3
) {
state.itemRenderingLocations.forEach { (locatorName, item) ->
poseStack.pushPose()
if (locators.containsKey(locatorName)) {
displayContext = model.getLocatorDisplayContext(locatorName)?: ItemDisplayContext.FIXED
poseStack.mulPose(locators[locatorName]!!.matrix)
applyContextTransformation(poseStack, rotation)
}
itemRenderer.renderStatic(entity, item, displayContext, (displayContext==ItemDisplayContext.THIRD_PERSON_LEFT_HAND), poseStack, buffer, null, light, OverlayTexture.NO_OVERLAY, 0)
poseStack.popPose()
}
}
fun renderOnModel(
model: PosableModel,
item: ItemStack,
locators: Map<String, MatrixWrapper>,
state: PosableState,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int = LightTexture.pack(11, 7)
){
if (!item.`is`(CobblemonItemTags.HIDDEN_ITEMS)) render(null, model, item, locators, poseStack, buffer, light, 0)
rotation: Vec3 = Vec3(0.0,0.0,0.0),
light: Int = LightTexture.pack(11, 7),
) {
val locators: Map<String, MatrixWrapper> = state.locatorStates
if (state.itemRenderingLocations.isNotEmpty()) {
renderAnimationItem(model, state, locators, poseStack, buffer, light, null, rotation)
}
else if (!item.`is`(CobblemonItemTags.HIDDEN_ITEMS)) {
render(null, model, item, locators, poseStack, buffer, light, 0, rotation)
}
}
fun renderOnEntity(
entity: PokemonEntity,
delegate: PokemonClientDelegate,
entity: LivingEntity,
item: ItemStack,
model: PosableModel,
state: PosableState,
poseStack: PoseStack,
buffer: MultiBufferSource,
light: Int
light: Int,
rotation: Vec3 = Vec3(0.0,0.0,0.0)
) {
val shownItem = entity.shownItem
val locators: Map<String, MatrixWrapper> = delegate.locatorStates
render(entity, model, shownItem, locators, poseStack, buffer, light, 0)
val locators: Map<String, MatrixWrapper> = state.locatorStates
if (state.itemRenderingLocations.isNotEmpty()) {
renderAnimationItem(model, state, locators, poseStack, buffer, light, entity, rotation)
}
else {
render(entity, model, item, locators, poseStack, buffer, light, 0, rotation)
}
}
private fun updateModifiers(name: String, locators: Map<String, MatrixWrapper>) {
locators.forEach { (locator: String, m: MatrixWrapper) ->
val modifiers: Map<String, Float>
if (locator.startsWith("_null_$name[")) {
modifiers = NullObjectParser.parseNullObject(locator).modifiers
if (modifiers.containsKey("scale")) scale = modifiers["scale"]!!
if (modifiers.containsKey("mode")) displayContext = ItemDisplayContext.entries[((modifiers["mode"]!!).toInt()) % 9]
return
private fun applyContextTransformation(poseStack: PoseStack, rotation: Vec3) {
when (displayContext) {
ItemDisplayContext.FIXED -> {
poseStack.mulPose(Axis.XP.rotationDegrees(90.0f))
poseStack.scale(.5f, .5f, .5f )
poseStack.translate(0f, 0.01666f, 0f)
}
ItemDisplayContext.THIRD_PERSON_RIGHT_HAND, ItemDisplayContext.THIRD_PERSON_LEFT_HAND -> {
poseStack.mulPose(Axis.XP.rotationDegrees((rotation.x).toFloat()))
poseStack.mulPose(Axis.YP.rotationDegrees((rotation.y).toFloat()))
poseStack.mulPose(Axis.ZP.rotationDegrees((rotation.z).toFloat()))
poseStack.translate(0.025f * if (displayContext==ItemDisplayContext.THIRD_PERSON_LEFT_HAND) 1 else -1 , 0.0f, 0.0f)
}
ItemDisplayContext.HEAD -> {
poseStack.mulPose(Axis.XP.rotationDegrees(90.0f))
poseStack.scale(.6f, .6f, .6f)
poseStack.translate(0f, -0.6f, 0f)
}
else -> {}
}
}
}
@@ -37,6 +37,7 @@ import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent<T, PlayerModel<T>>) : RenderLayer<T, PlayerModel<T>>(renderLayerParent) {
@@ -163,9 +164,10 @@ class PokemonOnShoulderRenderer<T : Player>(renderLayerParent: RenderLayerParent
heldItemRenderer.renderOnModel(
model,
shoulderData.shownItem,
state.locatorStates,
state,
matrixStack,
buffer
buffer,
Vec3(0.0,-90.0,-90.0)
)
model.setDefault()
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench
/**
* Uses the name of null objects in a model to get modifier values for a locator.
* @author Josh
*/
object NullObjectParser {
fun parseNullObject(input: String): NullObjectModifier {
// Extract modifiedLocator
val modifiedLocator = input.replaceFirst("^_null_".toRegex(), "").replace("\\[.*\\]$".toRegex(), "")
// Extract modifiers
val modifiers: MutableMap<String, Float> = HashMap()
val start = input.indexOf('[')
val end = input.indexOf(']')
if (start != -1 && end != -1 && end > start) {
val modifierPart = input.substring(start + 1, end)
val modifierPairs = modifierPart.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (pair in modifierPairs) {
val keyValue = pair.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (keyValue.size == 2) {
val key = keyValue[0].trim { it <= ' ' }
try {
val value = keyValue[1].trim { it <= ' ' }.toFloat()
modifiers[key] = value
} catch (e: NumberFormatException) {
System.err.println("Invalid number format for: " + keyValue[1])
}
}
}
}
return NullObjectModifier(modifiedLocator, modifiers)
}
/**
* Stores modifiers for a particular locator.
*/
class NullObjectModifier(val locatorName: String, val modifiers: Map<String, Float>)
}
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.client.ClientMoLangFunctions.animationFunctions
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
import com.cobblemon.mod.common.client.render.ModelLayer
@@ -56,6 +57,7 @@ import net.minecraft.client.renderer.texture.DynamicTexture
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemDisplayContext
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
@@ -36,14 +36,18 @@ import com.cobblemon.mod.common.client.render.models.blockbench.quirk.QuirkData
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.mojang.brigadier.StringReader
import net.minecraft.client.Minecraft
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.commands.arguments.item.ItemParser
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
import org.joml.Matrix4f
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
/**
@@ -124,6 +128,10 @@ abstract class PosableState : Schedulable {
/** This gets called 500 million times so use a mutable value for runtime */
private val reusableAnimTime = DoubleValue(0.0)
val itemRenderingLocations = mutableMapOf<String, ItemStack>()
// var itemRenderingLocation = ""
// var animationItem : ItemStack? = null
/** All of the MoLang functions that can be applied to something with this state. */
val functions = QueryStruct(hashMapOf())
.addFunction("anim_time") {
@@ -148,6 +156,24 @@ abstract class PosableState : Schedulable {
)
}
}
.addFunction("render_item") { params ->
if (params.get<MoValue>(0) !is StringValue) return@addFunction Unit
val client = Minecraft.getInstance().connection ?: return@addFunction Unit
val result = ItemParser(client.registryAccess()).parse(StringReader(params.getString(0)))
val item = ItemStack(result.item)
item.applyComponents(result.components)
val renderLocation = if (params.contains(1)) params.getString(1) else "item"
if (!item.isEmpty && locatorStates.containsKey(renderLocation)) {
itemRenderingLocations.put(renderLocation, item)
} else {
if (itemRenderingLocations.containsKey(renderLocation)) itemRenderingLocations.remove(renderLocation)
return@addFunction Unit
}
}
.addFunction("clear_items") { itemRenderingLocations.clear() }
.addFunction("play_animation") { params ->
val animationParameter = params.get<MoValue>(0)
val animation = if (animationParameter is ObjectValue<*>) {
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.client.render.npc
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.render.item.HeldItemRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.PosableEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.npc.PosableNPCModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
@@ -19,6 +20,7 @@ import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.entity.EntityRendererProvider.Context
import net.minecraft.client.renderer.entity.LivingEntityRenderer
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.phys.Vec3
import kotlin.math.min
class NPCRenderer(context: Context) : LivingEntityRenderer<NPCEntity, PosableEntityModel<NPCEntity>>(context, PosableNPCModel(), 0.5f) {
@@ -26,6 +28,8 @@ class NPCRenderer(context: Context) : LivingEntityRenderer<NPCEntity, PosableEnt
return VaryingModelRepository.getTexture(entity.npc.resourceIdentifier, (entity.delegate as NPCClientDelegate))
}
private val heldItemRenderer = HeldItemRenderer()
override fun render(
entity: NPCEntity,
entityYaw: Float,
@@ -56,6 +60,20 @@ class NPCRenderer(context: Context) : LivingEntityRenderer<NPCEntity, PosableEnt
model.blue = 1F
model.resetLayerContext()
if (entity.deathTime < 1){
//Render Held Item
heldItemRenderer.renderOnEntity(
entity,
entity.mainHandItem,
model,
clientDelegate,
poseMatrix,
buffer,
packedLight,
Vec3(-90.0,0.0,0.0)
)
}
// if (this.shouldRenderLabel(entity)) {
// this.renderLabelIfPresent(entity, entity.displayName, poseMatrix, buffer, packedLight)
// }
@@ -101,7 +101,7 @@ class PokemonRenderer(
packedLight: Int
) {
val clientDelegate = entity.delegate as PokemonClientDelegate
shadowRadius = min((entity.boundingBox.maxX - entity.boundingBox.minX), (entity.boundingBox.maxZ) - (entity.boundingBox.minZ)).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).entityScaleModifier
shadowRadius = (min((entity.boundingBox.maxX - entity.boundingBox.minX), (entity.boundingBox.maxZ) - (entity.boundingBox.minZ)).toFloat() / 1.5F * (entity.delegate as PokemonClientDelegate).entityScaleModifier)/entity.scale
model.posableModel = VaryingModelRepository.getPoser(entity.pokemon.species.resourceIdentifier, clientDelegate)
model.posableModel.context = model.context
model.setupEntityTypeContext(entity)
@@ -160,11 +160,13 @@ class PokemonRenderer(
//Render Held Item
heldItemRenderer.renderOnEntity(
entity,
clientDelegate,
entity.shownItem,
model.posableModel,
clientDelegate,
poseMatrix,
buffer,
packedLight
packedLight,
Vec3(0.0,-90.0,90.0)
)
}
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.storage.pc.PCBox
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.util.*
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.IntegerArgumentType
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.Commands.literal
import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.commands.arguments.selector.EntitySelector
import net.minecraft.server.level.ServerPlayer
object ChangePCBoxesCommand {
private const val NAME = "boxcount"
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
dispatcher.register(literal(NAME)
.permission(CobblemonPermissions.CHANGE_BOX_COUNT)
.then(literal("query").then(
argument("player", EntityArgument.player()).executes(::executeQuery))
)
.then(literal("add").then(
argument("player", EntityArgument.player()).then(
argument("amount", IntegerArgumentType.integer(1, 1000)).executes(::executeAdd)))
)
.then(literal("remove").then(
argument("player", EntityArgument.player()).then(
argument("amount", IntegerArgumentType.integer(1, 1000)).executes(::executeRemove)))
)
.then(literal("set").then(
argument("player", EntityArgument.player()).then(
argument("amount", IntegerArgumentType.integer(1, 1000)).executes(::executeSet)))
)
)
}
private fun executeQuery(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
context.source.sendSystemMessage(lang("command.boxcount", player.name, playerPc.boxes.size))
return Command.SINGLE_SUCCESS
}
private fun executeAdd(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
val amount = context.getArgument("amount", Int::class.java)
playerPc.resize(playerPc.boxes.size + amount, true)
playerPc.sendTo(player)
context.source.sendSystemMessage(lang("command.changeboxcount", player.name, playerPc.boxes.size).green())
return Command.SINGLE_SUCCESS
}
private fun executeRemove(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
val amount = context.getArgument("amount", Int::class.java)
if (amount < playerPc.boxes.size) {
val emptyBoxes = playerPc.boxes.filter { it.getNonEmptySlots().isEmpty() }
if (emptyBoxes.size >= amount) {
remove(context,player, playerPc, amount, emptyBoxes)
return Command.SINGLE_SUCCESS
}
else {
context.source.sendSystemMessage(lang("command.changeboxcount.removing_not_empty_box").red().append("\n").append(
lang("command.changeboxcount.force_remove").gray().onClick(true){remove(context,player, playerPc, amount, emptyBoxes)}
))
return 0
}
}
else {
context.source.sendSystemMessage(lang("command.changeboxcount.removing_too_many_boxes", player.name, playerPc.boxes.size).red())
return 0
}
}
private fun executeSet(context: CommandContext<CommandSourceStack>): Int {
val player = context.getArgument("player", EntitySelector::class.java).findSinglePlayer(context.source)
val playerPc = player.pc()
val amount = context.getArgument("amount", Int::class.java)
if (amount < playerPc.boxes.size) {
val emptyBoxes = playerPc.boxes.filter { it.getNonEmptySlots().isEmpty() }
val deleteAmount = playerPc.boxes.size - amount
if (emptyBoxes.size <= deleteAmount) {
context.source.sendSystemMessage(lang("command.changeboxcount.removing_not_empty_box").red().append("\n").append(
lang("command.changeboxcount.force_remove").gray().onClick(true){ remove(context,player, playerPc, deleteAmount, emptyBoxes) }
))
return 0
}
else {
remove(context,player, playerPc, deleteAmount, emptyBoxes)
return Command.SINGLE_SUCCESS
}
}
set(context,player, playerPc, amount)
return Command.SINGLE_SUCCESS
}
private fun set(context: CommandContext<CommandSourceStack>,player: ServerPlayer, playerPc: PCStore, amount: Int){
playerPc.resize(amount, true)
playerPc.sendTo(player)
context.source.sendSystemMessage(lang("command.changeboxcount", player.name, playerPc.boxes.size).green())
}
private fun remove(context: CommandContext<CommandSourceStack>,player: ServerPlayer, playerPc: PCStore, amount: Int,emptyBoxes: List<PCBox>){
if (amount <= emptyBoxes.size) {
playerPc.removeListOfBoxes(emptyBoxes.takeLast(amount),true)
}
else {
val slicedBoxes = emptyBoxes + playerPc.boxes.takeLast(amount - emptyBoxes.size)
playerPc.removeListOfBoxes(slicedBoxes,true)
}
playerPc.initialize()
playerPc.sendTo(player)
context.source.sendSystemMessage(lang("command.changeboxcount", player.name, playerPc.boxes.size).green())
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.net.serverhandling.battle.SpectateBattleHandler
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.Commands.literal
import net.minecraft.commands.arguments.EntityArgument
object SpectateBattleCommand {
fun register(dispatcher : CommandDispatcher<CommandSourceStack>) {
val command = literal("spectateBattle")
.permission(CobblemonPermissions.SPECTATE_BATTLE)
.then(argument("player", EntityArgument.player())
.executes(::execute))
dispatcher.register(command)
}
private fun execute(context: CommandContext<CommandSourceStack>): Int {
val player = context.source.playerOrException
val target = EntityArgument.getPlayer(context, "player")
SpectateBattleHandler.spectateBattle(target, player)
return Command.SINGLE_SUCCESS
}
}
@@ -557,10 +557,10 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
private fun pokemonExpressions(pokemon: Pokemon): List<String> {
return listOf(
"v.actioning_pokemon_name=\"${pokemon.species.name}\";",
"v.actioning_pokemon_name=\'${pokemon.species.name}\';",
"v.actioning_pokemon_level=${pokemon.level};",
"v.actioning_pokemon_ball=\"${pokemon.caughtBall.name}\";",
"v.actioning_pokemon_shiny=\"${pokemon.shiny}\";",
"v.actioning_pokemon_ball=\'${pokemon.caughtBall.name}\';",
"v.actioning_pokemon_shiny=\'${pokemon.shiny}\';",
)
}
@@ -959,6 +959,7 @@ open class PokemonEntity(
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.scaleModifier)
var result = this.exposedForm.hitbox.scale(scale)
result = result.withEyeHeight(this.exposedForm.eyeHeight(this) * result.height)
result = result.scale(this.scale)
return result
}
@@ -1310,7 +1311,7 @@ open class PokemonEntity(
}
} else if (form.behaviour.moving.swim.canBreatheUnderwater && !form.behaviour.moving.swim.canWalkOnWater) {
// Use half hitbox height for swimmers
val halfHeight = form.hitbox.height * form.baseScale / 2.0
val halfHeight = getDimensions(this.pose).height / 2.0
for (i in 1..halfHeight.toInt()) {
blockPos = blockPos.below()
if (!this.level().isWaterAt(blockPos) || !this.level().getBlockState(blockPos).getCollisionShape(this.level(), blockPos).isEmpty) {
@@ -1812,6 +1813,11 @@ open class PokemonEntity(
return this.riding.speed(this, controller)
}
fun useRidingAltPose(): Boolean {
val driver = this.controllingPassenger as? Player ?: return false
return this.riding.useRidingAltPose(this, driver)
}
var jumpInputStrength: Int = 0 // move this
override fun onPlayerJump(strength: Int) {
// See if this controls the hot bar element
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.net.serverhandling.battle
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
@@ -18,9 +19,7 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleInitializePacke
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
import com.cobblemon.mod.common.net.messages.server.battle.SpectateBattlePacket
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.traceFirstEntityCollision
import com.cobblemon.mod.common.util.*
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
@@ -48,9 +47,10 @@ object SpectateBattleHandler : ServerNetworkPacketHandler<SpectateBattlePacket>
player.sendSystemMessage(lang("ui.interact.failed").yellow())
return
}
battle.spectators.add(player.uuid)
player.sendPacket(BattleInitializePacket(battle, null))
player.sendPacket(BattleMessagePacket(battle.chatLog))
this.spectateBattle(targetedPlayerEntity, player)
// Handle music
target?.battleTheme?.let { player.sendPacket(BattleMusicPacket(it)) }
}
else {
@@ -58,4 +58,27 @@ object SpectateBattleHandler : ServerNetworkPacketHandler<SpectateBattlePacket>
}
}
fun spectateBattle(target: ServerPlayer, player: ServerPlayer) {
if (player == target) {
player.sendSystemMessage(lang("command.spectatebattle.self_spectate_disallowed").red())
return
}
if (!target.isInBattle()) {
player.sendSystemMessage(lang("command.spectatebattle.player_not_in_battle").red())
return
}
if (player.isInBattle()) {
player.sendSystemMessage(lang("command.spectatebattle.while_battling_disallowed").red())
return
}
val battle = BattleRegistry.getBattleByParticipatingPlayer(target) ?: return
battle.spectators.add(player.uuid)
player.sendPacket(BattleInitializePacket(battle, null))
player.sendPacket(BattleMessagePacket(battle.chatLog))
}
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.pokemon.riding.controllers
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.MoLangMath.lerp
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Rollable
import com.cobblemon.mod.common.api.riding.controller.RideController
@@ -19,7 +19,9 @@ import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.riding.states.BirdAirState
import com.cobblemon.mod.common.util.*
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.Mth
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
@@ -32,81 +34,37 @@ class BirdAirController : RideController {
override val poseProvider = PoseProvider(PoseType.HOVER)
.with(PoseOption(PoseType.FLY) {
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
it.deltaMovement.length() > 0.3 })
/*
.with(PoseOption(PoseType.FLY) {
val player = it.passengers.firstOrNull() as? Player ?: return@PoseOption false
player.zza > 0.0f // Checks if the player is actively attempting to move forward
})
*/
it.deltaMovement.length() > 0.5 })
override val condition: (PokemonEntity) -> Boolean = { true }
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
private set
var gravity: Expression = "0".asExpression()
var topSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
private set
var horizontalAcceleration: Expression = "0.1".asExpression()
var glideTopSpeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
private set
var verticalAcceleration: Expression = "0.1".asExpression()
// Max accel is a whole 1.0 in 3 seconds. The conversion in the function below is to convert seconds to ticks
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
private set
//Seconds self propelled flight (glide is not self propelled in this case)
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
private set
//max y level for the ride
var altitudeExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
private set
// Where 90 is the width of the range and 45 is the offset.
// So the handling interpolates between 135 and 45
var handlingExpr: Expression = "(q.get_ride_stats('SKILL', 'AIR') / 100) * 90 + 45".asExpression()
var infiniteStamina: Expression = "false".asExpression()
private set
var topSpeedExpr: Expression = "(q.get_ride_stats('SPEED', 'AIR') / 100) * (1.0 - 0.35) + 0.35".asExpression()
private set
//Reaches max top speed from a standstill at minimum 8 seconds and max 3 seconds.
//This is a bit of a confusing expression and likely should be cleaned up to be more readable
var accelExpr: Expression = "(q.get_ride_stats('ACCELERATION', 'AIR') / 100) * (0.01666666666 - 0.00625) + 0.00625".asExpression()
var infiniteAltitude: Expression = "false".asExpression()
private set
override fun speed(entity: PokemonEntity, driver: Player): Float {
//Retrieve the current bird controller state
//val state = entity.riding.getState(KEY) { e -> BirdAirControllerState(e) }
//Retrieve the current bird controller state and stats
val state = getState(entity, ::BirdAirState)
//retrieve stats
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val accel = getRuntime(entity).resolveDouble(accelExpr)
val maxGlideSpeed = topSpeed * 2
var glideSpeedChange = 0.00
//speed up and slow down based on input
if (driver.zza > 0.0 && state.currSpeed < topSpeed) {
state.currSpeed = min(state.currSpeed + accel , topSpeed)
} else if (driver.zza < 0.0 && state.currSpeed > 0.0) {
//Decelerate is currently always a constant half of max acceleration.
state.currSpeed = max(state.currSpeed - (0.0166 / 2), 0.0)
}
val rollable = driver as? Rollable
if (rollable != null) {
glideSpeedChange = sin(Math.toRadians(rollable.pitch.toDouble()))
glideSpeedChange = glideSpeedChange.pow(3)
//TODO: Possibly create a deadzone around parallel where speed isn't taken away from?
if (glideSpeedChange <= 0.0) {
//Ensures that a propelling force is still able to be applied when
//climbing in height
if (driver.zza <= 0) {
//speed decrease should be 2x speed increase?
state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
}
} else {
// only add to the speed if it hasn't exceeded the current
//glide angles maximum amount of speed that it can give.
state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
}
}
//air resistance
state.currSpeed = max(state.currSpeed - 0.005, 0.0)
return state.currSpeed.toFloat()
return state.rideVel.length().toFloat()
}
override fun rotation(entity: PokemonEntity, driver: LivingEntity): Vec2 {
@@ -118,62 +76,60 @@ class BirdAirController : RideController {
var leftForce = 0.0
var upForce = 0.0
var forwardForce = 0.0
val state = getState(entity, ::BirdAirState)
//Perform ride velocity update
calculateRideSpaceVel(entity, driver, state)
//Translate ride space velocity to world space velocity.
val rollable = driver as? Rollable
if (rollable != null) {
//Need to deadzone this when straight up or down
// test with and without strafing added. May be wanted while moving
leftForce = -1.0 * sin(Math.toRadians(rollable.roll.toDouble()))
upForce = -1.0 * sin(Math.toRadians(rollable.pitch.toDouble()))
upForce += -1.0 * sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
forwardForce += cos(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.z
forwardForce = cos(Math.toRadians(rollable.pitch.toDouble()))
upForce += cos(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.y
forwardForce += sin(Math.toRadians(rollable.pitch.toDouble())) * state.rideVel.y
}
//TODO: Add in an upwards and downwards force when pressing space maybe?
//Leave out tap to flap right now
/*
var jumpVector = Vec3.ZERO
if (entity.jumpInputStrength > 0) {
val runtime = getRuntime(entity)
runtime.environment.query.addFunction("jump_strength") { DoubleValue(entity.jumpInputStrength.toDouble()) }
val jumpForces = this.jumpVector.map { runtime.resolveFloat(it) }
jumpVector = Vec3(jumpForces[0].toDouble(), jumpForces[1].toDouble(), jumpForces[2].toDouble())
jumpVector = jumpVector.toVector3f().mul((driver as Rollable).orientation).toVec3d()
entity.jumpInputStrength = 0
entity.addDeltaMovement(jumpVector)
//Bring the ride out of the sky when stamina is depleted.
if (state.stamina <= 0.0) {
upForce -= 0.3
}
*/
//Retrieve the current composite controller state
val state = getState(entity, ::BirdAirState)
val velocity = Vec3(0.0 , upForce * state.currSpeed, forwardForce * state.currSpeed)
val altitudeLimit = getRuntime(entity).resolveDouble(altitudeExpr)
//Only limit altitude if altitude is not infinite
if (!getRuntime(entity).resolveBoolean(infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (entity.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(state.rideVel.x , upForce, forwardForce)
return velocity
}
override fun angRollVel(entity: PokemonEntity, driver: Player, deltaTime: Double): Vec3 {
val rollable = driver as? Rollable
val state = getState(entity, ::BirdAirState)
//TODO: Tie in handling
val handling = getRuntime(entity).resolveDouble(handlingExpr)
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val rotationChangeRate = 10.0
if (rollable != null) {
var yawForce = rotationChangeRate * sin(Math.toRadians(rollable.roll.toDouble()))
//for a bit of correction on the rolls limit it to a quarter the amount
var pitchForce = -0.25 * rotationChangeRate * abs(sin(Math.toRadians(rollable.roll.toDouble())))
//for a bit of correction on the rolls influence pitch as well
var pitchForce = -0.35 * rotationChangeRate * abs(sin(Math.toRadians(rollable.roll.toDouble())))
//limit rotation modulation when pitched up heavily or pitched down heavily
yawForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble())))
pitchForce *= abs(cos(Math.toRadians(rollable.pitch.toDouble()))) * 1.5
//Create a deadzone for easier leveling out
if (rollable.roll <= 0.0 && rollable.roll >= -0.0) {
yawForce = 0.0
pitchForce = 0.0
}
return Vec3(yawForce, pitchForce, 0.0)
}
@@ -191,42 +147,56 @@ class BirdAirController : RideController {
deltaTime: Double
): Vec3 {
if (driver !is Rollable) return Vec3.ZERO
val rollable = driver as Rollable
val state = getState(entity, ::BirdAirState)
val handling = getRuntime(entity).resolveDouble(handlingExpr)
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
//Smooth out mouse input.
val smoothingSpeed = 4
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
var xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val YInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1 * invertPitch, deltaTime * smoothingSpeed);
val xInput = xMouseSmoother.getNewDeltaValue(xMouse * 0.1 * invertRoll, deltaTime * smoothingSpeed);
val yInput = yMouseSmoother.getNewDeltaValue(yMouse * 0.1 * invertPitch, deltaTime * smoothingSpeed);
//limit rolling based on healing
val rotLimit = handling
//limit rolling based on handling and current speed.
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
val rotMin = 15.0
var rollForce = xInput
val rotLimit = max(handling * normalizeVal(state.rideVel.length(), 0.0, topSpeed).pow(3), rotMin)
//Limit roll by non linearly decreasing inputs towards
// a rotation limit based on the current distance from
// that rotation limit
if (abs(rollable.roll + xInput) < rotLimit) {
if (sign(xInput) == sign(rollable.roll).toDouble()) {
if (abs(rollable.roll + rollForce) < rotLimit) {
if (sign(rollForce) == sign(rollable.roll).toDouble()) {
val d = abs(abs(rollable.roll) - rotLimit)
xInput *= (d.pow(2)) / (rotLimit.pow(2))
rollForce *= (d.pow(2)) / (rotLimit.pow(2))
}
} else if (sign(xInput) == sign(rollable.roll).toDouble()) {
xInput = 0.0
} else if (sign(rollForce) == sign(rollable.roll).toDouble()) {
rollForce = 0.0
}
//Give the ability to yaw with x mouse input when at low speeds.
val yawForce = xInput * ( 1.0 - normalizeVal(state.rideVel.length(), 0.0, topSpeed).pow(3))
//yaw, pitch, roll
return Vec3(0.0, YInput, xInput)
return Vec3(yawForce, yInput, rollForce)
}
override fun canJump(
entity: PokemonEntity,
driver: Player
): Boolean {
return true
return false
}
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
//Retrieve stamina from state
val state = getState(entity, ::BirdAirState)
return (state.stamina / 1.0f)
}
override fun jumpForce(
@@ -243,29 +213,190 @@ class BirdAirController : RideController {
override fun shouldRoll(entity: PokemonEntity) = true
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val glideTopSpeed = getRuntime(entity).resolveDouble(glideTopSpeedExpr)
val state = getState(entity, ::BirdAirState)
//Must I ensure that topspeed is greater than minimum?
val normalizedGlideSpeed = normalizeVal(state.rideVel.length(), topSpeed, glideTopSpeed)
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
}
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = false
override fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean {
val state = getState(entity, ::BirdAirState)
return state.gliding
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeString(gravity.toString())
buffer.writeString(horizontalAcceleration.toString())
buffer.writeString(verticalAcceleration.toString())
buffer.writeString(topSpeedExpr.toString())
buffer.writeString(glideTopSpeedExpr.toString())
buffer.writeString(accelExpr.toString())
buffer.writeString(handlingExpr.toString())
buffer.writeString(altitudeExpr.toString())
buffer.writeString(infiniteStamina.toString())
buffer.writeString(infiniteAltitude.toString())
}
override fun decode(buffer: RegistryFriendlyByteBuf) {
gravity = buffer.readString().asExpression()
horizontalAcceleration = buffer.readString().asExpression()
verticalAcceleration = buffer.readString().asExpression()
topSpeedExpr = buffer.readString().asExpression()
glideTopSpeedExpr = buffer.readString().asExpression()
accelExpr = buffer.readString().asExpression()
handlingExpr = buffer.readString().asExpression()
altitudeExpr = buffer.readString().asExpression()
infiniteStamina = buffer.readString().asExpression()
infiniteAltitude = buffer.readString().asExpression()
}
companion object {
val KEY = cobblemonResource("air/bird")
}
/*
* Normalizes the given value between a min and a max.
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
* and 1.0 represents x is at or above it.
*/
fun normalizeVal(x: Double, min: Double, max: Double): Double {
require(max > min) { "max must be greater than min" }
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
}
/*
* Calculates the change in the ride space vector due to player input and ride state
*/
fun calculateRideSpaceVel(entity: PokemonEntity, driver: Player, state: BirdAirState) {
//retrieve stats
val topSpeed = getRuntime(entity).resolveDouble(topSpeedExpr)
val glideTopSpeed = getRuntime(entity).resolveDouble(glideTopSpeedExpr)
val accel = getRuntime(entity).resolveDouble(accelExpr)
val staminaStat = getRuntime(entity).resolveDouble(staminaExpr)
var glideSpeedChange = 0.0
val currSpeed = state.rideVel.length()
//Flag for determining if player is actively inputting
var activeInput = false
//speed up and slow down based on input
if (driver.zza != 0.0f && state.stamina > 0.0) {
//make sure it can't exceed top speed
val forwardInput = when {
driver.zza > 0 && state.rideVel.z > topSpeed -> 0.0
driver.zza < 0 && state.rideVel.z < (-topSpeed / 3.0) -> 0.0
else -> driver.zza.sign
}
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
(state.rideVel.z + (accel * forwardInput.toDouble())))
activeInput = true
}
val rollable = driver as? Rollable
if (rollable != null) {
//Base glide speed change on current pitch of the ride.
glideSpeedChange = sin(Math.toRadians(rollable.pitch.toDouble()))
glideSpeedChange = glideSpeedChange.pow(3) * 0.5
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
if (glideSpeedChange <= 0.0) {
//Ensures that a propelling force is still able to be applied when
//climbing in height
if (driver.zza <= 0) {
//speed decrease should be 2x speed increase?
//state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
lerp( state.rideVel.z, 0.0,glideSpeedChange * -0.0166 * 2 ))
}
} else {
// only add to the speed if it hasn't exceeded the current
//glide angles maximum amount of speed that it can give.
//state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
min(state.rideVel.z + ((0.0166 * 2) * glideSpeedChange), glideTopSpeed))
}
}
//Lateral movement based on driver input.
val latTopSpeed = topSpeed / 2.0
if (driver.xxa != 0.0f && state.stamina > 0.0) {
state.rideVel = Vec3(
(state.rideVel.x + (accel * driver.xxa)).coerceIn(-latTopSpeed, latTopSpeed),
state.rideVel.y,
state.rideVel.z)
activeInput = true
}
else {
state.rideVel = Vec3(
lerp(state.rideVel.x, 0.0, latTopSpeed / 20.0),
state.rideVel.y,
state.rideVel.z)
}
//Vertical movement based on driver input.
val vertTopSpeed = topSpeed / 2.0
val vertInput = when {
Minecraft.getInstance().options.keyJump.isDown() -> 1.0
Minecraft.getInstance().options.keyShift.isDown() -> -1.0
else -> 0.0
}
if (vertInput != 0.0 && state.stamina > 0.0) {
state.rideVel = Vec3(
state.rideVel.x,
(state.rideVel.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
state.rideVel.z)
activeInput = true
}
else {
state.rideVel = Vec3(
state.rideVel.x,
lerp(state.rideVel.y, 0.0, vertTopSpeed / 20.0),
state.rideVel.z)
}
//Check if the ride should be gliding
if (activeInput && state.stamina > 0.0) {
state.gliding = false
}else {
state.gliding = true
}
//Only perform stamina logic if the ride does not have infinite stamina
if (!getRuntime(entity).resolveBoolean(infiniteStamina)) {
if (activeInput) {
state.stamina -= (0.05 / staminaStat).toFloat()
}
//Lose a base amount of stamina just for being airborne
state.stamina -= (0.01 / staminaStat).toFloat()
}
else
{
state.stamina = 1.0f
}
//air resistance
state.rideVel = Vec3(
state.rideVel.x,
state.rideVel.y,
lerp( state.rideVel.z,0.0, topSpeed / ( 20.0 * 30.0)))
}
}
@@ -49,9 +49,9 @@ class JetAirController : RideController {
var accelExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
private set
// Between 60 seconds and 10 seconds at the lowest when at full speed.
var staminaExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 10.0)".asExpression()
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
private set
var jumpExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 300.0, 128.0)".asExpression()
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
private set
// Make configurable by json
@@ -123,14 +123,12 @@ class JetAirController : RideController {
upForce = if(state.stamina > 0.0) upForce else -0.7
val altitudeLimit = getRuntime(entity).resolveDouble(jumpExpr)
val pushingHeightLimit = if(getRuntime(entity).resolveBoolean(infiniteAltitude)) false
else (entity.y >= 128 && entity.xRot <= 0)
//Incorporate the altitude limiter.
//Apply downwards force if limit is reached. Resist upwards force on a buffer of 30 blocks past the limit.
upForce = if(pushingHeightLimit) upForce - upForce * ((entity.y - altitudeLimit) / 30) - 0.5
else upForce
//Only limit altitude if altitude is not infinite
if (!getRuntime(entity).resolveBoolean(infiniteAltitude)) {
//Provide a hard limit on altitude
upForce = if (entity.y >= altitudeLimit && upForce > 0) 0.0 else upForce
}
val velocity = Vec3(0.0 , upForce, forwardForce )
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.util.getString
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.writeString
import net.minecraft.client.Minecraft
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.SmoothDouble
import net.minecraft.world.entity.LivingEntity
@@ -71,15 +72,37 @@ class RunUpToFlightCompositeController : RideController {
driver: Player
): Float {
val state = getState(entity, ::RunUpToFlightCompositeState)
if (state.activeController == flightController && entity.onGround() && state.timeTransitioned + 20 < entity.level().gameTime) {
if (state.activeController == flightController && entity.onGround() &&
state.timeTransitioned + 20 < entity.level().gameTime) {
//Pass the speed to the next state
val flightState = flightController.getState(entity, ::BirdAirState)
val groundState = landController.getState(entity, ::GenericLandState)
groundState.currSpeed = flightState.currSpeed
groundState.rideVel = flightState.rideVel
groundState.stamina = flightState.stamina
getState(entity, ::RunUpToFlightCompositeState).activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = landController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, false)
it.timeTransitioned = entity.level().gameTime
}
}
else if (state.activeController == landController && Minecraft.getInstance().options.keyJump.isDown &&
state.timeTransitioned + 20 < entity.level().gameTime) {
//Pass the speed to the next state
val flightState = flightController.getState( entity, ::BirdAirState )
val groundState = landController.getState( entity, ::GenericLandState )
flightState.currSpeed = groundState.currSpeed
flightState.rideVel = groundState.rideVel
flightState.stamina = groundState.stamina
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
return getActiveController(entity).speed(entity, driver)
@@ -101,6 +124,14 @@ class RunUpToFlightCompositeController : RideController {
return getActiveController(entity).angRollVel(entity, driver, deltaTime)
}
override fun rideFovMult(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).rideFovMult(entity, driver)
}
override fun setRideBar(entity: PokemonEntity, driver: Player): Float {
return getActiveController(entity).setRideBar(entity, driver)
}
override fun canJump(entity: PokemonEntity, driver: Player): Boolean {
return getActiveController(entity).canJump(entity, driver)
}
@@ -111,19 +142,9 @@ class RunUpToFlightCompositeController : RideController {
jumpStrength: Int
): Vec3 {
val controller = getActiveController(entity)
if (controller == landController && jumpStrength >= getRuntime(entity).resolveInt(minimumJump)) {
val state = getState(entity, ::CompositeState)
val isJumping = Minecraft.getInstance().options.keyJump.isDown
//Pass the speed to the next state
val flightState = flightController.getState(entity, ::BirdAirState )
val groundState = landController.getState(entity, ::GenericLandState)
flightState.currSpeed = groundState.currSpeed
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = flightController
entity.setBehaviourFlag(PokemonBehaviourFlag.FLYING, true)
it.timeTransitioned = entity.level().gameTime
}
}
return controller.jumpForce(entity, driver, jumpStrength)
}
@@ -139,6 +160,10 @@ class RunUpToFlightCompositeController : RideController {
override fun useAngVelSmoothing(entity: PokemonEntity): Boolean = getActiveController(entity).useAngVelSmoothing(entity)
override fun useRidingAltPose(entity: PokemonEntity, driver: Player): Boolean {
return getActiveController(entity).useRidingAltPose(entity, driver)
}
override fun rotationOnMouseXY
(entity: PokemonEntity,
driver: Player,
@@ -84,6 +84,7 @@ class RunUpToJetFlightCompositeController : RideController {
val groundState = landController.getState(entity, ::GenericLandState)
groundState.currSpeed = flightState.currSpeed
groundState.stamina = flightState.stamina
groundState.rideVel = flightState.rideVel
//Clear accumulated rotation input when transferring to ground input
flightState.currMouseXForce = 0.0
@@ -108,6 +109,7 @@ class RunUpToJetFlightCompositeController : RideController {
val groundState = landController.getState(entity, ::GenericLandState)
flightState.currSpeed = groundState.currSpeed
flightState.stamina = groundState.stamina
flightState.rideVel = groundState.rideVel
getState(entity, ::RunUpToFlightCompositeState).let {
it.activeController = flightController
@@ -14,5 +14,7 @@ import net.minecraft.world.phys.Vec3
data class BirdAirState(val entity: PokemonEntity) : RidingState {
var currSpeed: Double = 0.0
var lastVelocity: Vec3 = Vec3(0.0, 0.0, 0.0)
var stamina: Float = 1.0f
var rideVel: Vec3 = Vec3.ZERO
var gliding: Boolean = false
}
@@ -124,8 +124,14 @@
},
"pokeball": {
"scale": {
"0.4583": {"pre": [0, 0, 0], "post": [0.3, 0.3, 0.3]},
"0.5417": {"pre": [0.3, 0.3, 0.3], "post": [0.3, 0.3, 0.3]},
"0.4583": {
"pre": [0, 0, 0],
"post": [0.3, 0.3, 0.3]
},
"0.5417": {
"pre": [0.3, 0.3, 0.3],
"post": [0.3, 0.3, 0.3]
},
"0.7083": [1, 1, 1]
}
},
@@ -556,105 +562,6 @@
}
}
},
"animation.trainer_generic.send_out": {
"animation_length": 3.625,
"bones": {
"torso": {
"rotation": {
"0.0": [1, 0, 0],
"0.9375": [1, 0, 0],
"1.375": [1, -7.5, 0],
"2.875": [1, -7.5, 0],
"3.375": [1, 0, 0],
"3.625": [1, 0, 0]
}
},
"head": {
"rotation": {
"0.0": [-1, 0, 0],
"0.9375": [-1, 0, 0],
"1.375": [-1, 7.5, 0],
"2.875": [-1, 7.5, 0],
"3.375": [-1, 0, 0],
"3.625": [-1, 0, 0]
}
},
"arm_ball": {
"rotation": {
"0.0": [3, 0, 3.5],
"0.5": [3, 0, 3.5],
"0.9375": {
"pre": [14.5, 2.5, -17.5],
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"1.125": {
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"1.375": [-91, 7.5, 0],
"2.875": [-91, 7.5, 0],
"3.1875": {
"pre": [5, 2.5, -0.5],
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"3.375": {
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"3.625": [3, 0, 3.5]
},
"position": {
"0.0": [0, 0.1, 0],
"3.625": [0, 0.1, 0]
}
},
"arm_left": {
"rotation": {
"0.0": [3, 0, -3.5],
"0.9375": [3, 0, -3.5],
"1.375": [13, 0, -6],
"2.875": [13, 0, -6],
"3.375": [3, 0, -3.5]
},
"position": [0, 0.1, 0]
},
"pokeball": {
"scale": {
"0.9375": {"pre": [0, 0, 0], "post": [0.3, 0.3, 0.3]},
"1.0": {"pre": [0.3, 0.3, 0.3], "post": [0.3, 0.3, 0.3]},
"1.1875": [1, 1, 1],
"3.0625": [1, 1, 1],
"3.25": [0.4, 0.4, 0.4],
"3.375": {"pre": [0.4, 0.4, 0.4], "post": [0, 0, 0]}
}
},
"leg_right": {
"rotation": {
"0.9375": [0, 8, 0.5],
"1.375": [1, 10, 0.5],
"2.875": [1, 10, 0.5],
"3.375": [0, 8, 0.5]
},
"position": {
"0.9375": [-0.2, 0, 0],
"1.375": [-0.2, 0, -0.2],
"2.875": [-0.2, 0, -0.2],
"3.375": [-0.2, 0, 0]
}
},
"leg_left": {
"rotation": {
"0.9375": [0, -8, -0.5],
"1.375": [0, -10, -0.5],
"2.875": [0, -10, -0.5],
"3.375": [0, -8, -0.5]
},
"position": [0.2, 0, 0]
}
}
},
"animation.trainer_generic.command": {
"animation_length": 2,
"bones": {
@@ -996,6 +903,276 @@
"scale": 0
}
}
},
"animation.trainer_generic.return": {
"animation_length": 3.625,
"bones": {
"torso": {
"rotation": {
"0.0": [1, 0, 0],
"0.9375": [1, 0, 0],
"1.375": [1, -7.5, 0],
"2.875": [1, -7.5, 0],
"3.375": [1, 0, 0],
"3.625": [1, 0, 0]
}
},
"head": {
"rotation": {
"0.0": [-1, 0, 0],
"0.9375": [-1, 0, 0],
"1.375": [-1, 7.5, 0],
"2.875": [-1, 7.5, 0],
"3.375": [-1, 0, 0],
"3.625": [-1, 0, 0]
}
},
"arm_ball": {
"rotation": {
"0.0": [3, 0, 3.5],
"0.5": [3, 0, 3.5],
"0.9375": {
"pre": [14.5, 2.5, -17.5],
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"1.125": {
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"1.375": [-91, 7.5, 0],
"2.875": [-91, 7.5, 0],
"3.1875": {
"pre": [5, 2.5, -0.5],
"post": [5, 2.5, -0.5],
"lerp_mode": "catmullrom"
},
"3.375": {
"post": [14.5, 2.5, -17.5],
"lerp_mode": "catmullrom"
},
"3.625": [3, 0, 3.5]
},
"position": {
"0.0": [0, 0.1, 0],
"3.625": [0, 0.1, 0]
}
},
"arm_left": {
"rotation": {
"0.0": [3, 0, -3.5],
"0.9375": [3, 0, -3.5],
"1.375": [13, 0, -6],
"2.875": [13, 0, -6],
"3.375": [3, 0, -3.5]
},
"position": [0, 0.1, 0]
},
"pokeball": {
"scale": {
"0.9375": {
"pre": [0, 0, 0],
"post": [0.3, 0.3, 0.3]
},
"1.0": [0.3, 0.3, 0.3],
"1.1875": [1, 1, 1],
"3.0625": [1, 1, 1],
"3.25": [0.4, 0.4, 0.4],
"3.375": {
"pre": [0.4, 0.4, 0.4],
"post": [0, 0, 0]
}
}
},
"leg_right": {
"rotation": {
"0.9375": [0, 8, 0.5],
"1.375": [1, 10, 0.5],
"2.875": [1, 10, 0.5],
"3.375": [0, 8, 0.5]
},
"position": {
"0.9375": [-0.2, 0, 0],
"1.375": [-0.2, 0, -0.2],
"2.875": [-0.2, 0, -0.2],
"3.375": [-0.2, 0, 0]
}
},
"leg_left": {
"rotation": {
"0.9375": [0, -8, -0.5],
"1.375": [0, -10, -0.5],
"2.875": [0, -10, -0.5],
"3.375": [0, -8, -0.5]
},
"position": [0.2, 0, 0]
}
},
"timeline": {
"1.0": "q.render_item(v.actioning_pokemon_ball, 'item_right');",
"3.3125": "q.clear_items();"
}
},
"animation.trainer_generic.send_out": {
"animation_length": 2.875,
"bones": {
"torso_crouch": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"arms": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"torso_item_right": {
"rotation": [0.5, 0, 0]
},
"arms_item_right": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"arm_item_right": {
"rotation": {
"0.9167": [1, 0, 2.5],
"1.4167": {
"pre": [-85, 10, -10],
"post": [-85, 10, -10],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-72.5, -30, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, 2.5]
},
"position": [0, 0.1, 0]
},
"torso_item_left": {
"rotation": [0.5, 0, 0]
},
"arms_item_left": {
"rotation": {
"0.9167": [0, 0, 0],
"1.4167": {
"pre": [0, 10, 0],
"post": [0, 10, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, -10, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [0, 0, 0]
}
},
"arm_item_left": {
"rotation": {
"0.9167": [1, 0, -2.5],
"1.4167": {
"pre": [4, 0, 0],
"post": [4, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-8, 0, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, -2.5]
},
"position": [0, 0.1, 0]
},
"torso": {
"rotation": [0.5, 0, 0]
},
"head": {
"rotation": [-0.5, 0, 0]
},
"arm_right": {
"rotation": {
"0.9167": [1, 0, 2.5],
"1.4167": {
"pre": [-85, 10, -10],
"post": [-85, 10, -10],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-72.5, -30, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, 2.5]
},
"position": [0, 0.1, 0]
},
"arm_left": {
"rotation": {
"0.9167": [1, 0, -2.5],
"1.4167": {
"pre": [4, 0, 0],
"post": [4, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-8, 0, 0],
"lerp_mode": "catmullrom"
},
"1.7917": [1, 0, -2.5]
},
"position": [0, 0.1, 0]
},
"leg_right": {
"rotation": [0, 2, 0.5],
"position": [-0.05, 0, 0]
},
"leg_left": {
"rotation": [0, -2, -0.5],
"position": [0.05, 0, 0]
}
},
"timeline": {
"0.0": "q.render_item(v.actioning_pokemon_ball, 'item_right');",
"1.5833": "q.render_item('air', 'item_right');"
}
}
},
"geometry.animations": {
"texturewidth": 64,
"textureheight": 64
},
"geometry.unknown": {
"texturewidth": 16,
"textureheight": 16
}
}
@@ -81,6 +81,14 @@
{"origin": [-7, 12, -2], "size": [3, 12, 4], "inflate": 0.25, "uv": [40, 32]}
]
},
{
"name": "item_right",
"parent": "arm_right",
"pivot": [-5.5, 12, -2],
"locators": {
"item_right": [-5.5, 12, -2]
}
},
{
"name": "pokeball",
"parent": "arm_ball",
@@ -99,6 +107,14 @@
{"origin": [4, 12, -2], "size": [3, 12, 4], "inflate": 0.25, "uv": [48, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [5.5, 12, -2],
"locators": {
"item_left": [5.5, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -97,6 +97,14 @@
{"origin": [-7, 12, -2], "size": [3, 11, 4], "inflate": 0.25, "uv": [40, 31]}
]
},
{
"name": "item_right",
"parent": "arm_right",
"pivot": [-5.5, 12, -2],
"locators": {
"item_right": [-5.5, 12, -2]
}
},
{
"name": "arm_left",
"parent": "torso",
@@ -106,6 +114,14 @@
{"origin": [4, 12, -2], "size": [3, 11, 4], "inflate": 0.25, "uv": [46, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [5.5, 12, -2],
"locators": {
"item_left": [5.5, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -81,6 +81,14 @@
{"origin": [-8, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [40, 32]}
]
},
{
"name": "item_right",
"parent": "arm_right",
"pivot": [-6, 12, -2],
"locators": {
"item_right": [-6, 12, -2]
}
},
{
"name": "pokeball",
"parent": "arm_ball",
@@ -99,6 +107,14 @@
{"origin": [4, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [48, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [6, 12, -2],
"locators": {
"item_left": [6, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -79,29 +79,18 @@
"cubes": [
{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [40, 16]},
{"origin": [-8, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [40, 32]}
]
},
{
"name": "pokeball",
"parent": "arm_ball",
"pivot": [-6, 12, 0],
"cubes": [
{"origin": [-8, 8, -2], "size": [4, 2, 4], "pivot": [-6, 10, 0], "rotation": [90, 0, 0], "uv": [0, 122]}
],
"locators": {
"beam": [-6, 8, 0]
}
},
{
"name": "pokeball_lid",
"parent": "pokeball",
"pivot": [-6, 10, 0],
"rotation": [90, 0, 0],
"cubes": [
{"origin": [-6.5, 9.5, -2.02], "size": [1, 1, 0], "uv": [0, 123]},
{"origin": [-7, 9, -2.01], "size": [2, 2, 0], "uv": [0, 124]},
{"origin": [-8, 10, -2], "size": [4, 2, 4], "uv": [0, 116]}
]
"name": "item_right",
"parent": "arm_right",
"pivot": [-6, 12, -2],
"locators": {
"item_right": [-6, 12, -2]
}
},
{
"name": "arm_left",
@@ -112,6 +101,14 @@
{"origin": [4, 12, -2], "size": [4, 12, 4], "inflate": 0.25, "uv": [48, 48]}
]
},
{
"name": "item_left",
"parent": "arm_left",
"pivot": [6, 12, -2],
"locators": {
"item_left": [6, 12, -2]
}
},
{
"name": "leg_right",
"parent": "body",
@@ -3,11 +3,16 @@
"portraitTranslation": [0.0, 1.1, 0.0],
"profileScale": 1.0,
"profileTranslation": [0.0, -0.5, 0.0],
"properties": {
"item_display_context": "THIRD_PERSON_RIGHT_HAND",
"item_right_display_context": "THIRD_PERSON_RIGHT_HAND",
"item_left_display_context": "THIRD_PERSON_LEFT_HAND"
},
"animations": {
"win": "q.bedrock_primary('trainer_generic', 'win', q.curve('symmetrical_wide'))",
"lose": "q.bedrock_primary('trainer_generic', 'lose', q.curve('symmetrical_wide'))",
"send_out": "q.bedrock_primary('trainer_generic', 'send_out', q.curve('symmetrical'), q.exclude_labels('look'))",
"recall": "q.bedrock_primary('trainer_generic', 'send_out', q.curve('symmetrical_wide'), q.exclude_labels('look'))",
"recall": "q.bedrock_primary('trainer_generic', 'return', q.curve('symmetrical_wide'), q.exclude_labels('look'))",
"punch_left": "q.primary_animation(q.punch('head', 'body', 'arm_left', 'arm_right', false), q.curve('symmetrical_wide'), q.exclude_labels('look'))",
"punch_right": "q.primary_animation(q.punch('head', 'body', 'arm_right', 'arm_left', true), q.curve('symmetrical_wide'), q.exclude_labels('look'))"
},
@@ -14543,7 +14543,7 @@
"rotation": [0, -6, -15]
},
"wing_right_elbow": {
"rotation": [0, -6, -15],
"rotation": [0, "-6", -15],
"position": [0.45, 0.1, 0]
},
"wing_right_membrane": {
@@ -20,7 +20,7 @@
"parent": "charizard",
"pivot": [0, 0, -1],
"locators": {
"root": [0, 0, -1]
"root": [0, 0, 0]
}
},
{
@@ -28,7 +28,7 @@
"parent": "charizard",
"pivot": [0, 41, -1],
"locators": {
"top": [0, 41, -1]
"top": [0, 41, 0]
}
},
{
@@ -116,8 +116,7 @@
{
"name": "head_correction",
"parent": "head",
"pivot": [0, 43.5, -0.5],
"rotation": [-95, 0, 0]
"pivot": [0, 43.5, -0.5]
},
{
"name": "head_ai",
@@ -128,15 +127,20 @@
"name": "head_angle",
"parent": "head_ai",
"pivot": [0, 43.5, -0.5],
"rotation": [95, 0, 0],
"cubes": [
{"origin": [-3.5, 41.5, -2], "size": [7, 7, 5], "uv": [48, 0]},
{"origin": [-3.5, 41.5, -4], "size": [7, 4, 2], "uv": [95, 64]},
{"origin": [-2.5, 48, -2], "size": [5, 5, 2], "inflate": 0.02, "uv": [37, 31]},
{"origin": [-2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [-2, 52.51777, -2], "rotation": [45, 0, 15], "uv": [43, 42]},
{"origin": [2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [2, 52.51777, -2], "rotation": [45, 0, -15], "uv": [41, 42]}
{"origin": [-3.5, 41.5, -4], "size": [7, 4, 2], "uv": [95, 64]}
]
},
{
"name": "item_hat",
"parent": "head_angle",
"pivot": [0, 45, 3],
"rotation": [-90, 0, 0],
"locators": {
"item_hat": [0, 45, 3]
}
},
{
"name": "locator_head",
"parent": "head_angle",
@@ -146,59 +150,51 @@
"head": [0, 46.5, 3]
}
},
{
"name": "locator_mouth",
"parent": "head_angle",
"pivot": [0, 48.75, -3],
"rotation": [-90, 0, 0],
"locators": {
"mouth": [0, 48.75, -3]
}
},
{
"name": "locator_special_attack",
"parent": "head_angle",
"pivot": [0, 48.75, -3],
"rotation": [-90, 0, 0],
"locators": {
"special_attack": [0, 48.75, -3]
}
},
{
"name": "locator_jaw_upper",
"parent": "head_angle",
"pivot": [0, 51.75, -2.25],
"rotation": [-90, 0, 0],
"locators": {
"jaw1": [0, 51.75, -2.25]
}
},
{
"name": "brow",
"parent": "head_angle",
"pivot": [0, 48.275, 1.765],
"cubes": [
{"origin": [-2.5, 48.55, 1], "size": [5, 0, 1], "uv": [0, 14]},
{"origin": [-3.5, 47.5, 1.53], "size": [7, 1, 1], "inflate": 0.04, "uv": [0, 12]}
]
},
{
"name": "eye",
"parent": "head_angle",
"pivot": [0, -3.5, 1.5]
},
{
"name": "locator_face",
"parent": "eye",
"parent": "head_angle",
"pivot": [0, 53.75, -0.25],
"rotation": [-90, 0, 0],
"locators": {
"face": [0, 53.75, -0.25]
}
},
{
"name": "locator_item_face",
"parent": "head_angle",
"pivot": [0, 48.5, 0.5],
"rotation": [-90, 0, 0],
"locators": {
"item_face": [0, 48.5, 0.5]
}
},
{
"name": "locator_mouth",
"parent": "head_angle",
"pivot": [0, 45.75, -2],
"rotation": [-90, 0, 0],
"locators": {
"mouth": [0, 45.75, -2],
"special": [0, 45.75, -2]
}
},
{
"name": "eyes",
"parent": "head_angle",
"pivot": [0, -3.5, 1.5]
},
{
"name": "brow",
"parent": "eyes",
"pivot": [0, 48.275, 1.765],
"cubes": [
{"origin": [-2.5, 48.55, 1], "size": [5, 0, 1], "uv": [0, 14]},
{"origin": [-3.5, 47.5, 1.53], "size": [7, 1, 1], "inflate": 0.04, "uv": [0, 12]}
]
},
{
"name": "eye_right",
"parent": "eye",
"parent": "eyes",
"pivot": [-2.75, 47.75, 0.875],
"cubes": [
{"origin": [-3.5, 47.5, 0], "size": [2, 1, 2], "inflate": 0.01, "uv": [106, 36]},
@@ -226,7 +222,7 @@
},
{
"name": "eye_left",
"parent": "eye",
"parent": "eyes",
"pivot": [2.75, 47.75, 0.875],
"cubes": [
{"origin": [1.5, 47.5, 0], "size": [2, 1, 2], "inflate": 0.01, "uv": [44, 82]},
@@ -255,6 +251,91 @@
{"origin": [2.41, 46.4, 0.5], "size": [1, 1, 1], "inflate": 0.03, "uv": [39, 86]}
]
},
{
"name": "muzzle",
"parent": "head_angle",
"pivot": [-3, 48.5, -2],
"cubes": [
{"origin": [-2.5, 48, -2], "size": [5, 5, 2], "inflate": 0.02, "uv": [37, 31]},
{"origin": [-2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [-2, 52.51777, -2], "rotation": [45, 0, 15], "uv": [43, 42]},
{"origin": [2, 51.48744, -3.03033], "size": [0, 1, 1], "inflate": 0.01, "pivot": [2, 52.51777, -2], "rotation": [45, 0, -15], "uv": [41, 42]}
]
},
{
"name": "locator_jaw_upper",
"parent": "muzzle",
"pivot": [0, 51.75, -2.25],
"rotation": [-90, 0, 0],
"locators": {
"jaw1": [0, 51.75, -2.25]
}
},
{
"name": "nose",
"parent": "muzzle",
"pivot": [0, 53, 0],
"rotation": [-40, 0, 0],
"cubes": [
{"origin": [-2.5, 51.36698, -1.67861], "size": [5, 2, 2], "inflate": 0.03, "uv": [48, 12]},
{"origin": [-2, 53.40698, -1.17861], "size": [4, 0, 1], "uv": [0, 15]}
]
},
{
"name": "jaw",
"parent": "head_angle",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-3.5, 45.5, -4], "size": [7, 3, 2], "inflate": 0.01, "uv": [76, 20]}
]
},
{
"name": "jaw2",
"parent": "jaw",
"pivot": [0, 48.5, -4],
"rotation": [-7.5, 0, 0],
"cubes": [
{"origin": [-2, 48.49, -4], "size": [4, 4, 2], "uv": [0, 51]},
{"origin": [-1.5, 48.5, -2], "size": [3, 4, 0], "uv": [90, 0]},
{"origin": [1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [1.5, 52.52782, -2], "rotation": [45, 0, -7.5], "uv": [37, 39]},
{"origin": [-1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [-1.5, 52.52782, -2], "rotation": [45, 0, 7.5], "uv": [37, 38]}
]
},
{
"name": "locator_jaw_lower",
"parent": "jaw2",
"pivot": [0, 51.75, -1.75],
"locators": {
"jaw2": {
"offset": [0, 51.75, -1.75],
"rotation": [-90, 0, 0]
}
}
},
{
"name": "tongue",
"parent": "jaw",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-1.5, 45.5, -2], "size": [3, 3, 0], "uv": [87, 29]}
]
},
{
"name": "tongue2",
"parent": "tongue",
"pivot": [0, 48.5, -2],
"cubes": [
{"origin": [-1, 48.5, -2], "size": [2, 2, 0], "inflate": 0.01, "uv": [46, 38]}
]
},
{
"name": "tongue3",
"parent": "tongue2",
"pivot": [0, 50.5, -2],
"cubes": [
{"origin": [-1, 50.5, -2], "size": [2, 1, 0], "uv": [37, 43]},
{"origin": [-0.5, 51.5, -2], "size": [1, 1, 0], "uv": [45, 43]}
]
},
{
"name": "horns",
"parent": "head_angle",
@@ -300,72 +381,6 @@
"horn2": [2.5, 39, 2]
}
},
{
"name": "nose",
"parent": "head_angle",
"pivot": [0, 53, 0],
"rotation": [-40, 0, 0],
"cubes": [
{"origin": [-2.5, 51.36698, -1.67861], "size": [5, 2, 2], "inflate": 0.03, "uv": [48, 12]},
{"origin": [-2, 53.40698, -1.17861], "size": [4, 0, 1], "uv": [0, 15]}
]
},
{
"name": "jaw",
"parent": "head_angle",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-3.5, 45.5, -4], "size": [7, 3, 2], "inflate": 0.01, "uv": [76, 20]}
]
},
{
"name": "jaw2",
"parent": "jaw",
"pivot": [0, 48.5, -4],
"rotation": [-7.5, 0, 0],
"cubes": [
{"origin": [-2, 48.49, -4], "size": [4, 4, 2], "uv": [0, 51]},
{"origin": [-1.5, 48.5, -1.99], "size": [3, 4, 0], "uv": [90, 0]},
{"origin": [1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [1.5, 52.52782, -2], "rotation": [45, 0, -7.5], "uv": [37, 39]},
{"origin": [-1.5, 51.49749, -3.03033], "size": [0, 1, 1], "pivot": [-1.5, 52.52782, -2], "rotation": [45, 0, 7.5], "uv": [37, 38]}
]
},
{
"name": "locator_jaw_lower",
"parent": "jaw2",
"pivot": [0, 51.75, -1.75],
"locators": {
"jaw2": {
"offset": [0, 51.75, -1.75],
"rotation": [-90, 0, 0]
}
}
},
{
"name": "tongue",
"parent": "jaw",
"pivot": [0, 45.5, -2],
"cubes": [
{"origin": [-1.5, 45.5, -2], "size": [3, 3, 0], "uv": [87, 29]}
]
},
{
"name": "tongue2",
"parent": "tongue",
"pivot": [0, 48.5, -2],
"cubes": [
{"origin": [-1, 48.5, -2], "size": [2, 2, 0], "inflate": 0.01, "uv": [46, 38]}
]
},
{
"name": "tongue3",
"parent": "tongue2",
"pivot": [0, 50.5, -2],
"cubes": [
{"origin": [-1, 50.5, -2], "size": [2, 1, 0], "uv": [37, 43]},
{"origin": [-0.5, 51.5, -2], "size": [1, 1, 0], "uv": [45, 43]}
]
},
{
"name": "arms",
"parent": "torso2",
@@ -403,6 +418,14 @@
{"origin": [-21.5, 22, -1.5], "size": [3, 2, 3], "inflate": 0.1, "uv": [0, 58]}
]
},
{
"name": "locator_item",
"parent": "hand_right",
"pivot": [-21.6, 23, 0],
"locators": {
"item": [-21.6, 23, 0]
}
},
{
"name": "locator_hand_primary",
"parent": "hand_right",
@@ -913,6 +936,14 @@
{"origin": [0, 9.5, 58.5], "size": [0, 13, 7], "pivot": [0, 14.45, 62], "rotation": [0, 90, 0], "uv": [0, 92]}
]
},
{
"name": "locator_fire",
"parent": "fire2",
"pivot": [0, 11, 62],
"locators": {
"fire": [0, 11, 62]
}
},
{
"name": "legs",
"parent": "body",
@@ -945,9 +976,10 @@
{
"name": "locator_foot_primary",
"parent": "foot_right",
"pivot": [-9, 1.5, -7],
"pivot": [-9, 1.5, -5],
"rotation": [90, 0, 0],
"locators": {
"foot_primary": [-9, 1.5, -7]
"foot_primary": [-9, 1.5, -5]
}
},
{
@@ -1009,9 +1041,10 @@
{
"name": "locator_foot_secondary",
"parent": "foot_left",
"pivot": [9, 1.5, -7],
"pivot": [9, 1.5, -5],
"rotation": [90, 0, 0],
"locators": {
"foot_secondary": [9, 1.5, -7]
"foot_secondary": [9, 1.5, -5]
}
},
{
@@ -86,12 +86,22 @@
"fly": {
"poseTypes": ["FLY"],
"isBattle": false,
"condition": "!q.is_gliding",
"animations": ["q.look('head_ai')", "q.bedrock('charizard', 'air_fly')"],
"quirks": [
"q.bedrock_quirk('charizard', 'blink')",
"q.bedrock_quirk('charizard', 'air_fly_quirk')"
]
},
"glide": {
"poseTypes": ["FLY"],
"isBattle": false,
"condition": "q.is_gliding",
"animations": ["q.look('head_ai')", "q.bedrock('charizard', 'air_glide_TEST')"],
"quirks": [
"q.bedrock_quirk('charizard', 'blink')"
]
},
"surface_idle": {
"poseTypes": ["STAND", "FLOAT"],
"isTouchingWater": true,
@@ -1561,12 +1561,20 @@
"cobblemon.command.pokebox.last_pokemon": "Did not send that Player's last Pokémon to the PC.",
"cobblemon.command.pokemonrestart": "Cleared %1$s's party and PC.",
"cobblemon.command.freezepokemon.non_pokemon": "You are not looking at a Pokémon.",
"cobblemon.command.spectatebattle.self_spectate_disallowed": "You can't spectate yourself.",
"cobblemon.command.spectatebattle.while_battling_disallowed": "You can't spectate other people while battling.",
"cobblemon.command.spectatebattle.player_not_in_battle": "This player is not in a battle.",
"cobblemon.command.npcedit.non_npc": "You are not looking at a NPC.",
"cobblemon.command.cobblemon_config.reload": "Reloading Cobblemon settings.",
"cobblemon.command.unlockboxwallpaper.success": "%1$s can now use the %2$s wallpaper.",
"cobblemon.command.unlockboxwallpaper.already": "%1$s already has the %2$s wallpaper.",
"cobblemon.wallpaper_unlocked": "Wallpaper Unlocked",
"cobblemon.unknown_wallpaper": "???",
"cobblemon.command.changeboxcount": "%1$s now has %2$s PC boxes",
"cobblemon.command.changeboxcount.removing_not_empty_box": "A box you are trying to remove is not empty",
"cobblemon.command.boxcount": "%1$s currently has %2$s PC boxes",
"cobblemon.command.changeboxcount.removing_too_many_boxes": "You can't remove all the boxes. %1$s currently has %2$s PC boxes",
"cobblemon.command.changeboxcount.force_remove": "Click here to remove anyway",
"cobblemon.config.ui.search_configurations": "Search Configurations",
"cobblemon.config.ui.reset": "Reset",
@@ -97,12 +97,12 @@
"party": {
"type": "simple",
"pokemon": [
"spiritomb level=100 held_item=cobblemon:sitrus_berry ability=pressure nature=jolly moves=darkpulse,shadowball,willowisp,suckerpunch hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 special_attack_ev=252 speed_ev=6",
"porygonz level=100 held_item=cobblemon:life_orb ability=adaptability nature=timid moves=hyperbeam,shadowball,icebeam,thunderbolt hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"togekiss level=100 held_item=cobblemon:leftovers ability=serenegrace nature=timid moves=airslash,dazzlinggleam,aurasphere,thunderwave hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"lucario level=100 held_item=cobblemon:focus_sash ability=innerfocus nature=jolly moves=closecombat,meteormash,earthquake,extremespeed hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252",
"milotic level=100 held_item=cobblemon:flame_orb ability=marvelscale nature=bold moves=recover,mirrorcoat,icebeam,scald hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 defence_ev=252 special_attack_ev=6",
"garchomp level=100 held_item=cobblemon:yache_berry ability=roughskin nature=jolly moves=dragonclaw,earthquake,swordsdance,poisonjab hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252"
"spiritomb level=100 pokeball=master_ball held_item=cobblemon:sitrus_berry ability=pressure nature=jolly moves=darkpulse,shadowball,willowisp,suckerpunch hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 special_attack_ev=252 speed_ev=6",
"porygonz level=100 pokeball=great_ball held_item=cobblemon:life_orb ability=adaptability nature=timid moves=hyperbeam,shadowball,icebeam,thunderbolt hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"togekiss level=100 pokeball=ultra_ball held_item=cobblemon:leftovers ability=serenegrace nature=timid moves=airslash,dazzlinggleam,aurasphere,thunderwave hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 special_attack_ev=252 speed_ev=252",
"lucario level=100 pokeball=dive_ball held_item=cobblemon:focus_sash ability=innerfocus nature=jolly moves=closecombat,meteormash,earthquake,extremespeed hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252",
"milotic level=100 pokeball=love_ball held_item=cobblemon:flame_orb ability=marvelscale nature=bold moves=recover,mirrorcoat,icebeam,scald hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=252 defence_ev=252 special_attack_ev=6",
"garchomp level=100 pokeball=quick_ball held_item=cobblemon:yache_berry ability=roughskin nature=jolly moves=dragonclaw,earthquake,swordsdance,poisonjab hp_iv=31 attack_iv=31 defence_iv=31 special_attack_iv=31 special_defence_iv=31 speed_iv=31 hp_ev=6 attack_ev=252 speed_ev=252"
]
}
}