forked from mirrors/cobblemon
Compare commits
129
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da73fd5616 | ||
|
|
729485f540 | ||
|
|
586fe259ad | ||
|
|
49cc86331f | ||
|
|
b923bcad50 | ||
|
|
b49cad021e | ||
|
|
78c733e1c1 | ||
|
|
2825e13e7a | ||
|
|
bcfd4cad49 | ||
|
|
037dd79a79 | ||
|
|
fc3da8a476 | ||
|
|
46eac875e7 | ||
|
|
8725f5c8b9 | ||
|
|
f2c4ebef7c | ||
|
|
d228da9eb5 | ||
|
|
f30209baf5 | ||
|
|
70d316b990 | ||
|
|
b3c56488d8 | ||
|
|
1b06085772 | ||
|
|
7e9a9c6dd9 | ||
|
|
eb38284e63 | ||
|
|
54095d535a | ||
|
|
04f2af22f3 | ||
|
|
ea916a0db8 | ||
|
|
e940483863 | ||
|
|
9e3d3df3a9 | ||
|
|
c18368d777 | ||
|
|
aab5b55204 | ||
|
|
3e6e346eb9 | ||
|
|
7bef6db7b7 | ||
|
|
71775328ef | ||
|
|
3944aaefd0 | ||
|
|
727785b0b3 | ||
|
|
762241420e | ||
|
|
8674cbe788 | ||
|
|
8075fe0bae | ||
|
|
0152803192 | ||
|
|
5e094241e3 | ||
|
|
bd028c328d | ||
|
|
598cb1d964 | ||
|
|
79d264fd94 | ||
|
|
5f691d3f1c | ||
|
|
7be0163e76 | ||
|
|
c0b3068824 | ||
|
|
dd35adc05d | ||
|
|
26b5707317 | ||
|
|
75e39f5595 | ||
|
|
7fdeaeabef | ||
|
|
809ffd8b90 | ||
|
|
da7610951d | ||
|
|
3487617e1d | ||
|
|
76fdd1fa98 | ||
|
|
156983bf58 | ||
|
|
c69930b836 | ||
|
|
6926932340 | ||
|
|
221b3308fa | ||
|
|
66840670b6 | ||
|
|
5d6a7807e5 | ||
|
|
9c7aba7481 | ||
|
|
20b7eb17f3 | ||
|
|
103ac86345 | ||
|
|
8f705a698d | ||
|
|
f4fb09947e | ||
|
|
97e31e341b | ||
|
|
813789235d | ||
|
|
5a82e7e185 | ||
|
|
3bc8d3ce75 | ||
|
|
d9e9fd0f4e | ||
|
|
fddb203db3 | ||
|
|
8a8cace5e8 | ||
|
|
5f5b4ee3c1 | ||
|
|
aa6581ae39 | ||
|
|
8aa9e0191f | ||
|
|
8dc09796f8 | ||
|
|
8f93712edb | ||
|
|
b91d1b931e | ||
|
|
9d84cbb3ac | ||
|
|
8e730f2b5c | ||
|
|
7b5809fd55 | ||
|
|
bc682ddf62 | ||
|
|
990ead0544 | ||
|
|
4cf55dc95d | ||
|
|
27a9f3c704 | ||
|
|
83aba7e4a6 | ||
|
|
73275626a3 | ||
|
|
0686428138 | ||
|
|
f05e6008e5 | ||
|
|
4450164d9c | ||
|
|
b99899353c | ||
|
|
e4e0846a24 | ||
|
|
608b711833 | ||
|
|
7d70af44ba | ||
|
|
3adc1b9f30 | ||
|
|
478cbc2489 | ||
|
|
7c8fcdcd48 | ||
|
|
03edb5a385 | ||
|
|
cf77713860 | ||
|
|
0494193c9b | ||
|
|
7bee34170d | ||
|
|
3a22e387b6 | ||
|
|
956b3bfcbd | ||
|
|
a9ceddd94a | ||
|
|
67684ff9e8 | ||
|
|
7144aa8dce | ||
|
|
e99c55e812 | ||
|
|
5053d559bc | ||
|
|
6801035c1d | ||
|
|
1a30109f1c | ||
|
|
5088497575 | ||
|
|
31129ef14d | ||
|
|
e80eb91e69 | ||
|
|
2a295a8796 | ||
|
|
245226f4f6 | ||
|
|
d3e1c2c79d | ||
|
|
e04e82c6de | ||
|
|
95589dd48b | ||
|
|
4cb17135bd | ||
|
|
8dc23160fc | ||
|
|
4a96a41c8e | ||
|
|
3f18e7c0bf | ||
|
|
3c3a23a397 | ||
|
|
438de6ed6d | ||
|
|
c754d820a5 | ||
|
|
81c819ddd3 | ||
|
|
ec3e0bfd04 | ||
|
|
b61d739e46 | ||
|
|
7a050d5374 | ||
|
|
52076de76a | ||
|
|
be8dfd7fa2 |
@@ -34,6 +34,7 @@
|
||||
- Added Hyper Training items (IV Modification) as well as some additional candy items to do so (Health Candy, Sickly Candy)
|
||||
- Added Galarica Nut Bushes
|
||||
- Many Pokémon (mostly cats) are now feared by phantoms
|
||||
- Combees are now capable of gathering nectar from flowers and delivering it to either Saccharine leaves or hives. (Hive interactions are restricted to wild Combees)
|
||||
- Lightning is now affected by a Pokemon's ability/typing
|
||||
- Pokémon with the ability Lightning Rod draw in lightning similar to a lightning rod block albeit with a lower priority and range, gain immunity to lightning damage, and receive a temporary damage buff.
|
||||
- Pokémon with the ability Motor Drive are immune to lightning damage and receive a temporary speed buff when struck by lightning
|
||||
@@ -293,6 +294,7 @@
|
||||
- Tauros - Paldea Blaze
|
||||
- Tauros - Paldea Combat
|
||||
- Dewgong
|
||||
- Exeggutor - Kanto
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
- Cleffa
|
||||
@@ -403,6 +405,9 @@
|
||||
- Revavroom
|
||||
- Nickit
|
||||
- Thievul
|
||||
- Litwick
|
||||
- Lampent
|
||||
- Chandelure
|
||||
|
||||
### Changes
|
||||
- Changed pokemon caught and seen count to update based on the current pokedex being looked
|
||||
@@ -467,6 +472,7 @@
|
||||
- Updated `HatchEggEvent.Post` to include the Pokemon that hatched.
|
||||
- Roseli Berry Trees now naturally generate in their preferred biomes.
|
||||
- Refactored dynamic lights compat to be more future proof. The dynamic light support is only tested with LambDynamicLights, on NeoForged use that mod in combination with Sinytra Connector
|
||||
- Shulker Boxes and Traveler's Backpacks can no longer be held by Pokémon. Thanks Monocle ;) You could re-enable this with a datapack but you'd be crazy.
|
||||
|
||||
### Fixes
|
||||
- Fixed game crashing when removing national pokedex using datapacks
|
||||
@@ -990,6 +996,7 @@ Added new/updated particles for the following moves:
|
||||
- Parametric particle motion now works.
|
||||
- Event-spawned particles now work.
|
||||
- Particles can now have independent coordinate spaces.
|
||||
- Fixed suffocation of Pokémon when a pokeball breaks near a wall
|
||||
|
||||
### Developer
|
||||
- Updated the Pokédex data updated events to always include a `Pokemon` instance, and optionally a `DisguiseData` instance.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public interface RidePassenger {
|
||||
void cobblemon$setRideXRot(float rideXRot);
|
||||
float cobblemon$getRideXRot();
|
||||
void cobblemon$setRideYRot(float rideYRot);
|
||||
float cobblemon$getRideYRot();
|
||||
void cobblemon$setRideEyePos(Vec3 rideEyePos);
|
||||
Vec3 cobblemon$getRideEyePos();
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.projectile.AbstractArrow;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(AbstractArrow.class)
|
||||
public abstract class AbstractArrowMixin {
|
||||
|
||||
// Target the constructor that takes a LivingEntity owner
|
||||
@Inject(
|
||||
method = "<init>(Lnet/minecraft/world/entity/EntityType;Lnet/minecraft/world/entity/LivingEntity;Lnet/minecraft/world/level/Level;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)V",
|
||||
at = @At("TAIL") // Inject at the very end of the constructor
|
||||
)
|
||||
private void cobblemon$adjustSpawnPosition(
|
||||
EntityType<? extends AbstractArrow> entityType,
|
||||
LivingEntity owner,
|
||||
Level level,
|
||||
ItemStack pickupItemStack,
|
||||
@Nullable ItemStack firedFromWeapon,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
AbstractArrow arrow = (AbstractArrow)(Object)this;
|
||||
if(!(owner.getVehicle() instanceof OrientationControllable vehicle)) return;
|
||||
var vehicleController = vehicle.getOrientationController();
|
||||
|
||||
// If this is a rolling ride passenger then use the rider eye position to set the arrow spawn position.
|
||||
if (owner instanceof RidePassenger ridePassenger && vehicleController != null && vehicleController.getActive()) {
|
||||
var eyePos = ridePassenger.cobblemon$getRideEyePos().add(0.0, -2.0, 0.0);
|
||||
arrow.setPos(eyePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories;
|
||||
import com.cobblemon.mod.common.api.ai.config.task.PathToBeeHiveTaskConfig;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.BeehiveBlock;
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(BeehiveBlockEntity.Occupant.class)
|
||||
public abstract class BeeOccupantMixin {
|
||||
|
||||
@Inject(method = "createEntity", at = @At("RETURN"), cancellable = true)
|
||||
private void cobblemon$createEntity(Level level, BlockPos pos, CallbackInfoReturnable<Entity> cir) {
|
||||
// Needing to replicate all the post op stuff on the hive isn't great
|
||||
if (cir.getReturnValue() == null) {
|
||||
final BeehiveBlockEntity.Occupant occupant = (BeehiveBlockEntity.Occupant) (Object) this;
|
||||
CompoundTag compoundTag = occupant.entityData().copyTag();
|
||||
Entity entity = EntityType.loadEntityRecursive(compoundTag, level, (entityx) -> entityx);
|
||||
if (entity instanceof PokemonEntity pokemonEntity) {
|
||||
var state = level.getBlockState(pos);
|
||||
var newPos = Vec3.ZERO;
|
||||
var newYaw = 0f;
|
||||
var brain = pokemonEntity.getBrain();
|
||||
if (state.is(BlockTags.BEEHIVES)) {
|
||||
// position in front of the entrance, face away from the block
|
||||
var facing = state.getValue(HorizontalDirectionalBlock.FACING);
|
||||
newPos = pos.relative(facing).getCenter();
|
||||
newYaw = facing.toYRot();
|
||||
brain.setMemory(CobblemonMemories.INSTANCE.getHIVE_LOCATION(), pos); // This needs to be set in the case that the hive was picked up and moved.
|
||||
} else {
|
||||
// Block has likely been destroyed
|
||||
var center = pos.getCenter();
|
||||
newPos = new Vec3(
|
||||
center.x + level.random.nextFloat() * 0.6 - 0.3,
|
||||
center.y + level.random.nextFloat() * 0.6 - 0.3,
|
||||
center.z + level.random.nextFloat() * 0.6 - 0.3
|
||||
);
|
||||
newYaw = level.random.nextFloat() * 360F;
|
||||
brain.eraseMemory(CobblemonMemories.INSTANCE.getHIVE_LOCATION());
|
||||
}
|
||||
entity.yRotO = newYaw;
|
||||
entity.setPos(newPos);
|
||||
// Do honey logic
|
||||
var hasNectar = brain.getMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR()).orElse(false);
|
||||
if (hasNectar) {
|
||||
// Remove nectar and reset got to hive cooldown
|
||||
brain.eraseMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR());
|
||||
brain.setMemoryWithExpiry(CobblemonMemories.INSTANCE.getHIVE_COOLDOWN(), true, PathToBeeHiveTaskConfig.STAY_OUT_OF_HIVE_COOLDOWN);
|
||||
// Increment honey level of the hive
|
||||
if (state.is(BlockTags.BEEHIVES, (blockStateBase) -> blockStateBase.hasProperty(BeehiveBlock.HONEY_LEVEL))) {
|
||||
int i = state.getValue(BeehiveBlock.HONEY_LEVEL);
|
||||
if (i < 5) {
|
||||
int j = level.random.nextInt(100) == 0 ? 2 : 1;
|
||||
if (i + j > 5) {
|
||||
--j;
|
||||
}
|
||||
level.setBlockAndUpdate(pos, state.setValue(BeehiveBlock.HONEY_LEVEL, i + j));
|
||||
}
|
||||
}
|
||||
}
|
||||
cir.setReturnValue(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "of", at = @At("HEAD"), cancellable = true)
|
||||
private static void cobblemon$of(Entity entity, CallbackInfoReturnable<BeehiveBlockEntity.Occupant> cir) {
|
||||
if (entity instanceof PokemonEntity pokemonEntity) {
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
Boolean hasNectar = pokemonEntity.getBrain().getMemory(CobblemonMemories.INSTANCE.getHAS_NECTAR()).orElse(false);
|
||||
cir.setReturnValue(new BeehiveBlockEntity.Occupant(CustomData.of(compoundTag), 0, hasNectar ? 2400 : 600));
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.mixin.accessor.BeeDataAccessor;
|
||||
import com.cobblemon.mod.common.mixin.accessor.BeehiveBlockEntityAccessor;
|
||||
import com.cobblemon.mod.common.mixin.accessor.BlockEntityAccessor;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
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;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(BeehiveBlockEntity.class)
|
||||
public abstract class BeehiveBlockEntityMixin {
|
||||
|
||||
|
||||
// Accessor for the private 'stored' field in BeehiveBlockEntity
|
||||
@Accessor("stored")
|
||||
public abstract List<BeeDataAccessor> getStored();
|
||||
|
||||
@Inject(method = "emptyAllLivingFromHive", at = @At("HEAD"))
|
||||
private void cobblemon$emptyAllLivingFromHive(@Nullable Player player, BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, CallbackInfo ci) {
|
||||
if (player != null) {
|
||||
final BeehiveBlockEntity blockEntity = (BeehiveBlockEntity) (Object) this;
|
||||
// if the block is sedated or the player is too far away, no aggro should be applied
|
||||
if (blockEntity.isSedated() || player.position().distanceToSqr(blockEntity.getBlockPos().getCenter()) > 16.0)
|
||||
return;
|
||||
// from this point need to access private members of the block entity
|
||||
final BeehiveBlockEntityAccessor blockEntityAccessor = (BeehiveBlockEntityAccessor) this;
|
||||
List<Object> storedList = blockEntityAccessor.getStored();
|
||||
|
||||
List<Entity> list = Lists.newArrayList();
|
||||
storedList.removeIf((beeData) -> {
|
||||
BeehiveBlockEntity.Occupant occupant = ((BeeDataAccessor) beeData).invokeToOccupant();
|
||||
// Checking the for the tag allows us to make sure it's one of our pokemon before trying to rehydrate it into an entity
|
||||
boolean isPokemon = false;
|
||||
if (occupant.entityData().contains("id")) {
|
||||
isPokemon = "cobblemon:pokemon".equals(occupant.entityData().getUnsafe().getString("id"));
|
||||
}
|
||||
if (isPokemon) {
|
||||
return BeehiveBlockEntityAccessor.releaseOccupant(
|
||||
((BlockEntityAccessor) blockEntityAccessor).getLevel(),
|
||||
((BlockEntityAccessor) blockEntityAccessor).getWorldPosition(),
|
||||
state,
|
||||
occupant,
|
||||
list,
|
||||
releaseStatus,
|
||||
blockEntityAccessor.savedFlowerPos());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
for (Entity entity : list) {
|
||||
if (entity instanceof PokemonEntity) {
|
||||
((PokemonEntity) entity).getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "releaseOccupant",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z"
|
||||
)
|
||||
)
|
||||
private static boolean cobblemon$captureReleasedEntity(
|
||||
Level level, Entity entity,
|
||||
Level lvl, BlockPos pos, BlockState state,
|
||||
BeehiveBlockEntity.Occupant occupant, @Nullable List<Entity> bees,
|
||||
BeehiveBlockEntity.BeeReleaseStatus releaseStatus, @Nullable BlockPos flowerPos) {
|
||||
|
||||
if (bees != null && !bees.contains(entity)) {
|
||||
// Avoid duplicates if vanilla also adds bees to the list on your version
|
||||
bees.add(entity);
|
||||
}
|
||||
return level.addFreshEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,23 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry;
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehaviour;
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate;
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
@@ -32,6 +37,7 @@ import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public abstract class LivingEntityMixin extends Entity implements OrientationControllable {
|
||||
@@ -47,6 +53,12 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
return cobblemon$orientationController;
|
||||
}
|
||||
|
||||
@Inject(method = "baseTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
|
||||
private void cobblemon$updateRotationMatrix(CallbackInfo ci) {
|
||||
if (!cobblemon$orientationController.isActive() || cobblemon$orientationController.getOrientation() == null) return;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(this.getId(), cobblemon$orientationController.getOrientation()));
|
||||
}
|
||||
|
||||
@Inject(method = "onEffectRemoved", at = @At(value = "TAIL"))
|
||||
private void cobblemon$onEffectRemoved(MobEffectInstance effect, CallbackInfo ci) {
|
||||
final LivingEntity entity = (LivingEntity) (Object) this;
|
||||
@@ -116,4 +128,31 @@ public abstract class LivingEntityMixin extends Entity implements OrientationCon
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "decreaseAirSupply", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$conditionalWaterBreathing(
|
||||
int currentAir,
|
||||
CallbackInfoReturnable<Integer> cir
|
||||
) {
|
||||
var entity = (LivingEntity) (Object) this;
|
||||
if (!(entity instanceof Player player)) return;
|
||||
if (!(player.getVehicle() instanceof PokemonEntity vehicle)) return;
|
||||
|
||||
var ridingController = vehicle.getRidingController();
|
||||
if (ridingController == null) return;
|
||||
|
||||
var activeContext = ridingController.getContext();
|
||||
if (activeContext == null) return;
|
||||
|
||||
// If the rider is currently on a submarine and the ride still has stamina.
|
||||
var stamina = activeContext.getState().getStamina().get();
|
||||
if (
|
||||
activeContext.getStyle() == RidingStyle.LIQUID &&
|
||||
activeContext.getBehaviour().equals(SubmarineBehaviour.Companion.getKEY()) &&
|
||||
stamina > 0.0f
|
||||
) {
|
||||
cir.setReturnValue(currentAir);
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.storage.party.PartyStore;
|
||||
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore;
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
|
||||
import com.cobblemon.mod.common.duck.PlayerDuck;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
|
||||
@@ -47,6 +48,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.food.FoodProperties;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
@@ -63,7 +65,7 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(Player.class)
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck {
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger {
|
||||
|
||||
@Unique
|
||||
private Vector3f cobblemon$driverInput;
|
||||
@@ -91,6 +93,12 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
@Shadow protected int jumpTriggerTime;
|
||||
@Unique private final OrientationController cobblemon$orientationController = new OrientationController(this);
|
||||
|
||||
@Unique private float cobblemon$rideXRot = 0.0f;
|
||||
|
||||
@Unique private float cobblemon$rideYRot = 0.0f;
|
||||
|
||||
@Unique private Vec3 cobblemon$rideEyePos = Vec3.ZERO;
|
||||
|
||||
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
|
||||
super(p_20966_, p_20967_);
|
||||
}
|
||||
@@ -117,11 +125,6 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void cobblemon$updateRenderOrientation(CallbackInfo ci) {
|
||||
this.cobblemon$orientationController.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityDimensions getDefaultDimensions(Pose pose) {
|
||||
if (this.getVehicle() instanceof PokemonEntity) {
|
||||
@@ -307,4 +310,34 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
return cobblemon$lastSentDriverInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float cobblemon$getRideXRot() {
|
||||
return this.cobblemon$rideXRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$setRideXRot(float rideXRot) {
|
||||
this.cobblemon$rideXRot = rideXRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float cobblemon$getRideYRot() {
|
||||
return this.cobblemon$rideYRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$setRideYRot(float rideYRot) {
|
||||
this.cobblemon$rideYRot = rideYRot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 cobblemon$getRideEyePos() {
|
||||
return this.cobblemon$rideEyePos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cobblemon$setRideEyePos(Vec3 rideEyePos) {
|
||||
this.cobblemon$rideEyePos = rideEyePos;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -25,16 +26,17 @@ public abstract class ServerPlayerMixin {
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((ServerPlayer)(Object)this);
|
||||
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
ServerPlayer player = (ServerPlayer)(Object)this;
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (!(vehicle instanceof OrientationControllable controllableVehicle)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation(vehicle);
|
||||
controllableVehicle.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseCustomOrientation(ServerPlayer player) {
|
||||
var playerVehicle = player.getVehicle();
|
||||
if (playerVehicle == null) return false;
|
||||
if (!(playerVehicle instanceof PokemonEntity pokemonEntity)) return false;
|
||||
private boolean cobblemon$shouldUseCustomOrientation(Entity entity) {
|
||||
if (entity == null) return false;
|
||||
if (!(entity instanceof PokemonEntity pokemonEntity)) return false;
|
||||
return pokemonEntity.ifRidingAvailableSupply(false, (behaviour, settings, state) -> {
|
||||
return behaviour.shouldRoll(settings, state, pokemonEntity);
|
||||
});
|
||||
@@ -42,8 +44,9 @@ public abstract class ServerPlayerMixin {
|
||||
|
||||
@Inject(method = "stopRiding", at = @At("HEAD"))
|
||||
public void cobblemon$resetOrientationOnDismount(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
controllable.getOrientationController().setActive(false);
|
||||
ServerPlayer player = (ServerPlayer)(Object)this;
|
||||
if (!(player.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
controllableVehicle.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
//TODO: Switch to sending out on load instead of preventing saving
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(targets = "net.minecraft.world.level.block.entity.BeehiveBlockEntity$BeeData")
|
||||
public interface BeeDataAccessor {
|
||||
@Invoker("toOccupant")
|
||||
BeehiveBlockEntity.Occupant invokeToOccupant();
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(BeehiveBlockEntity.class)
|
||||
public interface BeehiveBlockEntityAccessor {
|
||||
@Accessor("stored")
|
||||
List<Object> getStored();
|
||||
@Accessor("savedFlowerPos")
|
||||
BlockPos savedFlowerPos();
|
||||
|
||||
|
||||
/**
|
||||
* Access the private static method:
|
||||
* private static void releaseOccupant(Level level, BlockPos pos, BeeData beeData, ...)
|
||||
*/
|
||||
@Invoker("releaseOccupant")
|
||||
static boolean releaseOccupant(Level level, BlockPos pos, BlockState state, BeehiveBlockEntity.Occupant occupant, @Nullable List<Entity> storedInHives, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, @Nullable BlockPos storedFlowerPos) {
|
||||
throw new AssertionError(); // Mixin will overwrite
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(BlockEntity.class)
|
||||
public interface BlockEntityAccessor {
|
||||
@Accessor("level") Level getLevel();
|
||||
@Accessor("worldPosition") BlockPos getWorldPosition();
|
||||
}
|
||||
+2
-1
@@ -34,7 +34,8 @@ public class AbstractClientPlayerMixin {
|
||||
} else {
|
||||
// Only modify fov through riding if the pokedex is not open
|
||||
LocalPlayer player = (LocalPlayer) (Object) this;
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity) {
|
||||
// Only modify fov for the driver and not the passengers
|
||||
if (player != null && player.isPassenger() && player.getVehicle() instanceof PokemonEntity && player.getVehicle().getControllingPassenger() == player) {
|
||||
PokemonEntity ride = (PokemonEntity) player.getVehicle();
|
||||
|
||||
// Return custom fov mult for riding
|
||||
|
||||
@@ -14,12 +14,15 @@ import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Rideable;
|
||||
import com.cobblemon.mod.common.client.MountedPokemonAnimationRenderController;
|
||||
import com.cobblemon.mod.common.client.render.camera.MountedCameraRenderer;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.Blaze3D;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.joml.Matrix3f;
|
||||
@@ -33,6 +36,9 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static net.minecraft.util.Mth.abs;
|
||||
import static net.minecraft.util.Mth.atan2;
|
||||
|
||||
@Mixin(Camera.class)
|
||||
public abstract class CameraMixin {
|
||||
@Shadow private Entity entity;
|
||||
@@ -48,45 +54,43 @@ public abstract class CameraMixin {
|
||||
|
||||
@Shadow protected abstract void setPosition(Vec3 pos);
|
||||
|
||||
@Unique private float returnTimer = 0;
|
||||
@Unique private float rollAngleStart = 0;
|
||||
@Unique Minecraft minecraft = Minecraft.getInstance();
|
||||
@Shadow private float partialTickTime;
|
||||
|
||||
// Used to help in camera rotation rendering when transitioning
|
||||
// from rolled to unrolled.
|
||||
@Unique private float cobblemon$returnTimer = 0;
|
||||
@Unique private float cobblemon$rollAngleStart = 0;
|
||||
|
||||
@Unique private double cobblemon$lastHandledRotationTime = Double.MIN_VALUE;
|
||||
@Unique private double cobblemon$frameTime = 0.0;
|
||||
|
||||
@Inject(method = "setRotation", at = @At("HEAD"), cancellable = true)
|
||||
|
||||
public void cobblemon$setRotation(float f, float g, CallbackInfo ci) {
|
||||
if (!(this.entity instanceof OrientationControllable controllable)) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
if (!controller.isActive() && controller.getOrientation() != null) {
|
||||
if(this.returnTimer < 1) {
|
||||
//Rotation is taken from entity since we no longer handle mouse ourselves
|
||||
//Stops a period of time when you can't input anything.
|
||||
controller.getOrientation().set(
|
||||
new Matrix3f()
|
||||
.rotateY((float) Math.toRadians(180 - this.entity.getYRot()))
|
||||
.rotateX((float) Math.toRadians(-this.entity.getXRot()))
|
||||
);
|
||||
if(rollAngleStart == 0){
|
||||
this.returnTimer = 1;
|
||||
controller.reset();
|
||||
return;
|
||||
}
|
||||
controller.getOrientation().rotateZ((float) Math.toRadians(-rollAngleStart*(1-returnTimer)));
|
||||
cobblemon$applyRotation();
|
||||
this.returnTimer += .05F;
|
||||
ci.cancel();
|
||||
} else {
|
||||
this.returnTimer = 1;
|
||||
controller.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (controller.getOrientation() == null) return;
|
||||
cobblemon$applyRotation();
|
||||
// Calculate the current frametime
|
||||
double d = Blaze3D.getTime();
|
||||
this.cobblemon$frameTime = (this.cobblemon$lastHandledRotationTime != Double.MIN_VALUE) ? d - this.cobblemon$lastHandledRotationTime : 0.0;
|
||||
this.cobblemon$lastHandledRotationTime = d;
|
||||
|
||||
this.returnTimer = 0;
|
||||
this.rollAngleStart = controller.getRoll();
|
||||
ci.cancel();
|
||||
var vehicle = this.entity.getVehicle();
|
||||
if (!(vehicle instanceof OrientationControllable controllableVehicle)) return;
|
||||
var instance = (Camera)(Object)this;
|
||||
|
||||
// If the current vehicle has no orientation then return and perform
|
||||
// vanilla camera rotations
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
if (vehicleController.getOrientation() == null) return;
|
||||
|
||||
// If the controller has been deactivated but the orientation isn't null yet
|
||||
// then perform transition from rolled ride/camera to vanilla
|
||||
if (!vehicleController.isActive() && vehicleController.getOrientation() != null) {
|
||||
cobblemon$applyTransitionRotation(vehicleController, ci);
|
||||
} else { // Otherwise perform cobblemon specific camera rotation and set transition helper variables
|
||||
cobblemon$applyRotation();
|
||||
this.cobblemon$returnTimer = 0;
|
||||
this.cobblemon$rollAngleStart = instance.rotation().getEulerAnglesYXZ(new Vector3f()).z();
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;setPosition(DDD)V"))
|
||||
@@ -119,20 +123,128 @@ public abstract class CameraMixin {
|
||||
|
||||
@Unique
|
||||
private void cobblemon$applyRotation(){
|
||||
if (!(this.entity instanceof OrientationControllable controllable) || controllable.getOrientationController().getOrientation() == null) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
var newRotation = controller.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
if (this.minecraft.options.getCameraType().isMirrored()) {
|
||||
var driver = this.entity;
|
||||
if (!(driver.getVehicle() instanceof OrientationControllable controllableVehicle) ||
|
||||
controllableVehicle.getOrientationController().getOrientation() == null ||
|
||||
!controllableVehicle.getOrientationController().getActive()
|
||||
) return;
|
||||
|
||||
// Grab the driver and vehicle orientation controllers.
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
|
||||
var isDriving = driver.getControlledVehicle() != null;
|
||||
|
||||
// Grab the ride orientation
|
||||
var newRotation = new Quaternionf();
|
||||
|
||||
// If the player is driving then use the current orientation since the driver sets it.
|
||||
// If not then use the smoothed render orientation for passengers otherwise the vehicle rotation will be jerky
|
||||
if (isDriving) {
|
||||
newRotation = vehicleController.getOrientation().normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
} else {
|
||||
newRotation = vehicleController.getRenderOrientation(this.partialTickTime);
|
||||
}
|
||||
|
||||
// Unroll the current rotation if no roll is requested
|
||||
if (Cobblemon.config.getDisableRoll()) {
|
||||
var eulerAngs = newRotation.getEulerAnglesYXZ(new Vector3f());
|
||||
var currAngs = this.rotation.getEulerAnglesYXZ(new Vector3f());
|
||||
var vehicle = (PokemonEntity)driver.getVehicle();
|
||||
var vel = vehicle.getRidingAnimationData().getVelocitySpring().getInterpolated(this.partialTickTime, 0);
|
||||
|
||||
// lerp the new rotation towards this y rot at a rate proportional to the horizontal movement speed?
|
||||
var yaw = Mth.wrapDegrees(Math.toDegrees(Mth.atan2(vel.x(), vel.z())) + 180.0f);
|
||||
|
||||
var degDiff = Mth.degreesDifference((float)Math.toDegrees(currAngs.y()), (float)yaw);
|
||||
var pitchDeadzoneDeg = 15.0; // x degrees of deadzone about +-90 degrees of pitch.
|
||||
var pitchDeg = vehicleController.getPitch();
|
||||
// ConsiderPitch to be already 90 if within the deadzone around it.
|
||||
var cutoffPitch = Math.min(1.0,(Mth.abs(pitchDeg) / (90.0f - pitchDeadzoneDeg))) * 90.0f * Math.signum(pitchDeg);
|
||||
var lerpRateMod = Mth.cos((float)Math.toRadians(cutoffPitch));
|
||||
|
||||
// Apply smoothing from the cameras current yaw to the yaw of the velocity vector of the ride.
|
||||
var k = 2.0f; // Some smoothing constant k
|
||||
var smoothingFactor = lerpRateMod * cobblemon$frameTime * k;
|
||||
var newYaw = currAngs.y() + (Math.toRadians(degDiff) * smoothingFactor);
|
||||
|
||||
newRotation.set(new Quaternionf().rotateYXZ((float)newYaw, eulerAngs.x(), 0.0f));
|
||||
}
|
||||
|
||||
var k = 0;
|
||||
if (Minecraft.getInstance().options.getCameraType().isMirrored()) {
|
||||
newRotation.rotateY((float)Math.toRadians(180));
|
||||
k = 180;
|
||||
}
|
||||
|
||||
// Get the drivers local look angle in relation to the ride and apply it.
|
||||
if (vehicleController.getActive()) {
|
||||
var vehicleMatrix = new Matrix3f().set(newRotation);
|
||||
var driverMatrix = new Matrix3f();// Base the driver
|
||||
var playerRotater = (RidePassenger)driver;
|
||||
newRotation = vehicleMatrix.mul(
|
||||
driverMatrix.rotateYXZ(
|
||||
-Mth.DEG_TO_RAD * (playerRotater.cobblemon$getRideYRot()),
|
||||
-Mth.DEG_TO_RAD * playerRotater.cobblemon$getRideXRot(),
|
||||
0.0f)
|
||||
).normal(new Matrix3f()).getNormalizedRotation(new Quaternionf());
|
||||
}
|
||||
this.rotation.set(newRotation);
|
||||
this.xRot = controller.getPitch();
|
||||
this.yRot = controller.getYaw();
|
||||
var eulerAngs = newRotation.getEulerAnglesXYZ(new Vector3f());
|
||||
this.xRot = eulerAngs.x() * Mth.RAD_TO_DEG;
|
||||
this.yRot = eulerAngs.y() * Mth.RAD_TO_DEG;
|
||||
// Set the drivers rotations to match
|
||||
var driverEulerAngs = new Quaternionf(this.rotation).getEulerAnglesYXZ(new Vector3f());
|
||||
driver.setXRot(-driverEulerAngs.x() * Mth.RAD_TO_DEG + k);
|
||||
driver.setYRot(180.0f - (driverEulerAngs.y() * Mth.RAD_TO_DEG));
|
||||
|
||||
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
|
||||
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
|
||||
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void cobblemon$applyTransitionRotation(
|
||||
OrientationController vehicleController,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
// If the transition has just started then
|
||||
if (this.cobblemon$returnTimer == 0.0) {
|
||||
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
|
||||
}
|
||||
|
||||
if(this.cobblemon$returnTimer < 1) {
|
||||
//Rotation is taken from entity since we no longer handle mouse ourselves
|
||||
//Stops a period of time when you can't input anything.
|
||||
float interpolatedRoll = Mth.lerp(this.cobblemon$returnTimer, this.cobblemon$rollAngleStart, 0.0f);
|
||||
float pitch = (float) Math.toRadians(-this.entity.getXRot());
|
||||
float yaw = (float) Math.toRadians(180 - this.entity.getYRot());
|
||||
|
||||
Quaternionf interRot = new Quaternionf();
|
||||
interRot.rotationYXZ(yaw, pitch, interpolatedRoll);
|
||||
|
||||
this.rotation.set(interRot);
|
||||
var eulerAngs = interRot.getEulerAnglesXYZ(new Vector3f());
|
||||
this.xRot = eulerAngs.x() * Mth.RAD_TO_DEG;
|
||||
this.yRot = eulerAngs.y() * Mth.RAD_TO_DEG;
|
||||
OrientationController.Companion.getFORWARDS().rotate(this.rotation, this.forwards);
|
||||
OrientationController.Companion.getUP().rotate(this.rotation, this.up);
|
||||
OrientationController.Companion.getLEFT().rotate(this.rotation, this.left);
|
||||
|
||||
if(cobblemon$rollAngleStart == 0){
|
||||
this.cobblemon$returnTimer = 1;
|
||||
vehicleController.reset();
|
||||
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
|
||||
return;
|
||||
}
|
||||
this.cobblemon$returnTimer += (partialTickTime * (1.0f/20.0f));
|
||||
ci.cancel();
|
||||
} else {
|
||||
this.cobblemon$returnTimer = 1;
|
||||
vehicleController.reset();
|
||||
MountedCameraRenderer.INSTANCE.resetDriverRotations((Camera)(Object)this, this.entity);
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(method = "setup", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Camera;move(FFF)V", ordinal = 0))
|
||||
public void stopMoveIfRolling(Camera instance, float zoom, float dy, float dx, Operation<Void> original) {
|
||||
Entity entity = instance.getEntity();
|
||||
|
||||
@@ -19,9 +19,11 @@ import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState;
|
||||
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.duck.PlayerDuck;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
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.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.client.CameraType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -29,12 +31,10 @@ import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.Input;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -83,19 +83,33 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
|
||||
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V"))
|
||||
private void cobblemon$updateRotationMatrixPassenger(CallbackInfo ci) {
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var controller = controllable.getOrientationController();
|
||||
if (!controller.isActive() || controller.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = controller.getOrientation() != null ? new Matrix3f(controller.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(controller.getOrientation()));
|
||||
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
if (!vehicleController.isActive() || vehicleController.getOrientation() == cobblemon$lastOrientation) return;
|
||||
cobblemon$lastOrientation = vehicleController.getOrientation() != null ? new Matrix3f(vehicleController.getOrientation()) : null;
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateOrientationPacket(this.getVehicle().getId(), vehicleController.getOrientation()));
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateOrientationControllerRideTick(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
var driver = (LocalPlayer)(Object)this;
|
||||
var vehicle = driver.getVehicle();
|
||||
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
var shouldUseCustomOrientation = cobblemon$shouldUseCustomOrientation((LocalPlayer)(Object)this);
|
||||
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
|
||||
// If the player has just switched to riding a custom orientation ride then set their
|
||||
// x and y rots local to the vehicle rots(if the controller wasn't active and will now be active)
|
||||
// This ensures that on transition your camera stays in the same spot
|
||||
if (!vehicleController.isActive() && shouldUseCustomOrientation) {
|
||||
// Set local to the vehicle x and yrot so
|
||||
var playerRotater = (RidePassenger)driver;
|
||||
playerRotater.cobblemon$setRideXRot(Mth.wrapDegrees(driver.getXRot() - vehicle.getXRot()));
|
||||
playerRotater.cobblemon$setRideYRot(Mth.wrapDegrees(driver.getYRot() - vehicle.getYRot()));
|
||||
}
|
||||
|
||||
vehicleController.setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
@@ -115,6 +129,27 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(driverInput));
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateRiderRotationsRideTick(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
|
||||
|
||||
// Only send rotation updates if the player is riding a vehicle that is currently rollable
|
||||
if (!(this.getVehicle() instanceof OrientationControllable controllableVehicle)) return;
|
||||
if (!controllableVehicle.getOrientationController().getActive()) return;
|
||||
|
||||
var passenger = (LocalPlayer)(Object)this;
|
||||
var playerRotater = (RidePassenger)passenger;
|
||||
|
||||
// Gather Rider Rotation info
|
||||
var rideXRot = playerRotater.cobblemon$getRideXRot();
|
||||
var rideYRot = playerRotater.cobblemon$getRideYRot();
|
||||
var rideEyePos = playerRotater.cobblemon$getRideEyePos();
|
||||
|
||||
// Send new info to server. Currently not checking to see if this has change substantially
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateRiderRotationPacket(rideXRot, rideYRot, rideEyePos));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
|
||||
var playerVehicle = player.getVehicle();
|
||||
@@ -129,8 +164,8 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
public void stopRiding() {
|
||||
super.stopRiding();
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this instanceof OrientationControllable controllable)) return;
|
||||
controllable.getOrientationController().setActive(false);
|
||||
if (!(this instanceof OrientationControllable controllableVehicle)) return;
|
||||
controllableVehicle.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
@Inject(method = "getJumpRidingScale", at = @At("HEAD"), cancellable = true)
|
||||
|
||||
@@ -10,13 +10,16 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.client.CobblemonClient;
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext;
|
||||
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.Blaze3D;
|
||||
import kotlin.Pair;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.MouseHandler;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
@@ -32,6 +35,9 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static com.google.common.primitives.Floats.min;
|
||||
import static net.minecraft.util.Mth.lerp;
|
||||
|
||||
@Mixin(MouseHandler.class)
|
||||
public abstract class MouseHandlerMixin {
|
||||
@Shadow private double accumulatedScrollY;
|
||||
@@ -126,20 +132,22 @@ public abstract class MouseHandlerMixin {
|
||||
var defaultSensitivity = this.minecraft.options.sensitivity().get() * 0.6 + 0.2;
|
||||
var spyglassSensitivity = Math.pow(defaultSensitivity, 3);
|
||||
var lookSensitivity = spyglassSensitivity * 8.0;
|
||||
var sensitivity = Mth.lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
|
||||
var sensitivity = lerp(usageContext.getFovMultiplier(), spyglassSensitivity, lookSensitivity);
|
||||
var yRotationFlip = this.minecraft.options.invertYMouse().get() ? -1 : 1;
|
||||
player.turn(this.accumulatedDX * sensitivity, (this.accumulatedDY * sensitivity * yRotationFlip));
|
||||
returnValue = false;
|
||||
}
|
||||
|
||||
var vehicle = player.getVehicle();
|
||||
if (!(vehicle instanceof PokemonEntity)) return true;
|
||||
|
||||
// Clamp player rotation if riding and the vehicle demands it
|
||||
if (player.isPassenger() && player.getVehicle() instanceof PokemonEntity vehicle) {
|
||||
vehicle.clampPassengerRotation(player);
|
||||
if (player.isPassenger() && vehicle instanceof PokemonEntity pokeVehicle) {
|
||||
pokeVehicle.clampPassengerRotation(player);
|
||||
}
|
||||
|
||||
if (!(player instanceof OrientationControllable controllable)) return true;
|
||||
|
||||
if (!controllable.getOrientationController().isActive()) {
|
||||
// If the ride is not orientation controllable then return and perform the normal turnPlayer logic
|
||||
if (!(vehicle instanceof OrientationControllable controllableVehicle) || !controllableVehicle.getOrientationController().isActive()) {
|
||||
xMouseSmoother.reset();
|
||||
yMouseSmoother.reset();
|
||||
pitchSmoother.reset();
|
||||
@@ -148,12 +156,55 @@ public abstract class MouseHandlerMixin {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// Send mouse input to be interpreted into rotation
|
||||
// deltas by the ride controller
|
||||
var vehicleController = controllableVehicle.getOrientationController();
|
||||
var lookaroundButtonPressed = Minecraft.getInstance().mouseHandler.isMiddlePressed();
|
||||
Pair<Boolean, Boolean> mouseShouldModifyDriver = ((PokemonEntity) vehicle).ifRidingAvailableSupply(new Pair(false, false), (behaviour, settings, state) -> {
|
||||
return behaviour.mouseModifiesDriverRotation(
|
||||
settings,
|
||||
state,
|
||||
(PokemonEntity) vehicle
|
||||
);
|
||||
});
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// If looking around then modify the players orientation instead and zero the mouse deltas so the mouse doesn't
|
||||
// affect the vehicle orientation when looking around.
|
||||
// If the client is a passenger it will modify these rotations and return
|
||||
//-------------------------------------------------------------------------
|
||||
if (lookaroundButtonPressed || player.getControlledVehicle() == null) {
|
||||
// Modify driver rotations
|
||||
cobblemon$setDriverRotations(player, cursorDeltaX, cursorDeltaY);
|
||||
|
||||
// If the player/passenger is not the driver then return and ignore all vehicle rotation logic
|
||||
if (player.getControlledVehicle() == null) return false;
|
||||
|
||||
// Set to zero so that vehicle rotations don't also get affected by movement.
|
||||
cursorDeltaX = 0.0;
|
||||
cursorDeltaY = 0.0;
|
||||
} else if (mouseShouldModifyDriver.getFirst()) { // If not freelooking then check modification per mouse input dimension
|
||||
cobblemon$setDriverRotations(player, cursorDeltaX, 0.0);
|
||||
cursorDeltaX = 0.0;
|
||||
} else if (mouseShouldModifyDriver.getSecond()) {
|
||||
cobblemon$setDriverRotations(player, 0.0, cursorDeltaY);
|
||||
cursorDeltaY = 0.0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Smoothly return driver rotations to zero when needed
|
||||
//-------------------------------------------------------------------------
|
||||
var playerRotater = (RidePassenger)player;
|
||||
if( !lookaroundButtonPressed && !mouseShouldModifyDriver.getFirst()){
|
||||
playerRotater.cobblemon$setRideYRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideYRot()), 0.0f));
|
||||
}
|
||||
if( !lookaroundButtonPressed && !mouseShouldModifyDriver.getSecond()) {
|
||||
playerRotater.cobblemon$setRideXRot(lerp(min(5.0f * (float)movementTime, 1.0f), Mth.wrapDegrees(playerRotater.cobblemon$getRideXRot()), 0.0f));
|
||||
}
|
||||
|
||||
// Send mouse input to be interpreted into rotation deltas by the ride controller
|
||||
Vec3 angVecMouse = cobblemon$getRideMouseRotation(cursorDeltaX, cursorDeltaY, movementTime);
|
||||
|
||||
// Perform Rotation using mouse influenced rotation deltas.
|
||||
controllable.getOrientationController().rotate(
|
||||
vehicleController.rotate(
|
||||
(float) angVecMouse.x,
|
||||
(float) angVecMouse.y,
|
||||
(float) angVecMouse.z
|
||||
@@ -163,17 +214,16 @@ public abstract class MouseHandlerMixin {
|
||||
var angRot = cobblemon$getAngularVelocity(movementTime);
|
||||
|
||||
// Apply smoothing if requested by the controller.
|
||||
// This Might be best if done by the controller itself?
|
||||
if(cobblemon$shouldUseAngVelSmoothing()) {
|
||||
var yaw = yawSmoother.getNewDeltaValue(angRot.x * 0.5f, d);
|
||||
var pitch = pitchSmoother.getNewDeltaValue(angRot.y * 0.5f, d);
|
||||
var roll = rollSmoother.getNewDeltaValue(angRot.z * 0.5f, d);
|
||||
controllable.getOrientationController().rotate((float) yaw, (float) pitch, (float) roll);
|
||||
vehicleController.rotate((float) yaw, (float) pitch, (float) roll);
|
||||
}
|
||||
// Otherwise simply apply the smoothing
|
||||
else {
|
||||
controllable.getOrientationController().rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
vehicleController.rotate((float) (angRot.x * 10 * d), (float) (angRot.y * 10 * d), (float) (angRot.z * 10 * d));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -186,10 +236,11 @@ public abstract class MouseHandlerMixin {
|
||||
@Inject(method = "handleAccumulatedMovement", at = @At("TAIL"))
|
||||
private void cobblemon$maintainMovementWhenInScreens(CallbackInfo ci) {
|
||||
if (minecraft.player == null) return;
|
||||
if (!(minecraft.player instanceof OrientationControllable controllable)) return;
|
||||
if (!controllable.getOrientationController().isActive()) return;
|
||||
if (!(minecraft.player.getVehicle() instanceof OrientationControllable vehicleControllable)) return;
|
||||
if (!(vehicleControllable.getOrientationController() instanceof OrientationController vehicleController)) return;
|
||||
if (vehicleController.getOrientation() == null) return;
|
||||
if (minecraft.isPaused()) return;
|
||||
if (isMouseGrabbed()) return;
|
||||
if (this.isMouseGrabbed()) return; // return if turnPlayer() has already run this pass
|
||||
|
||||
this.turnPlayer(cobblemon$timeDelta);
|
||||
}
|
||||
@@ -251,6 +302,20 @@ public abstract class MouseHandlerMixin {
|
||||
return Math.pow(sensitivity, 3);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void cobblemon$setDriverRotations(LocalPlayer player, double cursorDeltaX, double cursorDeltaY) {
|
||||
// the vanilla turn() method redone so that it uses rideRotations.
|
||||
float f = (float)cursorDeltaY * 0.15F; // Note: turn() uses xRot for pitch (up/down)
|
||||
float g = (float)cursorDeltaX * 0.15F; // Note: turn() uses yRot for yaw (left/right)
|
||||
var playerRotater = (RidePassenger)player;
|
||||
playerRotater.cobblemon$setRideXRot(playerRotater.cobblemon$getRideXRot() + f);
|
||||
playerRotater.cobblemon$setRideYRot(playerRotater.cobblemon$getRideYRot() + g);
|
||||
playerRotater.cobblemon$setRideXRot(Mth.clamp(playerRotater.cobblemon$getRideXRot(), -90.0F, 90.0F));
|
||||
if (player.getVehicle() != null) {
|
||||
player.getVehicle().onPassengerTurned(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseAngVelSmoothing() {
|
||||
var player = minecraft.player;
|
||||
|
||||
@@ -8,20 +8,24 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.entity;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class EntityMixin {
|
||||
@@ -57,5 +61,46 @@ public abstract class EntityMixin {
|
||||
return original.call(entity);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "getEyePosition(F)Lnet/minecraft/world/phys/Vec3;",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$modifyEyePosition_partial(float partialTicks, CallbackInfoReturnable<Vec3> cir) {
|
||||
var entity = (Entity)(Object)this;
|
||||
if (!(entity instanceof Player player)) return;
|
||||
if (!(player instanceof RidePassenger ridePassenger)) return;
|
||||
if (!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
|
||||
var vehicleController = vehicle.getOrientationController();
|
||||
if (vehicleController == null) return;
|
||||
|
||||
// If Orientation is currently being controlled then use the stored eye position from camera positioning.
|
||||
if (vehicleController.getActive() && vehicleController.getOrientation() != null) {
|
||||
// TODO: Determine if it needs partialtick involved.
|
||||
Vec3 customEyePos = ridePassenger.cobblemon$getRideEyePos();
|
||||
cir.setReturnValue(customEyePos);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "getEyePosition()Lnet/minecraft/world/phys/Vec3;",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void cobblemon$modifyEyePosition_noPartial(CallbackInfoReturnable<Vec3> cir) {
|
||||
var entity = (Entity)(Object)this;
|
||||
if (!(entity instanceof Player player)) return;
|
||||
if (!(player instanceof RidePassenger ridePassenger)) return;
|
||||
if (!(player.getVehicle() instanceof OrientationControllable vehicle)) return;
|
||||
var vehicleController = vehicle.getOrientationController();
|
||||
if (vehicleController == null) return;
|
||||
|
||||
// If Orientation is currently being controlled then use the stored eye position from camera positioning.
|
||||
if (vehicleController.getActive() && vehicleController.getOrientation() != null) {
|
||||
// TODO: Determine if it needs partialtick involved.
|
||||
Vec3 customEyePos = ridePassenger.cobblemon$getRideEyePos();
|
||||
cir.setReturnValue(customEyePos);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import net.minecraft.core.UUIDUtil
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.GlobalPos
|
||||
import net.minecraft.core.UUIDUtil
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import java.util.*
|
||||
|
||||
object CobblemonMemories {
|
||||
val memories = mutableMapOf<String, MemoryModuleType<*>>()
|
||||
@@ -48,10 +48,13 @@ object CobblemonMemories {
|
||||
val TIME_TRYING_TO_REACH_BERRY_BUSH = register<Int>("time_trying_to_reach_berry_bush")
|
||||
val IS_CONSUMING_ITEM = register<Boolean>("is_consuming_item")
|
||||
val RECENTLY_ATE_GRASS = register<Boolean>("recently_ate_grass")
|
||||
val HIVE_LOCATION = register<BlockPos>("hive_location")
|
||||
val HIVE_LOCATION = register<BlockPos>("hive_location", BlockPos.CODEC)
|
||||
val HIVE_BLACKLIST = register<List<BlockPos>>("hive_blacklist")
|
||||
val HIVE_COOLDOWN = register<Boolean>("hive_cooldown")
|
||||
val NEARBY_FLOWERS = register<List<BlockPos>>("nearby_flower")
|
||||
val POLLINATED = register<Boolean>("pollinated", PrimitiveCodec.BOOL)
|
||||
val NEARBY_FLOWER = register<BlockPos>("nearby_flower", BlockPos.CODEC)
|
||||
val PATH_TO_NEARBY_FLOWER_COOLDOWN = register<Boolean>("path_to_nearby_flower_cooldown")
|
||||
val HAS_NECTAR = register<Boolean>("has_nectar", PrimitiveCodec.BOOL)
|
||||
val NEARBY_SACC_LEAVES = register<BlockPos>("nearby_sacc_leaves", BlockPos.CODEC)
|
||||
/** who am i following rn? */
|
||||
val HERD_LEADER = register<String>("herd_leader", PrimitiveCodec.STRING)
|
||||
/** how many are following me rn? */
|
||||
|
||||
@@ -179,6 +179,7 @@ import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetNicknamePa
|
||||
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.riding.ServerboundUpdateRiderRotationPacket
|
||||
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
|
||||
@@ -231,6 +232,7 @@ import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHan
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetNicknameHandler
|
||||
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.riding.ServerboundUpdateRiderRotationHandler
|
||||
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
|
||||
@@ -584,6 +586,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingSettingsPacket.ID, ServerboundUpdateRidingSettingsPacket::decode, ServerboundUpdateRidingSettingsHandler))
|
||||
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRiderRotationPacket.ID, ServerboundUpdateRiderRotationPacket::decode, ServerboundUpdateRiderRotationHandler))
|
||||
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineSettings
|
||||
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
|
||||
@@ -63,6 +64,7 @@ object CobblemonRideSettings : DataRegistry {
|
||||
var boat = BoatSettings()
|
||||
var burst = BurstSettings()
|
||||
var dolphin = DolphinSettings()
|
||||
var submarine = SubmarineSettings()
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
player.sendPacket(
|
||||
@@ -78,7 +80,8 @@ object CobblemonRideSettings : DataRegistry {
|
||||
vehicle = vehicle,
|
||||
boat = boat,
|
||||
burst = burst,
|
||||
dolphin = dolphin
|
||||
dolphin = dolphin,
|
||||
submarine = submarine
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -96,6 +99,7 @@ object CobblemonRideSettings : DataRegistry {
|
||||
boat = loadStyle(manager, "boat", BoatSettings::class.java)
|
||||
burst = loadStyle(manager, "burst", BurstSettings::class.java)
|
||||
dolphin = loadStyle(manager, "dolphin", DolphinSettings::class.java)
|
||||
submarine = loadStyle(manager, "submarine", SubmarineSettings::class.java)
|
||||
}
|
||||
|
||||
private fun <T : RidingBehaviourSettings> loadStyle(manager: ResourceManager, name: String, clazz: Class<T>): T {
|
||||
|
||||
@@ -41,6 +41,8 @@ object CobblemonSensors {
|
||||
|
||||
val POKEMON_NEARBY_WANTED_ITEM = register("pokemon_nearby_wanted_item", ::PokemonItemSensor)
|
||||
|
||||
val NEARBY_SACC_LEAVES = register("nearby_sacc_leaves", ::SacLeavesSensor)
|
||||
|
||||
fun <E : Entity, U : Sensor<E>> register(id: String, supplier: Supplier<U>): SensorType<U> {
|
||||
val sensor = SensorType(supplier)
|
||||
sensors[id] = sensor
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.cobblemon.mod.common.util.resolveString
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -62,7 +63,7 @@ class MemoryAspectTaskConfig : SingleTaskConfig {
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long) = true
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
val hasMemory = !owner.brain.hasMemoryValue(memory)
|
||||
val hasMemory = owner.brain.hasMemoryValue(memory)
|
||||
if (owner is PokemonEntity) {
|
||||
if (!hasMemory && aspect in owner.pokemon.forcedAspects) {
|
||||
owner.pokemon.forcedAspects -= aspect
|
||||
|
||||
+118
-2
@@ -8,16 +8,31 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonSensors
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToBeeHiveTask
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceHoneyInHiveTask
|
||||
import com.cobblemon.mod.common.util.distanceTo
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PathToBeeHiveTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val HONEY = "honey"
|
||||
const val STAY_OUT_OF_HIVE_COOLDOWN = 400
|
||||
|
||||
}
|
||||
|
||||
val condition = booleanVariable(HONEY, "can_add_honey", true).asExpressible()
|
||||
@@ -36,6 +51,107 @@ class PathToBeeHiveTaskConfig : SingleTaskConfig {
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
return PathToBeeHiveTask.create()
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_BEE_HIVE)
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(
|
||||
MemoryModuleType.LOOK_TARGET to MemoryStatus.REGISTERED,
|
||||
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HAS_NECTAR to MemoryStatus.REGISTERED,
|
||||
CobblemonMemories.HIVE_LOCATION to MemoryStatus.VALUE_PRESENT,
|
||||
CobblemonMemories.HIVE_COOLDOWN to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HIVE_BLACKLIST to MemoryStatus.REGISTERED,
|
||||
),
|
||||
400,
|
||||
400
|
||||
) {
|
||||
|
||||
val maxTicks = 400
|
||||
var traveledTicks = 0
|
||||
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
|
||||
return (owner is PathfinderMob && owner.isAlive && (owner is PokemonEntity && PlaceHoneyInHiveTask.wantsToEnterHive(owner))) && owner.brain.checkMemory(
|
||||
MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT)
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
|
||||
if (maxTicks >= traveledTicks) return false
|
||||
val pos = entity.brain.getMemory(CobblemonMemories.HIVE_LOCATION)
|
||||
return !pos.isEmpty && entity.distanceTo(pos.get()) > 1.0
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
super.start(level, entity, gameTime)
|
||||
val hiveLocation = entity.brain.getMemorySafely(CobblemonMemories.HIVE_LOCATION).orElse(null)
|
||||
val world = entity.level()
|
||||
val sidesByClosest = listOf(
|
||||
hiveLocation.north(),
|
||||
hiveLocation.south(),
|
||||
hiveLocation.east(),
|
||||
hiveLocation.west()
|
||||
).sortedBy { it.distSqr(entity.blockPosition()) }
|
||||
|
||||
var openSide = BlockPos.ZERO
|
||||
for (side in sidesByClosest) {
|
||||
if (world.getBlockState(side).isAir) {
|
||||
openSide = side
|
||||
break
|
||||
}
|
||||
}
|
||||
if (openSide == BlockPos.ZERO) {
|
||||
if (world.getBlockState(hiveLocation.above()).isAir) {
|
||||
// If the above position is also air, we can use it
|
||||
openSide = hiveLocation.above() // Fallback to above if no open sides found
|
||||
} else if (world.getBlockState(hiveLocation.below()).isAir) {
|
||||
// If the below position is also air, we can use it
|
||||
openSide = hiveLocation.below() // Fallback to below if no open sides found
|
||||
} else {
|
||||
// If no open sides or above/below positions are found, we cannot proceed
|
||||
return
|
||||
}
|
||||
}
|
||||
val targetVec = Vec3.atCenterOf(openSide)
|
||||
|
||||
// Set path target toward hive
|
||||
if (pathfindDirectlyTowards(openSide, entity)) {
|
||||
entity.brain.setMemory(MemoryModuleType.WALK_TARGET,WalkTarget(targetVec, 0.35F, 0))
|
||||
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET,BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
traveledTicks = 0
|
||||
} else {
|
||||
dropAndBlackListHive(entity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
traveledTicks++
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
if (maxTicks < traveledTicks) {
|
||||
dropAndBlackListHive(entity)
|
||||
entity.brain.setMemoryWithExpiry(CobblemonMemories.HIVE_COOLDOWN, true, 100)
|
||||
}
|
||||
}
|
||||
|
||||
fun dropAndBlackListHive(entity: LivingEntity) {
|
||||
val hivePos = entity.brain.getMemorySafely(CobblemonMemories.HIVE_LOCATION).orElse(null)
|
||||
if (hivePos == null) return
|
||||
val blackList = entity.brain.getMemorySafely(CobblemonMemories.HIVE_BLACKLIST).orElse(emptyList()).toMutableList()
|
||||
|
||||
blackList.add(hivePos)
|
||||
if (blackList.count() > 3) {
|
||||
blackList.removeFirst()
|
||||
}
|
||||
entity.brain.eraseMemory(CobblemonMemories.HIVE_LOCATION)
|
||||
entity.brain.setMemory(CobblemonMemories.HIVE_BLACKLIST, blackList)
|
||||
|
||||
}
|
||||
|
||||
private fun pathfindDirectlyTowards(pos: BlockPos, entity: LivingEntity): Boolean {
|
||||
if (entity !is PathfinderMob) return false
|
||||
val nav = entity.navigation
|
||||
val path = nav. createPath(pos, 0)
|
||||
return path != null && path.canReach()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonSensors
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
@@ -41,12 +40,14 @@ class PathToFlowerTaskConfig : SingleTaskConfig {
|
||||
return null
|
||||
}
|
||||
behaviourConfigurationContext.addMemories(
|
||||
CobblemonMemories.NEARBY_FLOWERS,
|
||||
CobblemonMemories.NEARBY_FLOWER,
|
||||
CobblemonMemories.PATH_TO_NEARBY_FLOWER_COOLDOWN,
|
||||
CobblemonMemories.HIVE_COOLDOWN,
|
||||
CobblemonMemories.HIVE_LOCATION,
|
||||
CobblemonMemories.POLLINATED
|
||||
CobblemonMemories.HIVE_BLACKLIST,
|
||||
CobblemonMemories.HAS_NECTAR,
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_BEE_HIVE, CobblemonSensors.NEARBY_FLOWER)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_FLOWER)
|
||||
return PathToFlowerTask.create()
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonSensors
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PathToSacLeafTask
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
|
||||
class PathToSaccLeavesTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
behaviourConfigurationContext.addMemories(
|
||||
CobblemonMemories.NEARBY_SACC_LEAVES,
|
||||
CobblemonMemories.HAS_NECTAR
|
||||
)
|
||||
behaviourConfigurationContext.addSensors(CobblemonSensors.NEARBY_SACC_LEAVES, CobblemonSensors.NEARBY_FLOWER)
|
||||
return PathToSacLeafTask.create()
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -8,8 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.PlaceHoneyInHiveTask
|
||||
@@ -37,6 +37,9 @@ class PlaceHoneyInHiveTaskConfig : SingleTaskConfig {
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
return PlaceHoneyInHiveTask.create()
|
||||
return WrapperLivingEntityTask(
|
||||
PlaceHoneyInHiveTask.create(),
|
||||
PokemonEntity::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class PlaceHoneyInSaccLeavesTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
const val MIN_DURATION : Int = 300
|
||||
const val MAX_DURATION : Int = 400
|
||||
const val REQUIRED_SUCCESSFUL_POLLINATION_TICKS = 250
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition
|
||||
).asVariables()
|
||||
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is PokemonEntity) {
|
||||
return null
|
||||
}
|
||||
if (!checkCondition(behaviourConfigurationContext.runtime, condition)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(
|
||||
CobblemonMemories.NEARBY_SACC_LEAVES to MemoryStatus.VALUE_PRESENT,
|
||||
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HAS_NECTAR to MemoryStatus.VALUE_PRESENT,
|
||||
),
|
||||
MIN_DURATION,
|
||||
MAX_DURATION
|
||||
) {
|
||||
var successfulPollinationTicks = 0
|
||||
var lastSoundPlayedTick = 0
|
||||
var hoverPos: Vec3? = null
|
||||
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
|
||||
if (level.isNight || level.isRaining || !(entity.brain.getMemorySafely(CobblemonMemories.HAS_NECTAR).orElse(false))) return false
|
||||
val optionalBlockPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES)
|
||||
if (!optionalBlockPos.isPresent || level.getBlockState(optionalBlockPos.get()).block != CobblemonBlocks.SACCHARINE_LEAVES) return false
|
||||
return Vec3.atCenterOf(optionalBlockPos.get()).distanceTo(entity.position()) <= 0.6
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
|
||||
return checkExtraStartConditions(level, entity) && entity.brain.activeActivities.indexOf(
|
||||
CobblemonActivities.POKEMON_POLLINATION) != -1
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
super.start(level, entity, gameTime)
|
||||
hoverPos = null
|
||||
}
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
++successfulPollinationTicks
|
||||
if (entity.random.nextFloat() < 0.05f && successfulPollinationTicks > this.lastSoundPlayedTick + 60) {
|
||||
this.lastSoundPlayedTick = successfulPollinationTicks
|
||||
entity.playSound(SoundEvents.BEEHIVE_DRIP, 1.0f, 1.0f)
|
||||
}
|
||||
val leavesPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES).orElse(null)
|
||||
|
||||
val bl = hoverPos == null || entity.position().distanceTo(hoverPos!!) <= 0.1
|
||||
val bl3 = entity.random.nextInt(40) == 0
|
||||
if (bl && bl3) {
|
||||
// mimics the wriggling over a flower the vanilla bees do
|
||||
if (hoverPos == null) {
|
||||
hoverPos = leavesPos.bottomCenter.add(0.0, 0.4, 0.0)
|
||||
}
|
||||
if (leavesPos != null) {
|
||||
hoverPos = leavesPos.bottomCenter.add(Vec3((entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0), 0.3 + (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/7.0), (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0)))
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
entity.lookControl.setLookAt(leavesPos.bottomCenter.add(0.0,0.5,0.0))
|
||||
}
|
||||
} else if (!bl) {
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
if (successfulPollinationTicks > REQUIRED_SUCCESSFUL_POLLINATION_TICKS) {
|
||||
val blockPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES).orElse(null)
|
||||
blockPos.let {
|
||||
val blockState = level.getBlockState(blockPos)
|
||||
if (blockState.block == CobblemonBlocks.SACCHARINE_LEAVES) {
|
||||
val saccAge = blockState.getValue( SaccharineLeafBlock.AGE)
|
||||
if (saccAge < SaccharineLeafBlock.MAX_AGE) {
|
||||
entity.brain.eraseMemory(CobblemonMemories.HAS_NECTAR)
|
||||
val newBlockState = blockState.setValue(SaccharineLeafBlock.AGE, saccAge + 1)
|
||||
level.setBlock(blockPos, newBlockState, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
successfulPollinationTicks = 0
|
||||
entity.brain.eraseMemory(MemoryModuleType.WALK_TARGET)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
-24
@@ -8,17 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonActivities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.cobblemon.mod.common.util.sendParticlesServer
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
@@ -29,10 +26,11 @@ import net.minecraft.world.phys.Vec3
|
||||
class PollinateFlowerTaskConfig : SingleTaskConfig {
|
||||
companion object {
|
||||
const val POLLINATE = "pollinate"
|
||||
const val PATH_TO_FLOWER_COOLDOWN_TICKS : Long = 200
|
||||
const val HAS_NECTAR_ASPECT = "has_nectar"
|
||||
}
|
||||
|
||||
val condition = booleanVariable(POLLINATE, "can_pollinate", true).asExpressible()
|
||||
val durationTicks: ExpressionOrEntityVariable = Either.left("30".asExpression())
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
|
||||
condition
|
||||
@@ -51,22 +49,28 @@ class PollinateFlowerTaskConfig : SingleTaskConfig {
|
||||
|
||||
return object : Behavior<LivingEntity>(
|
||||
mapOf(
|
||||
CobblemonMemories.NEARBY_FLOWERS to MemoryStatus.VALUE_PRESENT,
|
||||
CobblemonMemories.NEARBY_FLOWER to MemoryStatus.VALUE_PRESENT,
|
||||
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.POLLINATED to MemoryStatus.VALUE_ABSENT,
|
||||
CobblemonMemories.HAS_NECTAR to MemoryStatus.REGISTERED,
|
||||
CobblemonMemories.HIVE_COOLDOWN to MemoryStatus.VALUE_ABSENT,
|
||||
),
|
||||
15,
|
||||
45
|
||||
600,
|
||||
625
|
||||
) {
|
||||
val REQUIRED_SUCCESSFUL_POLLINATION_TICKS = 400
|
||||
var successfulPollinationTicks = 0
|
||||
var lastSoundPlayedTick = 0
|
||||
var hoverPos: Vec3? = null
|
||||
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity): Boolean {
|
||||
return entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWERS).orElse(emptyList()).any {
|
||||
Vec3.atCenterOf(it).distanceTo(entity.position()) <= 1.0
|
||||
}
|
||||
if (level.isNight || level.isRaining || entity.brain.getMemorySafely(CobblemonMemories.HAS_NECTAR).orElse(false)) return false
|
||||
val optionalBlockPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWER)
|
||||
return optionalBlockPos.isPresent && Vec3.atCenterOf(optionalBlockPos.get()).distanceTo(entity.position()) <= 1.0
|
||||
}
|
||||
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long): Boolean {
|
||||
return checkExtraStartConditions(level, entity)
|
||||
return checkExtraStartConditions(level, entity) && entity.brain.activeActivities.indexOf(
|
||||
CobblemonActivities.POKEMON_POLLINATION) != -1
|
||||
}
|
||||
|
||||
override fun start(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
@@ -74,20 +78,44 @@ class PollinateFlowerTaskConfig : SingleTaskConfig {
|
||||
}
|
||||
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
level.sendParticlesServer(
|
||||
particleType = ParticleTypes.DRIPPING_HONEY,
|
||||
position = owner.eyePosition,
|
||||
particles = 3,
|
||||
offset = Vec3.ZERO,
|
||||
speed = 0.0
|
||||
)
|
||||
++successfulPollinationTicks
|
||||
if (entity.random.nextFloat() < 0.05f && successfulPollinationTicks > this.lastSoundPlayedTick + 60) {
|
||||
this.lastSoundPlayedTick = successfulPollinationTicks
|
||||
entity.playSound(SoundEvents.BEE_POLLINATE, 1.0f, 1.0f)
|
||||
}
|
||||
val flowerPos = entity.brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWER).orElse(null)
|
||||
|
||||
|
||||
val bl = hoverPos == null || entity.position().distanceTo(hoverPos!!) <= 0.1
|
||||
val bl3 = entity.random.nextInt(40) == 0
|
||||
if (bl && bl3) {
|
||||
// mimics the wriggling over a flower the vanilla bees do
|
||||
if (hoverPos == null) {
|
||||
hoverPos = flowerPos.bottomCenter.add(0.0, 0.4, 0.0)
|
||||
}
|
||||
|
||||
if (flowerPos != null) {
|
||||
hoverPos = flowerPos.bottomCenter.add(Vec3((entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0), 0.3 + (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/7.0), (entity.random.nextDouble() * 2.0 - 1.0) * (1.0/5.0)))
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
entity.lookControl.setLookAt(flowerPos.bottomCenter.add(0.0,0.5,0.0))
|
||||
}
|
||||
} else if (!bl) {
|
||||
entity.getMoveControl()
|
||||
.setWantedPosition(hoverPos!!.x(), hoverPos!!.y(), hoverPos!!.z(), 0.35)
|
||||
}
|
||||
entity.lookControl.setLookAt(flowerPos.bottomCenter.add(0.0,0.5,0.0))
|
||||
|
||||
}
|
||||
|
||||
override fun stop(level: ServerLevel, entity: LivingEntity, gameTime: Long) {
|
||||
val flowerStillThere = canStillUse(level, entity, gameTime)
|
||||
if (flowerStillThere) {
|
||||
entity.brain.setMemory(CobblemonMemories.POLLINATED, true)
|
||||
|
||||
if (successfulPollinationTicks > REQUIRED_SUCCESSFUL_POLLINATION_TICKS) {
|
||||
entity.brain.setMemory(CobblemonMemories.HAS_NECTAR, true)
|
||||
}
|
||||
successfulPollinationTicks = 0
|
||||
entity.brain.setMemoryWithExpiry(CobblemonMemories.PATH_TO_NEARBY_FLOWER_COOLDOWN, true, PATH_TO_FLOWER_COOLDOWN_TICKS)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,9 @@ interface TaskConfig {
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
|
||||
cobblemonResource("path_to_hive") to PathToBeeHiveTaskConfig::class.java,
|
||||
cobblemonResource("place_honey_in_hive") to PlaceHoneyInHiveTaskConfig::class.java,
|
||||
cobblemonResource("place_honey_in_sacc_leaves") to PlaceHoneyInSaccLeavesTaskConfig::class.java,
|
||||
cobblemonResource("path_to_flower") to PathToFlowerTaskConfig::class.java,
|
||||
cobblemonResource("path_to_sacc_leaves") to PathToSaccLeavesTaskConfig::class.java,
|
||||
cobblemonResource("pollinate_flower") to PollinateFlowerTaskConfig::class.java,
|
||||
cobblemonResource("go_to_land") to GoToLandTaskConfig::class.java,
|
||||
cobblemonResource("manage_flight_in_battle") to ManageFlightInBattleTaskConfig::class.java,
|
||||
|
||||
+2
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.aspect
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.api.ai.config.task.PollinateFlowerTaskConfig
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SaccharineLogSlatheredInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.LurePokeSnackDetector
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
@@ -27,6 +28,7 @@ val aspectParticleMap: Map<String, ParticleData> = mapOf(
|
||||
ParticleTypes.BLOCK,
|
||||
CobblemonBlocks.POKE_SNACK.defaultBlockState()
|
||||
), 0.05, 3),
|
||||
PollinateFlowerTaskConfig.HAS_NECTAR_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_NECTAR, 0.075, 1)
|
||||
)
|
||||
|
||||
sealed class ParticleData {
|
||||
|
||||
+12
@@ -103,6 +103,18 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
*/
|
||||
fun damageOnCollision(settings: Settings, state: State, vehicle: PokemonEntity, impactVec: Vec3): Boolean = false
|
||||
|
||||
/**
|
||||
* Called in [com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer] to allow the controller to set rotations
|
||||
* through code using partialTicks and right before rendering.
|
||||
* //TODO: This needs to be further refined. Maybe only the orientationController needs affected? Maybe the rotations set here don't need to be applied to the controller/camera
|
||||
*/
|
||||
fun applyRenderRotation(settings: Settings, state: State, vehicle: PokemonEntity, partialTicks: Float) {}
|
||||
|
||||
/**
|
||||
* Determines if the pair of mouse inputs Pair(mouseX, mouseY) get used to modify the driverRotations or the vehicle rotations themselves.
|
||||
* Driver rotations are the rotations used by passengers or a driver that is freelooking. This does not override the freelook button.
|
||||
*/
|
||||
fun mouseModifiesDriverRotation(settings: Settings, state: State, vehicle: PokemonEntity): Pair<Boolean, Boolean> = Pair(false, false)
|
||||
|
||||
/**
|
||||
* Internal helpers to help behaviour specific calculations
|
||||
|
||||
+1
-1
@@ -30,6 +30,6 @@ interface RidingBehaviourSettings: Encodable, Decodable {
|
||||
|
||||
fun calculate(stat: RidingStat, boostAmount: Float): Float {
|
||||
val range = stats[stat] ?: return 0F
|
||||
return range.first + boostAmount
|
||||
return (range.first + boostAmount).coerceAtMost(range.endInclusive.toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviou
|
||||
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
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineBehaviour
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
object RidingBehaviours {
|
||||
@@ -34,6 +35,7 @@ object RidingBehaviours {
|
||||
register(MinekartBehaviour.KEY, MinekartBehaviour())
|
||||
register(HoverBehaviour.KEY, HoverBehaviour())
|
||||
register(RocketBehaviour.KEY, RocketBehaviour())
|
||||
register(SubmarineBehaviour.KEY, SubmarineBehaviour())
|
||||
register(CompositeBehaviour.KEY, CompositeBehaviour())
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -19,6 +19,7 @@ import net.minecraft.world.entity.LivingEntity
|
||||
import kotlin.collections.get
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import kotlin.collections.get
|
||||
|
||||
/**
|
||||
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
|
||||
@@ -109,12 +110,22 @@ class RidingController(
|
||||
}
|
||||
|
||||
private fun canTransitionToLand(): Boolean {
|
||||
if (entity.isInLiquid || entity.isUnderWater) return false
|
||||
val hasLandController = entity.pokemon.riding.behaviours?.get(RidingStyle.LAND) != null
|
||||
if (hasLandController && (entity.isInLiquid || entity.isUnderWater)) return false
|
||||
if (!hasLandController && (entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA))) return false
|
||||
return entity.onGround()
|
||||
}
|
||||
|
||||
private fun canTransitionToLiquid(): Boolean {
|
||||
return entity.isInLiquid || entity.isUnderWater
|
||||
// If it has a liquid controller to transition to and it isn't currently an air controller then transition on
|
||||
// touching water. If not then check for eyes in water. This allows fliers to fly closely above water and
|
||||
// for walkers that don't have a liquid controller to be able to walk in not deep water
|
||||
return if (entity.pokemon.riding.behaviours?.get(RidingStyle.LIQUID) != null && context?.style != RidingStyle.AIR) {
|
||||
entity.isInLiquid || entity.isUnderWater
|
||||
} else {
|
||||
entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun canTransitionToAir(driver: LivingEntity?): Boolean {
|
||||
|
||||
+136
-166
@@ -34,6 +34,7 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
|
||||
class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
@@ -54,18 +55,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
get() = CobblemonRideSettings.bird
|
||||
|
||||
override fun isActive(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
}
|
||||
return !(vehicle.isInWater || vehicle.isUnderWater)
|
||||
}
|
||||
|
||||
override fun pose(settings: BirdSettings, state: BirdState, vehicle: PokemonEntity): PoseType {
|
||||
@@ -99,7 +89,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
) {
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
// Also checks stamina
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
val upsideDownRate = 1.0 / (2.0 * 20.0) // 2 seconds to max out upsideDown force
|
||||
state.currUpsideDownForce.set( min(1.0, state.currUpsideDownForce.get() + upsideDownRate * abs(cos(controller.roll.toRadians()))))
|
||||
@@ -118,16 +109,28 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalBird.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
|
||||
// proportional to the number of ticks of boost thus making a full boost take x ticks
|
||||
// in short: "Stamina drains slower at higher values and also replenishes slower"
|
||||
val totalStam = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalBird.staminaExpr!!) * 20.0f
|
||||
val stamDrainRate = (1.0f / totalStam).toFloat()
|
||||
val horzMod = vehicle.runtime.resolveDouble(settings.stamDrainHorzMod ?: globalBird.stamDrainHorzMod!!)
|
||||
val glideMod = vehicle.runtime.resolveFloat(settings.stamDrainPercRateGlide ?: globalBird.stamDrainPercRateGlide!!)
|
||||
val stamDrainPerRateDive = vehicle.runtime.resolveDouble(settings.stamDrainPerRateDive ?: globalBird.stamDrainPerRateDive!!).coerceIn(0.0, 1.0) // Ensure its between 1 and 0 or things get weird
|
||||
|
||||
val newStam = if (driver.zza > 0.0) max(0.0f,stam - stamDrainRate)
|
||||
else if (!state.gliding.get()) max(0.0f,stam - stamDrainRate * 0.5f) // if hovering half the stam drain
|
||||
else min(1.0f,stam + stamDrainRate * 0.2f)
|
||||
val stamDrainHorz = if(vehicle.deltaMovement.horizontalDistance() < 1e-6) horzMod else max(1.0 + ((horzMod - 1.0) * atan( vehicle.deltaMovement.y / vehicle.deltaMovement.horizontalDistance())), 1.0)
|
||||
val diveAmount = sin(controller.pitch.toRadians()).coerceIn(0.0f, 1.0f)// from 0.0 -> 1.0 how steep is the current dive angle
|
||||
val diveMod = ((1.0 - diveAmount) * ( 1.0 - stamDrainPerRateDive ) + stamDrainPerRateDive).toFloat() // smooth between 1.0 and the dive drain mod depending upon how dived the ride is
|
||||
val stamDrainRate = (1.0f / totalStam).toFloat() * stamDrainHorz.toFloat()
|
||||
|
||||
val newStam = if (driver.deltaMovement.length() > 0.1) max(0.0f,stam - stamDrainRate) // Drain at normal fly rate
|
||||
else if (state.gliding.get()) min(1.0f,stam - stamDrainRate * glideMod * diveMod) // Gliding drain logic
|
||||
else max(0.0f,stam - stamDrainRate * 0.5f) // if hovering half the stam drain
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
@@ -138,7 +141,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
return Vec2.ZERO//Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
@@ -148,29 +151,26 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
val controller = (vehicle as? OrientationControllable)?.orientationController
|
||||
if (controller == null || controller.orientation == null) return Vec3.ZERO
|
||||
|
||||
//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) {
|
||||
//Need to deadzone this when straight up or down
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// The downward force used to encourage players to stop flying upside down.
|
||||
val extraDownwardForce = state.currUpsideDownForce.get().pow(2) * -0.3 // 6 blocks a second downward
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce + extraDownwardForce, forwardForce)
|
||||
return velocity
|
||||
// Convert the local velocity vector into a world vector
|
||||
val localVelVec = Vector3f(
|
||||
state.rideVelocity.get().x.toFloat(),
|
||||
(state.rideVelocity.get().y).toFloat(),
|
||||
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed
|
||||
)
|
||||
// Unrotate preemptively as this vector gets rotatee later down the line in MC logic.
|
||||
var worldVelVec = localVelVec.mul(controller.orientation).toVec3d().yRot(vehicle.yRot.toRadians())
|
||||
worldVelVec = worldVelVec.add(0.0, extraDownwardForce, 0.0) // Add the stamina depletion force to bring the ride down.
|
||||
|
||||
return worldVelVec
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -191,8 +191,8 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
newVelocity = newVelocity.normalize().scale(vehicle.deltaMovement.length())
|
||||
}
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
// speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && newVelocity.length() < glideTopSpeed) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
state.stamina.get() == 0.0f -> 0.0
|
||||
@@ -209,7 +209,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
val controller = (vehicle as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Base glide speed change on current pitch of the ride.
|
||||
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
@@ -285,15 +285,13 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (Cobblemon.config.disableRoll) return Vec3.ZERO
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
|
||||
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //* sin(Math.toRadians(controller.roll.toDouble())).sign
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))// * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed))
|
||||
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble()))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))*2).coerceIn(-1.0, 1.0)
|
||||
|
||||
// Dampen yaw when upside down
|
||||
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)).pow(2))
|
||||
@@ -302,10 +300,13 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
// Correct orientation when at low speeds
|
||||
correctOrientation(settings, state, vehicle, controller, deltaTime)
|
||||
|
||||
controller.applyGlobalPitch(state.currPitchCorrectionForce.get().toRadians())
|
||||
val pCorr = (state.currPitchCorrectionForce.get() * deltaTime).toFloat()
|
||||
val rCorr = (state.currRollCorrectionForce.get() * deltaTime).toFloat()
|
||||
controller.applyGlobalPitch(pCorr)
|
||||
controller.rotateRoll(rCorr)
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
|
||||
return Vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
@@ -316,45 +317,48 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
deltaTime: Double
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalBird.timeToRollCorrect!!)
|
||||
val maxRollCorrectionRate = if (state.rideVelocity.get().length() < 0.1) 15.0f else 8.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTimeRoll.get() - rollCorrectionTimer)).coerceIn(0.0, 1.0).toFloat()
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0
|
||||
val rollArrivalDeg = 90.0
|
||||
if(state.rideVelocity.get().length() < 0.1 || state.noInputTimeRoll.get() > rollCorrectionTimer) {
|
||||
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
|
||||
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
// If the ride is moving then have the correction rate be an equal proportion of:
|
||||
// - how rolled are you
|
||||
// - how long has it been since last mouse input (higher is stronger correction)
|
||||
// Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
|
||||
val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
|
||||
val maxRollCorrectionRate =
|
||||
if (state.rideVelocity.get().length() < 0.2) 45.0f
|
||||
else 45.0f * cos(controller.pitch.toRadians()).pow(2) * (
|
||||
howRolledAmI*1.0f +
|
||||
(state.noInputTimeRoll.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
|
||||
)
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0f
|
||||
val rollArrivalDeg = 35.0f
|
||||
val desiredRoll = 0.0f
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
}
|
||||
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
} else {
|
||||
state.currRollCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
|
||||
)
|
||||
// Calculate the signed error (how far and in what direction)
|
||||
val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
|
||||
// Use the absolute error to calculate the magnitude of the influence
|
||||
val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
|
||||
val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
}
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 8f * state.currUpsideDownForce.get() else 15f
|
||||
val maxPitchForce = maxPitchCorrectionRate.toFloat() / 40.0f
|
||||
val pitchArrivalDeg = 45.0f
|
||||
var desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0 else 0.0
|
||||
// if (cos(controller.roll.toRadians()) < 0.0) {
|
||||
// desiredPitch *= -1 // If not rolled over then flip the pitch force. There are better ways to do this :(
|
||||
// }
|
||||
val maxPitchCorrectionRate = if (state.currUpsideDownForce.get() != 0.0) 15.0f * state.currUpsideDownForce.get().toFloat() else 30.0f
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0f
|
||||
val pitchArrivalDeg = 35.0f
|
||||
val desiredPitch = if (state.currUpsideDownForce.get() != 0.0) 90.0f else 0.0f
|
||||
|
||||
if((state.rideVelocity.get().length() < 0.1 && controller.pitch < 0.0) || state.currUpsideDownForce.get() != 0.0) {
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(Mth.wrapDegrees(desiredPitch.toFloat().angleDifference(controller.pitch)))) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate.toFloat() * arrivalInfluence
|
||||
var steeredPitchForce = (desiredPitchForce - state.currPitchCorrectionForce.get())
|
||||
if((state.rideVelocity.get().length() < 0.1) || state.currUpsideDownForce.get() != 0.0) {
|
||||
val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
|
||||
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredPitchForce) > maxPitchForce) {
|
||||
@@ -404,80 +408,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
state.noInputTimePitch.set(0.0)
|
||||
}
|
||||
|
||||
return when {
|
||||
Cobblemon.config.disableRoll -> noRollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
return rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
else -> rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun noRollRotation(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
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
|
||||
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalBird.handlingExpr!!)
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = ((mouseX).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
|
||||
val mouseYc = ((mouseY).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * handling //* ( 1.0 - min(sqrt(RidingBehaviour.scaleToRange(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() * abs(cos(controller.pitch.toRadians())).pow(2))
|
||||
|
||||
var pitchRot = yInput * handling
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
fun rollRotation(
|
||||
@@ -492,29 +434,31 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalBird.mouseInputMult!!)
|
||||
// Applied to mouseX to prevent turning upside down when trying to roll hard.
|
||||
val rollDegreeDebuff = if(mouseX.sign.toFloat() == controller.roll.sign) 1 - 0.9f*sqrt(abs(sin(controller.roll.toRadians())))*sqrt(abs(cos(controller.pitch))) else 1.0f
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 2.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult * rollDegreeDebuff
|
||||
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
|
||||
// of being upside down
|
||||
val rolledOverPitchCorrection = -8*sqrt(abs(min(cos(controller.roll.toRadians()),0.0f))) * (cos(controller.pitch.toRadians())).pow(2)
|
||||
val mouseYc = ((mouseY).coerceIn(-45.0, 45.0) * mouseInputMult) + rolledOverPitchCorrection
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed)
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed)
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
|
||||
val hoverSpeed = 0.1
|
||||
val pitchDamping = abs(cos(controller.pitch.toRadians())).pow(2) // Helper value used when wanting to discard the influence of another value at steep pitches
|
||||
|
||||
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
val localYaw = if (currHorzSpeed < hoverSpeed) xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
else 0.0 //xInput * 0.4 * cos(controller.roll.toRadians()).coerceIn(0.0f, 1.0f) * yawDampen
|
||||
val globalYaw = xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed))).coerceIn(0.0, 1.0)
|
||||
controller.applyGlobalYaw(globalYaw.toFloat())
|
||||
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
@@ -529,14 +473,18 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val pitchInfluencedRollCorrection = 0.0
|
||||
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2).coerceIn(0.0f, 0.7f) //Coerce into how much roll you want to let in in the deadzone
|
||||
val inputInRolledDir = if (xInput.sign.toFloat() == controller.roll.sign) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
|
||||
val rollDampen = (1 - howRolledAmI * abs(cos(controller.pitch.toRadians())).pow(2) * inputInRolledDir)
|
||||
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2) //Coerce into how much roll you want to let in in the deadzone
|
||||
val isInputtingTowardsRoll = if ( xInput.sign == controller.roll.sign.toDouble()) 1.0 else 0.0
|
||||
val inputInRolledDir = if (isInputtingTowardsRoll == 1.0) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
|
||||
val rollDampen = (1 - howRolledAmI.coerceIn(0.0f, 0.7f) * pitchDamping * inputInRolledDir)
|
||||
val rollForce = if (currHorzSpeed > hoverSpeed) xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, hoverSpeed, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
|
||||
else 0.0
|
||||
|
||||
// When already rolled significantly part of the roll force should turn to local yaw force
|
||||
val localYawForce = rollForce * abs(sin(controller.roll.toRadians())).pow(2) * pitchDamping * isInputtingTowardsRoll
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(localYaw, localPitch, rollForce)
|
||||
return Vec3(localYawForce, localPitch, rollForce)
|
||||
}
|
||||
|
||||
|
||||
@@ -584,11 +532,11 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalBird.speedExpr!!) / 20.0
|
||||
val glideTopSpeed = topSpeed * vehicle.runtime.resolveDouble(settings.glideSpeedExpr ?: globalBird.glideSpeedExpr!!)
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
|
||||
// Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(currSpeed, topSpeed, glideTopSpeed)
|
||||
val speedOverTopSpeed = max( 0.0, currSpeed - topSpeed)
|
||||
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(speedOverTopSpeed, 0.0, 0.3)
|
||||
|
||||
// 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
|
||||
@@ -692,6 +640,22 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
var glideSpeedExpr: Expression? = null
|
||||
private set
|
||||
|
||||
// debuff to stamina drain (increases stamina drain?) depending on how horizontal you're looking (so just drain more when climbing in altitude)
|
||||
// So for the base 1.5: If looking straight up and holding forward you drain at 150 percent the rate you would if
|
||||
// you were horizontal.
|
||||
var stamDrainHorzMod: Expression? = null
|
||||
private set
|
||||
|
||||
// long ass variable name
|
||||
// Percent of stamina drain during glide (percent of normal drain rate) So base is 25 percent. (change to 0 if you want no drain or negative if you want regen)
|
||||
var stamDrainPercRateGlide: Expression? = null
|
||||
private set
|
||||
|
||||
// Percent of stamina drain as a factor of how hard you're diving. So if its zero then at full dive you drain no stam.
|
||||
// This stacks with `stamDrainPercRateGlide`
|
||||
// This is coerced between 1 and 0 or things would get weird.
|
||||
var stamDrainPerRateDive: Expression? = null
|
||||
private set
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
@@ -706,6 +670,9 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(stamDrainHorzMod)
|
||||
buffer.writeNullableExpression(stamDrainPercRateGlide)
|
||||
buffer.writeNullableExpression(stamDrainPerRateDive)
|
||||
buffer.writeNullableExpression(timeToRollCorrect)
|
||||
}
|
||||
|
||||
@@ -721,6 +688,9 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
stamDrainHorzMod = buffer.readNullableExpression()
|
||||
stamDrainPercRateGlide = buffer.readNullableExpression()
|
||||
stamDrainPerRateDive = buffer.readNullableExpression()
|
||||
timeToRollCorrect = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
|
||||
val poseProvider = PoseProvider<HoverSettings, HoverState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
return@PoseOption abs(state.rideVelocity.get().z) > 0.2
|
||||
return@PoseOption abs(state.rideVelocity.get().length()) > 0.01
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
|
||||
+7
-90
@@ -161,7 +161,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
val controller = (vehicle as? OrientationControllable)?.orientationController
|
||||
if (controller == null || controller.orientation == null) return Vec3.ZERO
|
||||
|
||||
//Calculate ride space velocity
|
||||
@@ -265,8 +265,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//don't yaw this way if
|
||||
if (Cobblemon.config.disableRoll) return Vec3.ZERO
|
||||
|
||||
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
@@ -298,8 +296,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return when {
|
||||
Cobblemon.config.disableRoll -> noRollRotation(
|
||||
return rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
@@ -310,78 +307,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
else -> rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun noRollRotation(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
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
|
||||
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
|
||||
// Accumulate the mouse input
|
||||
state.currMouseXForce.set((state.currMouseXForce.get() + (0.0015 * mouseX)).coerceIn(-1.0, 1.0))
|
||||
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY)).coerceIn(-1.0, 1.0))
|
||||
|
||||
//Get handling in degrees per second
|
||||
val handlingDebuff = if(state.stamina.get() == 0.0f) 0.5 else 1.0
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalJet.handlingExpr!!) * handlingDebuff
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
|
||||
var pitchRot = handling * state.currMouseYForce.get()
|
||||
|
||||
// Yaw
|
||||
val yawRot = handling * 0.5 * state.currMouseXForce.get()
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
}
|
||||
|
||||
fun rollRotation(
|
||||
@@ -396,9 +322,8 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
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
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
|
||||
// Accumulate the mouse input
|
||||
@@ -412,14 +337,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
|
||||
|
||||
val poke = driver.vehicle as? PokemonEntity
|
||||
|
||||
//TODO: reevaluate if deadzones are needed and if they are still causing issues.
|
||||
//create deadzones for the constant input values.
|
||||
//val xInput = remapWithDeadzone(state.currMouseXForce, 0.025, 1.0)
|
||||
//val yInput = remapWithDeadzone(state.currMouseYForce, 0.025, 1.0)
|
||||
|
||||
val pitchRot = handling * state.currMouseYForce.get()
|
||||
|
||||
// Roll
|
||||
@@ -430,10 +347,10 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
// 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))
|
||||
state.currMouseXForce.set(lerp(state.currMouseXForce.get(), 0.0, 0.08))
|
||||
}
|
||||
if (mouseY == 0.0) {
|
||||
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.005))
|
||||
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.02))
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
|
||||
+18
-13
@@ -103,12 +103,12 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
|
||||
if (!state.isVehicleSprinting.get()) {
|
||||
if (state.staminaBuffer.get() >= 20) {
|
||||
val staminaIncrease = vehicle.runtime.resolveDouble(settings.staminaIncreaseExpr ?: globalBoat.staminaIncreaseExpr!!)
|
||||
state.stamina.set(min(1.0f, state.stamina.get() + 0.01f * staminaIncrease.toFloat()))
|
||||
val staminaIncrease = (1.0f / vehicle.runtime.resolveDouble(settings.stamReplenishTimeSeconds ?: globalBoat.stamReplenishTimeSeconds!!) ).toFloat() / 20.0f
|
||||
state.stamina.set(min(1.0f, state.stamina.get() + staminaIncrease))
|
||||
}
|
||||
}
|
||||
else if (abs(state.rideVelocity.get().z) > 0.1 && state.isVehicleSprinting.get()) {
|
||||
consumeStamina(vehicle, driver, settings, state, 0.01f)
|
||||
consumeStamina(vehicle, driver, settings, state)
|
||||
}
|
||||
state.staminaBuffer.set(state.staminaBuffer.get() + 1)
|
||||
super.tick(settings, state, vehicle, driver, input)
|
||||
@@ -219,12 +219,12 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
return state.stamina.get() != 0f
|
||||
}
|
||||
|
||||
private fun consumeStamina(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState, drain: Float) {
|
||||
val staminaModifier = vehicle.runtime.resolveFloat(settings.staminaDecreaseExpr ?: globalBoat.staminaDecreaseExpr!!)
|
||||
if (staminaModifier == 0F) {
|
||||
private fun consumeStamina(vehicle: PokemonEntity, driver: Player, settings: BoatSettings, state: BoatState) {
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalBoat.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
state.stamina.set(max(0f, state.stamina.get() - drain / staminaModifier))
|
||||
val stamDrainRate = (1.0f / vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalBoat.staminaExpr!!)).toFloat() / 20.0f
|
||||
state.stamina.set(max(0f, state.stamina.get() - stamDrainRate))
|
||||
if (state.stamina.get() == 0f) {
|
||||
state.isVehicleSprinting.set(false)
|
||||
driver.isSprinting = false
|
||||
@@ -404,16 +404,19 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var terminalVelocity: Expression? = null
|
||||
private set
|
||||
|
||||
var rotatePokemonHead: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaIncreaseExpr: Expression? = null
|
||||
var stamReplenishTimeSeconds: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaDecreaseExpr: Expression? = null
|
||||
var staminaExpr: Expression? = null
|
||||
private set
|
||||
|
||||
var rotationSpeedModifierExpr: Expression? = null
|
||||
@@ -438,10 +441,11 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(terminalVelocity)
|
||||
buffer.writeNullableExpression(rotatePokemonHead)
|
||||
buffer.writeNullableExpression(staminaIncreaseExpr)
|
||||
buffer.writeNullableExpression(staminaDecreaseExpr)
|
||||
buffer.writeNullableExpression(stamReplenishTimeSeconds)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
buffer.writeNullableExpression(rotationSpeedModifierExpr)
|
||||
buffer.writeNullableExpression(jumpStrengthExpr)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
@@ -454,10 +458,11 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
terminalVelocity = buffer.readNullableExpression()
|
||||
rotatePokemonHead = buffer.readNullableExpression()
|
||||
staminaIncreaseExpr = buffer.readNullableExpression()
|
||||
staminaDecreaseExpr = buffer.readNullableExpression()
|
||||
stamReplenishTimeSeconds = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
rotationSpeedModifierExpr = buffer.readNullableExpression()
|
||||
jumpStrengthExpr = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
|
||||
+160
-163
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.Side
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.ridingState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.air.angleDifference
|
||||
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
|
||||
@@ -47,6 +48,7 @@ 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
|
||||
|
||||
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
companion object {
|
||||
@@ -62,7 +64,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) { _, state, entity -> abs(state.rideVelocity.get().length()) > 0.05 })
|
||||
|
||||
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return vehicle.isInWater || !vehicle.onGround()
|
||||
@@ -151,37 +153,46 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
val orientation = controller.orientation ?: return Vec3.ZERO
|
||||
|
||||
val runtime = vehicle.runtime
|
||||
val boostMod = if (state.boosting.get()) runtime.resolveDouble(settings.jumpExpr ?: globalDolphin.jumpExpr!!) else 1.0
|
||||
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0) * boostMod
|
||||
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalDolphin.accelerationExpr!!) * 20.0)) * boostMod
|
||||
val strafeFactor = runtime.resolveDouble(settings.strafeFactor ?: globalDolphin.strafeFactor!!)
|
||||
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor ?: globalDolphin.reverseDriveFactor!!)
|
||||
var currVel = state.rideVelocity.get()
|
||||
|
||||
/*********************************************
|
||||
* Breaching logic
|
||||
*********************************************/
|
||||
if (!vehicle.isInWater && !vehicle.isUnderWater)
|
||||
{
|
||||
val currVel = state.lastVelocity.get()
|
||||
val breachBoost = 1.5
|
||||
var prevVel = state.lastVelocity.get()
|
||||
val breachBoost = 0.3
|
||||
state.clearMouseSmoothers.set(false)
|
||||
if (!state.hasBreached.get()) {
|
||||
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more
|
||||
// fun
|
||||
// Apply an upwards breaching force if boosting. This will enhance jumping out of water to feel more fun
|
||||
if (state.boosting.get()) {
|
||||
state.lastVelocity.set(Vec3(currVel.x, currVel.y + breachBoost*sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f), currVel.z))
|
||||
prevVel = Vec3(prevVel.x, prevVel.y + breachBoost*(sin(-controller.pitch.toRadians()).coerceIn(0.0f,0.5f)*2.0).pow(2), prevVel.z)
|
||||
}
|
||||
state.hasBreached.set(true)
|
||||
} else {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
|
||||
val terminalVel = 2.0
|
||||
state.lastVelocity.set(Vec3(currVel.x, max(-terminalVel, currVel.y - gravity), currVel.z)) //.yRot(vehicle.yRot.toRadians()))
|
||||
prevVel = Vec3(prevVel.x, max(-terminalVel, prevVel.y - gravity), prevVel.z)
|
||||
}
|
||||
return state.lastVelocity.get()
|
||||
|
||||
state.lastVelocity.set(prevVel)
|
||||
return state.lastVelocity.get().yRot(vehicle.yRot.toRadians())
|
||||
} else {
|
||||
state.lastVelocity.set(vehicle.deltaMovement.yRot(vehicle.yRot.toRadians()))
|
||||
if(state.hasBreached.get()) {
|
||||
state.clearMouseSmoothers.set(true)
|
||||
}
|
||||
state.hasBreached.set(false)
|
||||
currVel = Vec3(currVel.x, 0.0, currVel.z) // Reset y velocity to account for y vel persistance edgecase
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
@@ -190,32 +201,30 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
|
||||
// not actually friction
|
||||
val frictionLimit = 0.1
|
||||
val postColSpeed = min(max(vehicle.deltaMovement.length(), frictionLimit), state.speed.get())
|
||||
val postColSpeed = min(max(state.rideVelocity.get().length(), frictionLimit), state.speed.get())
|
||||
state.speed.set(postColSpeed)
|
||||
}
|
||||
|
||||
val currSpeed = state.speed.get()
|
||||
val strafeFactor = runtime.resolveDouble(settings.strafeFactor ?: globalDolphin.strafeFactor!!)
|
||||
val reverseDriveFactor = runtime.resolveDouble(settings.reverseDriveFactor ?: globalDolphin.reverseDriveFactor!!)
|
||||
val f = -driver.xxa * strafeFactor
|
||||
when {
|
||||
driver.zza > 0.0 -> state.speed.set(min(topSpeed, currSpeed + accel))
|
||||
driver.zza < 0.0 -> state.speed.set(max( -topSpeed * reverseDriveFactor, currSpeed - accel))
|
||||
else -> state.speed.set( lerp(currSpeed, 0.0, 0.05))
|
||||
val currZVel = state.rideVelocity.get().z
|
||||
val newZVel = when {
|
||||
driver.zza > 0.0 -> min(topSpeed, currZVel + accel)
|
||||
driver.zza < 0.0 -> max( -topSpeed * reverseDriveFactor, currZVel - accel)
|
||||
else -> lerp(currZVel, 0.0, 0.05)
|
||||
}
|
||||
val vertInput = when {
|
||||
driver.jumping && state.stamina.get() != 0.0f -> 1.0
|
||||
driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
} * strafeFactor
|
||||
state.rideVelocity.set(Vec3(currVel.x, currVel.y, newZVel))
|
||||
|
||||
var newVelocity = Vec3(f, vertInput,-state.speed.get())
|
||||
// Convert the local velocity vector into a world vector
|
||||
val localVelVec = Vector3f(
|
||||
state.rideVelocity.get().x.toFloat(),
|
||||
(state.rideVelocity.get().y).toFloat(),
|
||||
state.rideVelocity.get().z.toFloat() * -1.0f // Flip the z axis to make this left handed
|
||||
)
|
||||
|
||||
// Align to the ride
|
||||
newVelocity = controller.orientation?.transform(newVelocity.toVector3f())?.toVec3d() ?: Vec3.ZERO
|
||||
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
|
||||
val desiredVec = newVelocity.subtract(vehicle.deltaMovement)
|
||||
return vehicle.deltaMovement.add(desiredVec)
|
||||
val worldVelVec = localVelVec.mul(orientation).toVec3d()
|
||||
state.lastVelocity.set(worldVelVec) // Set last velocity to be used during breaching
|
||||
|
||||
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
|
||||
return worldVelVec.yRot(vehicle.yRot.toRadians())
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
@@ -225,9 +234,8 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (Cobblemon.config.disableRoll) return Vec3.ZERO
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
|
||||
@@ -235,7 +243,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
|
||||
|
||||
val yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())).pow(2) * sin(Math.toRadians(controller.roll.toDouble())).sign
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))) * sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble()))*2).coerceIn(-1.0, 1.0) * sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))
|
||||
|
||||
// Dampen yaw when upside down
|
||||
val yawDampen = (1 - abs(min(cos(controller.roll.toRadians()),0.0f)))
|
||||
@@ -256,45 +264,62 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
// Calculate correcting roll force.
|
||||
// Mult by cos(pitch) to ensure that it doesn't roll correct at when diving or climbing
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalDolphin.timeToRollCorrect!!)
|
||||
val maxRollCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0f
|
||||
else 10.0f * (cos(controller.pitch.toRadians())).pow(2) * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat()
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0
|
||||
val rollArrivalDeg = 90.0
|
||||
|
||||
if((state.speed.get() < 0.1 && driver.zza.toDouble() == 0.0) || state.noInputTime.get() > rollCorrectionTimer || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val arrivalInfluence = (min(rollArrivalDeg,abs(controller.roll).toDouble()) / rollArrivalDeg)
|
||||
val desiredRollForce = (0 - controller.roll).sign * maxRollCorrectionRate * arrivalInfluence
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
}
|
||||
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
// Stop roll and pitch correction when breached
|
||||
if (state.hasBreached.get()) {
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
)
|
||||
} else {
|
||||
state.currRollCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.96)
|
||||
state.currPitchCorrectionForce.set(
|
||||
lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = if (!(vehicle.isInWater || vehicle.isUnderWater)) 15.0 else 5.0f * ((state.noInputTime.get() - rollCorrectionTimer) * 2).coerceIn(0.0, 1.0).toFloat() * (1 - (currSpeed * 0.5).coerceIn(0.0, 1.0))
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0
|
||||
val pitchArrivalDeg = 45.0
|
||||
val targetPitch = if ((vehicle.isInWater || vehicle.isUnderWater)) 0.0f else (-asin(vehicle.deltaMovement.y / vehicle.deltaMovement.length()).toDegrees()).coerceIn(-45.0f, 45.0f)
|
||||
// Calculate correcting roll force.
|
||||
val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalDolphin.timeToRollCorrect!!)
|
||||
// If the ride is moving then have the correction rate be an equal proportion of:
|
||||
// - how rolled are you
|
||||
// - how long has it been since last mouse input (higher is stronger correction)
|
||||
// Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
|
||||
val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
|
||||
val maxRollCorrectionRate =
|
||||
if (state.rideVelocity.get().length() < 0.2) 10.0f * abs(cos(controller.pitch.toRadians()))
|
||||
else 10.0f * cos(controller.pitch.toRadians()).pow(2) * (
|
||||
howRolledAmI*1.0f +
|
||||
(state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
|
||||
)
|
||||
val maxRollForce = maxRollCorrectionRate / 40.0f
|
||||
val rollArrivalDeg = 60.0f
|
||||
val desiredRoll = 0.0f
|
||||
|
||||
if((state.noInputTime.get() > rollCorrectionTimer && driver.zza.toDouble() == 0.0) || !(vehicle.isInWater || vehicle.isUnderWater)) {
|
||||
val arrivalInfluence = min(rollArrivalDeg, abs(Mth.wrapDegrees(targetPitch - controller.pitch.toDouble()))) / pitchArrivalDeg
|
||||
val desiredPitchForce = (targetPitch - controller.pitch).sign * maxPitchCorrectionRate * arrivalInfluence
|
||||
// Calculate the signed error (how far and in what direction)
|
||||
val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
|
||||
|
||||
// Use the absolute error to calculate the magnitude of the influence
|
||||
val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
|
||||
val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
|
||||
var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
if (abs(steeredRollForce) > maxRollForce) {
|
||||
steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
}
|
||||
|
||||
// Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + steeredRollForce
|
||||
)
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
val maxPitchCorrectionRate = 5.0f * (state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
|
||||
val maxPitchForce = maxPitchCorrectionRate / 40.0f
|
||||
val pitchArrivalDeg = 45.0f
|
||||
val desiredPitch = 0.0f
|
||||
|
||||
if(state.rideVelocity.get().length() < 0.1 && state.noInputTime.get() > rollCorrectionTimer) {
|
||||
val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
|
||||
val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
|
||||
val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
|
||||
var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
|
||||
// Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
@@ -333,81 +358,18 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
} else {
|
||||
state.noInputTime.set(0.0)
|
||||
}
|
||||
return when {
|
||||
Cobblemon.config.disableRoll -> noRollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
return rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
else -> rollRotation(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun noRollRotation(
|
||||
settings: DolphinSettings,
|
||||
state: DolphinState,
|
||||
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
|
||||
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalDolphin.handlingExpr!!) * boostingHandleDebuff
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = ((mouseX).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
|
||||
val mouseYc = ((mouseY).coerceIn(-60.0, 60.0) / 60.0) * mouseInputMult
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * handling
|
||||
|
||||
//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() * abs(cos(controller.pitch.toRadians())).pow(2))
|
||||
|
||||
var pitchRot = yInput * handling
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
fun rollRotation(
|
||||
@@ -422,41 +384,71 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr ?: globalDolphin.speedExpr!!) / 20.0
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.2 else 1.0
|
||||
val boostingHandleDebuff = if (state.boosting.get()) 0.5 else 1.0
|
||||
val mouseInputMult = vehicle.runtime.resolveDouble(settings.mouseInputMult ?: globalDolphin.mouseInputMult!!) * boostingHandleDebuff
|
||||
// Applied to mouseX to prevent turning upside down when trying to roll hard.
|
||||
val rollDegreeDebuff = if(mouseX.sign.toFloat() == controller.roll.sign) 1 - 0.9f*sqrt(abs(sin(controller.roll.toRadians())))*sqrt(abs(cos(controller.pitch))) else 1.0f
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1, deltaTime * smoothingSpeed);
|
||||
val currSpeed = vehicle.deltaMovement.length()
|
||||
val smoothingSpeed = if(state.hasBreached.get()) 1.0f else 2.0f
|
||||
// When upside down apply a pitching force to nudge the player towards a dive. This reduces the 'weirdness'
|
||||
// of being upside down
|
||||
val rolledOverPitchCorrection = -8*sqrt(abs(min(cos(controller.roll.toRadians()),0.0f))) * (cos(controller.pitch.toRadians())).pow(2)
|
||||
val mouseXc = (mouseX).coerceIn(-45.0, 45.0) * mouseInputMult * rollDegreeDebuff
|
||||
val mouseYc = (mouseY).coerceIn(-45.0, 45.0) * mouseInputMult + rolledOverPitchCorrection
|
||||
val mouseModForce = if (state.hasBreached.get()) 0.15 else 0.1 // Used to effect more spin momentum during breaching
|
||||
|
||||
// Yaw locally
|
||||
// Dampen yaw when rolled or pitched substantially
|
||||
val yawDampen = abs(cos(controller.pitch.toRadians())).pow(2) * abs(cos(controller.roll.toRadians())).pow(2)
|
||||
val localYaw = xInput * (1 - (RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.4, 1.0) * yawDampen
|
||||
if (state.clearMouseSmoothers.get()) {
|
||||
mouseXSmoother.reset()
|
||||
mouseYSmoother.reset()
|
||||
state.clearMouseSmoothers.set(false)
|
||||
}
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * mouseModForce, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * mouseModForce, deltaTime * smoothingSpeed);
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
val currHorzSpeed = state.rideVelocity.get().horizontalDistance()
|
||||
val hoverSpeed = 0.2
|
||||
val pitchDamping = abs(cos(controller.pitch.toRadians())).pow(2) // Helper value used when wanting to discard the influence of another value at steep pitches
|
||||
|
||||
// Yaw locally when at or below hoverSpeed but not when pitched up or rolled over
|
||||
val globalYaw = xInput * (1 - sqrt(RidingBehaviour.scaleToRange(currSpeed, 0.0, hoverSpeed))).coerceIn(0.0, 1.0)
|
||||
controller.rotateYaw(pitchDamping*globalYaw.toFloat())
|
||||
controller.rotateRoll((1-pitchDamping)*globalYaw.toFloat())
|
||||
|
||||
// Pitch locally up or down depending upon a number of factors:
|
||||
// - Reduce pitch substantially when rolled and in a steep yaw. This prevents the player from ignoring a slow handling stat
|
||||
// - Do not pitch at all when at or below hover speed
|
||||
// - pitch faster at higher speeds
|
||||
val p = 1.0 - vehicle.runtime.resolveDouble(settings.horizontalPitchExpr ?: globalDolphin.horizontalPitchExpr!!)
|
||||
val localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
|
||||
// localPitch *= if (currSpeed > hoverSpeed) RidingBehaviour.scaleToRange(currSpeed, hoverSpeed, topSpeed)
|
||||
// else 0.0
|
||||
val localPitch = yInput * (1 - abs(sin(controller.roll.toRadians())).pow(2) * p * hypot(controller.forwardVector.x.toDouble(), controller.forwardVector.z.toDouble()).toFloat())
|
||||
|
||||
var localYawForce = 0.0
|
||||
var rollForce = 0.0
|
||||
|
||||
// If it has breached then turn the yaw into roll
|
||||
if (state.hasBreached.get()) {
|
||||
localYawForce = xInput
|
||||
} else {
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val pitchInfluencedRollCorrection = 0.0
|
||||
val howRolledAmI = (( cos(controller.roll.toRadians()) - 1).coerceIn(-1.0f, 0.0f) * -2) //Coerce into how much roll you want to let in in the deadzone
|
||||
val isInputtingTowardsRoll = if ( xInput.sign == controller.roll.sign.toDouble()) 1.0 else 0.0
|
||||
val inputInRolledDir = if (isInputtingTowardsRoll == 1.0) 1.0 else -cos(controller.roll.toRadians()).coerceIn(-1.0f, 0.0f).toDouble()
|
||||
val rollDampen = (1 - howRolledAmI.coerceIn(0.0f, 0.5f) * pitchDamping * inputInRolledDir)
|
||||
rollForce = xInput * sqrt(abs(RidingBehaviour.scaleToRange(currHorzSpeed, 0.0, topSpeed)).coerceIn(0.0,1.0)) * rollDampen + pitchInfluencedRollCorrection
|
||||
|
||||
// When already rolled significantly part of the roll force should turn to local yaw force
|
||||
localYawForce = rollForce * abs(sin(controller.roll.toRadians())).pow(2) * pitchDamping * isInputtingTowardsRoll
|
||||
}
|
||||
|
||||
// Roll less when slow and not at all when at hoverSpeed or lower.
|
||||
// Apply a roll righting force when trying to pitch hard while rolled sideways. This nudges the player towards
|
||||
// pitching globally
|
||||
val rollForce = xInput * ((RidingBehaviour.scaleToRange(currSpeed, 0.0, topSpeed))).coerceIn(0.2, 1.0) //+ pitchInfluencedRollCorrection
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(localYaw, localPitch, rollForce)
|
||||
return Vec3(localYawForce, localPitch, rollForce)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
@@ -603,6 +595,7 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
buffer.writeNullableExpression(canJump)
|
||||
buffer.writeNullableExpression(reverseDriveFactor)
|
||||
buffer.writeNullableExpression(strafeFactor)
|
||||
buffer.writeNullableExpression(timeToRollCorrect)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(horizontalPitchExpr)
|
||||
@@ -619,6 +612,7 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
canJump = buffer.readNullableExpression()
|
||||
reverseDriveFactor = buffer.readNullableExpression()
|
||||
strafeFactor = buffer.readNullableExpression()
|
||||
timeToRollCorrect = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
horizontalPitchExpr = buffer.readNullableExpression()
|
||||
@@ -638,6 +632,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
var boostIsToggleable = ridingState(false, Side.CLIENT)
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var hasBreached = ridingState(false, Side.CLIENT)
|
||||
var clearMouseSmoothers = ridingState(false, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
@@ -659,6 +654,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
boostIsToggleable.set(true, forced = true)
|
||||
speed.set(0.0, forced = true)
|
||||
hasBreached.set(false, forced = true)
|
||||
clearMouseSmoothers.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -676,6 +672,7 @@ class DolphinState : RidingBehaviourState() {
|
||||
it.boostIsToggleable.set(this.boosting.get(), forced = true)
|
||||
it.speed.set(this.speed.get(), forced = true)
|
||||
it.hasBreached.set(this.hasBreached.get(), forced = true)
|
||||
it.clearMouseSmoothers.set(this.clearMouseSmoothers.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
+643
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
* 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.liquid
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.readNullableExpression
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.cobblemon.mod.common.util.writeNullableExpression
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.tags.FluidTags
|
||||
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 SubmarineBehaviour : RidingBehaviour<SubmarineSettings, SubmarineState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("liquid/submarine")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
val globalSubmarine: SubmarineSettings
|
||||
get() = CobblemonRideSettings.submarine
|
||||
|
||||
override fun getRidingStyle(settings: SubmarineSettings, state: SubmarineState): RidingStyle {
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<SubmarineSettings, SubmarineState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, state, entity -> state.rideVelocity.get().z > 0.05 && !state.onSurface.get()})
|
||||
.with(PoseOption(PoseType.STAND) { _, state, entity -> state.rideVelocity.get().z <= 0.05 && state.onSurface.get()})
|
||||
.with(PoseOption(PoseType.STAND) { _, state, entity -> state.rideVelocity.get().z > 0.05 && state.onSurface.get()})
|
||||
|
||||
override fun isActive(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
|
||||
return vehicle.isInWater || !vehicle.onGround()
|
||||
}
|
||||
|
||||
override fun pose(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
return state.speed.get().toFloat()
|
||||
}
|
||||
|
||||
override fun tick(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
) {
|
||||
if(vehicle.level().isClientSide) {
|
||||
tickStamina(settings, state, vehicle)
|
||||
surfaceCheck(settings, state, vehicle, driver)
|
||||
}
|
||||
}
|
||||
|
||||
fun tickStamina(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity
|
||||
) {
|
||||
val stam = state.stamina.get()
|
||||
|
||||
if (vehicle.runtime.resolveBoolean(settings.infiniteStamina ?: globalSubmarine.infiniteStamina!!)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Grab the boost time in seconds and convert to ticks. Then calculate the drain rate as inversely
|
||||
// proportional to the number of ticks of boost thus making a full boost take x ticks
|
||||
// in short: "Stamina drains slower at higher values and also replenishes slower"
|
||||
val breathTime = vehicle.runtime.resolveDouble(settings.staminaExpr ?: globalSubmarine.staminaExpr!!) * 20.0f
|
||||
val stamRegenRate = 1.0f / (vehicle.runtime.resolveDouble(settings.staminaRegenTime ?: globalSubmarine.staminaRegenTime!!).toFloat() * 20.0f)
|
||||
val stamDrainRate = (1.0f / breathTime).toFloat()
|
||||
val driver = vehicle.controllingPassenger
|
||||
val newStam = if (driver?.isEyeInFluid(FluidTags.WATER) ?: false) max(0.0f,stam - stamDrainRate)
|
||||
else min(1.0f,stam + stamRegenRate)
|
||||
|
||||
state.stamina.set(newStam)
|
||||
}
|
||||
|
||||
private fun surfaceCheck(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
) {
|
||||
// determine if the vehicle is in water.
|
||||
// if it isn't then set surface flag to false and return
|
||||
if (!vehicle.isInWater) {
|
||||
state.onSurface.set(false)
|
||||
return
|
||||
}
|
||||
|
||||
// if it is then check half the bbheight above and below the center point.
|
||||
|
||||
// If the point above is air and the point below is water then you know to trend towards on surface.
|
||||
|
||||
// Grab the center position of the vehicle (vehicle.position.add(bbheight/2)
|
||||
val vehicleCenter = vehicle.position().add(0.0, vehicle.bbHeight/2.0, 0.0)
|
||||
|
||||
// If the block above the center is not fluid but this one is then move the center
|
||||
val centerBlockPos = BlockPos.containing(vehicleCenter)
|
||||
val centerInWater = !vehicle.level().getFluidState(centerBlockPos).isEmpty
|
||||
|
||||
val abovePos = centerBlockPos.above()
|
||||
val aboveInWater = !vehicle.level().getFluidState(abovePos).isEmpty
|
||||
|
||||
if (!centerInWater && !aboveInWater) {
|
||||
state.onSurface.set(true)
|
||||
} else {
|
||||
state.onSurface.set(false)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// Function for if x mouse and y mouse "get through" to affecting the mounts rotation. Return a Pair<Boolean, Boolean>
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
val orientation = controller.orientation ?: return Vec3.ZERO
|
||||
|
||||
val runtime = vehicle.runtime
|
||||
val topSpeed = (vehicle.runtime.resolveDouble(settings.speedExpr ?: globalSubmarine.speedExpr!!) / 20.0)
|
||||
val accel = (topSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalSubmarine.accelerationExpr!!) * 20.0))
|
||||
val strafeSpeed = (runtime.resolveDouble(settings.jumpExpr ?: globalSubmarine.jumpExpr!!) / 20.0)
|
||||
val strafeAccel = (strafeSpeed / (vehicle.runtime.resolveDouble(settings.accelerationExpr ?: globalSubmarine.accelerationExpr!!) * 20.0))
|
||||
var currVel = state.rideVelocity.get()
|
||||
|
||||
/*********************************************
|
||||
* Handle collisions
|
||||
*********************************************/
|
||||
if (vehicle.horizontalCollision || vehicle.verticalCollision) {
|
||||
// not actually friction
|
||||
val frictionLimit = 0.1
|
||||
val postColSpeed = min(max(state.rideVelocity.get().length(), frictionLimit), state.speed.get())
|
||||
state.speed.set(postColSpeed)
|
||||
}
|
||||
if (vehicle.verticalCollision) {
|
||||
state.rideVelocity.set(Vec3(state.rideVelocity.get().x, 0.0, state.rideVelocity.get().z))
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* Calculate Forward Velocity
|
||||
*********************************************/
|
||||
val currZVel = state.rideVelocity.get().z
|
||||
val newZVel = when {
|
||||
driver.zza > 0.0 -> min(topSpeed, currZVel + accel)
|
||||
driver.zza < 0.0 -> max( -strafeSpeed, currZVel - strafeAccel) //TODO: Might need to be applied in addition to the normal braking force?
|
||||
else -> lerp(currZVel, 0.0, 0.05)
|
||||
}
|
||||
currVel = Vec3(currVel.x, currVel.y, newZVel)
|
||||
|
||||
/*********************************************
|
||||
* Calculate up/down Velocity and gravity
|
||||
*********************************************/
|
||||
val vertTopSpeed = strafeSpeed
|
||||
val currYVel = state.rideVelocity.get().y
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.4
|
||||
val terminalYVel = 2.0
|
||||
val driverYInput = if(driver.jumping) 1.0 else if (driver.isShiftKeyDown) -1.0 else 0.0
|
||||
val newYVel = when {
|
||||
(!vehicle.isInWater) -> max(-terminalYVel, currYVel - gravity)
|
||||
//(vehicle.isInWater && !vehicle.isUnderWater && currYVel > 0.001) -> lerp(currYVel, 0.0, 0.1) // Make sure that yVel is above a threshold before doing this otherwise it'll block submerging.
|
||||
//driverYInput != 0.0 && currYVel > 0.001 && driverYInput.sign != currYVel.sign -> lerp(currYVel, 0.0, 0.05)
|
||||
driverYInput > 0.0 -> min(vertTopSpeed, currYVel + strafeAccel)
|
||||
driverYInput < 0.0 -> max(-vertTopSpeed, currYVel - strafeAccel)
|
||||
else -> lerp(currYVel, 0.0, 0.05)
|
||||
}
|
||||
currVel = Vec3(currVel.x, newYVel, currVel.z)
|
||||
|
||||
/*********************************************
|
||||
* Calculate left/right strafing Velocity
|
||||
*********************************************/
|
||||
val currXVel = state.rideVelocity.get().x
|
||||
val horzTopSpeed = strafeSpeed
|
||||
val newXVel = when {
|
||||
driver.xxa > 0.0 -> min(horzTopSpeed, currXVel + strafeAccel)
|
||||
driver.xxa < 0.0 -> max( -horzTopSpeed, currXVel - strafeAccel)
|
||||
else -> lerp(currXVel, 0.0, 0.01)
|
||||
}
|
||||
currVel = Vec3(newXVel, currVel.y, currVel.z)
|
||||
|
||||
// Set the new velocity
|
||||
state.rideVelocity.set(currVel)
|
||||
|
||||
/*********************************************
|
||||
* Separate and apply velocities
|
||||
*********************************************/
|
||||
// If the driver and vehicles eyes are out of the water but the vehicle is still in it then move only horizontally or downwards as you have surfaced
|
||||
if (state.onSurface.get()) {
|
||||
val worldVelVec = Vec3(currVel.x, min(0.0, currVel.y), currVel.z)
|
||||
return worldVelVec
|
||||
} else {
|
||||
val vertVec = Vec3(0.0, currVel.y, 0.0)
|
||||
val horzVec = Vec3(currVel.x, 0.0, 0.0)
|
||||
val localZVelVec = Vector3f(0.0f, 0.0f, currVel.z.toFloat() * -1.0f) // Flip the z axis to make this left handed
|
||||
// Unrotate preemptively as this vector gets rotated later down the line in MC logic.
|
||||
val worldVelVec = localZVelVec.mul(orientation).toVec3d().yRot(vehicle.yRot.toRadians())
|
||||
// Add back the horizontal and vertical components.
|
||||
return worldVelVec.add(vertVec).add(horzVec)
|
||||
}
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun applyRenderRotation(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
partialTicks: Float
|
||||
) {
|
||||
if (vehicle !is OrientationControllable) return
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalSubmarine.handlingExpr!!)
|
||||
val surfaceHandlingBoost = if(state.onSurface.get()) 3.0 else 1.0
|
||||
handling *= surfaceHandlingBoost
|
||||
|
||||
val strafeSpeed = (vehicle.runtime.resolveDouble(settings.jumpExpr ?: globalSubmarine.jumpExpr!!) / 20.0)
|
||||
|
||||
// Grab the roll and see if its hit the limit yet. If it hasn't then trend it towards its desired roll
|
||||
val maxRollDeg = if (state.onSurface.get()) 5 else 15
|
||||
val maxPitchDeg = 15
|
||||
var rollAmount = 0.0
|
||||
var pitchAmount = 0.0
|
||||
|
||||
// Do horizontal speed based rolling
|
||||
val maxHorzSpeed = strafeSpeed
|
||||
val currHorzSpeed = state.rideVelocity.get().x
|
||||
var desiredRoll = -(currHorzSpeed / maxHorzSpeed).coerceIn(-1.0, 1.0) * maxRollDeg
|
||||
rollAmount += lerp(controller.roll.toDouble(), desiredRoll, partialTicks.toDouble()) - controller.roll
|
||||
//val rotAmount //Maybe use some lerp?
|
||||
controller.rotateRoll(rollAmount.toFloat())
|
||||
|
||||
// Do yawRate based rolling
|
||||
val maxYawRate = handling
|
||||
val currYawRate = vehicle.ridingAnimationData.rotDeltaSpring.getInterpolated(partialTicks.toDouble(), 2).y()
|
||||
desiredRoll = -(currYawRate / maxYawRate).coerceIn(-1.0, 1.0) * maxRollDeg
|
||||
rollAmount += desiredRoll - controller.roll
|
||||
controller.rotateRoll(rollAmount.toFloat())
|
||||
|
||||
//TODO: why does this affect the actual controller but rolling doesn't??
|
||||
// Do upwards movement based pitching
|
||||
// val maxVertSpeed = 0.25 //TODO: CONNECT UP TO JUMP
|
||||
// val currVertSpeed = state.rideVelocity.get().y
|
||||
// var desiredPitch = -(currVertSpeed / maxVertSpeed) * maxPitchDeg
|
||||
// pitchAmount += lerp(controller.pitch.toDouble(), desiredPitch, partialTicks.toDouble()) - controller.pitch
|
||||
// controller.rotatePitch(pitchAmount.toFloat())
|
||||
|
||||
}
|
||||
|
||||
private fun correctOrientation(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
controller: OrientationController,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
) {
|
||||
|
||||
// TODO: remove if not needed
|
||||
|
||||
// // Calculate correcting roll force.
|
||||
// // If the ride is moving then have the correction rate be an equal proportion of:
|
||||
// // - how rolled are you
|
||||
// // - how long has it been since last mouse input (higher is stronger correction)
|
||||
// // Also the correction is proportional as a whole to how NOT pitched are you so you don't spin endlessly when looking up or down
|
||||
// val howRolledAmI = sqrt(abs((cos(controller.roll.toRadians()) - 1.0f) * 0.5f)) // sqrt so it trends towards one pretty quickly
|
||||
// val maxRollCorrectionRate =
|
||||
// if (state.rideVelocity.get().length() < 0.2) 10.0f * abs(cos(controller.pitch.toRadians()))
|
||||
// else 10.0f * cos(controller.pitch.toRadians()).pow(2) * (
|
||||
// howRolledAmI*1.0f +
|
||||
// (state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
|
||||
// )
|
||||
// val maxRollForce = maxRollCorrectionRate / 40.0f
|
||||
// val rollArrivalDeg = 60.0f
|
||||
// val desiredRoll = 0.0f
|
||||
//
|
||||
// // Calculate the signed error (how far and in what direction)
|
||||
// val rollError = Mth.wrapDegrees(desiredRoll.angleDifference(controller.roll))
|
||||
//
|
||||
// // Use the absolute error to calculate the magnitude of the influence
|
||||
// val influenceMagnitude = (min(rollArrivalDeg, abs(rollError)) / rollArrivalDeg)
|
||||
// val desiredRollForce = maxRollCorrectionRate * influenceMagnitude * -rollError.sign
|
||||
// var steeredRollForce = desiredRollForce - state.currRollCorrectionForce.get()
|
||||
//
|
||||
// // Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
// if (abs(steeredRollForce) > maxRollForce) {
|
||||
// steeredRollForce = steeredRollForce.sign * maxRollForce
|
||||
// }
|
||||
//
|
||||
// // Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
// state.currRollCorrectionForce.set(
|
||||
// state.currRollCorrectionForce.get() + steeredRollForce
|
||||
// )
|
||||
|
||||
//Calculate correcting pitch force.
|
||||
// val rollCorrectionTimer = vehicle.runtime.resolveDouble(settings.timeToRollCorrect ?: globalSubmarine.timeToRollCorrect!!)
|
||||
// val maxPitchCorrectionRate = 5.0f * (state.noInputTime.get() - rollCorrectionTimer).coerceIn(0.0, 1.0).pow(2).toFloat()*0.5f
|
||||
// val maxPitchForce = maxPitchCorrectionRate / 40.0f
|
||||
// val pitchArrivalDeg = 45.0f
|
||||
// val desiredPitch = 0.0f
|
||||
//
|
||||
// if(state.rideVelocity.get().length() < 0.1 && state.noInputTime.get() > rollCorrectionTimer) {
|
||||
// val pitchErrror = Mth.wrapDegrees(desiredPitch.angleDifference(controller.pitch))
|
||||
// val arrivalInfluence = (min(pitchArrivalDeg,abs(pitchErrror)) / pitchArrivalDeg)
|
||||
// val desiredPitchForce = maxPitchCorrectionRate * arrivalInfluence * -pitchErrror.sign
|
||||
// var steeredPitchForce = desiredPitchForce - state.currPitchCorrectionForce.get()
|
||||
//
|
||||
// // Ensure the 'steering' correction force doesn't exceed our maxForce
|
||||
// if (abs(steeredPitchForce) > maxPitchForce) {
|
||||
// steeredPitchForce = steeredPitchForce.sign * maxPitchForce
|
||||
// }
|
||||
//
|
||||
// // Apply the 'steered' force (its really not a steering force I need a better name)
|
||||
// state.currPitchCorrectionForce.set(
|
||||
// state.currPitchCorrectionForce.get() + steeredPitchForce
|
||||
// )
|
||||
// } else {
|
||||
// state.currPitchCorrectionForce.set(
|
||||
// //reduce rollCorrection gradually
|
||||
// lerp(state.currPitchCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (vehicle !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (vehicle as OrientationControllable).orientationController
|
||||
|
||||
// Set roll to zero if transitioning to noroll config
|
||||
controller.rotateRoll(controller.roll * -1.0f)
|
||||
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr ?: globalSubmarine.handlingExpr!!)
|
||||
val surfaceHandlingBoost = if(state.onSurface.get()) 3.0 else 1.0
|
||||
val effHandling = if(state.onSurface.get()) 1000 * deltaTime else handling * deltaTime
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = if(state.onSurface.get()) 10.0 else 2.0
|
||||
val mouseXc = (mouseX * 0.1).coerceIn(-effHandling, effHandling)
|
||||
val mouseYc = (mouseY * 0.1).coerceIn(-effHandling, effHandling)
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc, deltaTime * smoothingSpeed);
|
||||
|
||||
// Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput
|
||||
|
||||
// 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
|
||||
val pitchLimit = 45.0
|
||||
if (abs(controller.pitch + pitchRot) >= pitchLimit ) {
|
||||
pitchRot = 0.0
|
||||
mouseYSmoother.reset()
|
||||
} else {
|
||||
controller.applyGlobalPitch(pitchRot.toFloat())
|
||||
}
|
||||
|
||||
// If on the surface then conditionally bring the pitch to 0 degrees.
|
||||
if (state.onSurface.get()) {
|
||||
//move back at max 30 degrees per second.
|
||||
val maxCorrectionSpeed = 30.0
|
||||
val arrivalDegreeWidth = 30.0
|
||||
val currentPitch = controller.pitch
|
||||
val correctionDegrees = (-currentPitch / arrivalDegreeWidth).coerceIn(-1.0, 1.0) * maxCorrectionSpeed * deltaTime
|
||||
|
||||
controller.applyGlobalPitch(correctionDegrees.toFloat())
|
||||
|
||||
// Compensate player ride rotations
|
||||
// val playerRotater = driver as RideRotation
|
||||
// playerRotater.`cobblemon$setRideXRot`(playerRotater.`cobblemon$getRideXRot`() - correctionDegrees.toFloat() )
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Double {
|
||||
return if (!vehicle.isUnderWater) 1.0 else 0.1
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun mouseModifiesDriverRotation(settings: SubmarineSettings, state: SubmarineState, vehicle: PokemonEntity): Pair<Boolean, Boolean> {
|
||||
return Pair(false, state.onSurface.get())
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: SubmarineSettings,
|
||||
state: SubmarineState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: SubmarineSettings) = SubmarineState()
|
||||
|
||||
}
|
||||
|
||||
class SubmarineSettings : RidingBehaviourSettings {
|
||||
override val key = SubmarineBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteStamina: Expression? = null
|
||||
private set
|
||||
|
||||
var staminaRegenTime: Expression? = null
|
||||
private set
|
||||
|
||||
// Tope strafe speed in Bl/s
|
||||
var jumpExpr: Expression? = null
|
||||
// Yaw rate in degrees per second
|
||||
var handlingExpr: Expression? = null
|
||||
// Top speed in Bl/s
|
||||
var speedExpr: Expression? = null
|
||||
// Seconds to get to top speed
|
||||
var accelerationExpr: Expression? = null
|
||||
// Seconds of air provided by mount
|
||||
var staminaExpr: Expression? = null
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeNullableExpression(infiniteStamina)
|
||||
buffer.writeNullableExpression(staminaRegenTime)
|
||||
buffer.writeNullableExpression(jumpExpr)
|
||||
buffer.writeNullableExpression(handlingExpr)
|
||||
buffer.writeNullableExpression(speedExpr)
|
||||
buffer.writeNullableExpression(accelerationExpr)
|
||||
buffer.writeNullableExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteStamina = buffer.readNullableExpression()
|
||||
staminaRegenTime = buffer.readNullableExpression()
|
||||
jumpExpr = buffer.readNullableExpression()
|
||||
handlingExpr = buffer.readNullableExpression()
|
||||
speedExpr = buffer.readNullableExpression()
|
||||
accelerationExpr = buffer.readNullableExpression()
|
||||
staminaExpr = buffer.readNullableExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class SubmarineState : RidingBehaviourState() {
|
||||
var speed = ridingState(0.0, Side.CLIENT)
|
||||
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var currPitchCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
var noInputTime = ridingState(0.0, Side.CLIENT)
|
||||
var onSurface = ridingState(false, Side.BOTH)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(onSurface.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
onSurface.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
speed.set(0.0, forced = true)
|
||||
currRollCorrectionForce.set(0.0, forced = true)
|
||||
currPitchCorrectionForce.set(0.0, forced = true)
|
||||
noInputTime.set(0.0, forced = true)
|
||||
onSurface.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = SubmarineState().also {
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.speed.set(speed.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
|
||||
it.currPitchCorrectionForce.set(this.currPitchCorrectionForce.get(), forced = true)
|
||||
it.noInputTime.set(this.noInputTime.get(), forced = true)
|
||||
it.onSurface.set(this.onSurface.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is SubmarineState) return false
|
||||
if (previous.onSurface.get() != onSurface.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -74,7 +74,7 @@ class RidingAnimationData(val ride: PokemonEntity)
|
||||
}
|
||||
if( r ) {
|
||||
//TODO: should this return?
|
||||
val controller = (ride.firstPassenger as? OrientationControllable)?.orientationController ?: return
|
||||
val controller = (ride as? OrientationControllable)?.orientationController ?: return
|
||||
val orientation = controller.orientation ?: Matrix3f()
|
||||
|
||||
val currPitch = controller.pitch
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.dispenser.DispenseItemBehavior
|
||||
@@ -25,6 +27,7 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Block
|
||||
@@ -40,6 +43,9 @@ import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.level.pathfinder.PathComputationType
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.EntityCollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
|
||||
@@ -90,7 +96,13 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!added) Containers.dropItemStack(level, source.pos.x.toDouble(), source.pos.y.toDouble(), source.pos.z.toDouble(), outputStack)
|
||||
if (!added) Containers.dropItemStack(
|
||||
level,
|
||||
source.pos.x.toDouble(),
|
||||
source.pos.y.toDouble(),
|
||||
source.pos.z.toDouble(),
|
||||
outputStack
|
||||
)
|
||||
}
|
||||
}
|
||||
stack
|
||||
@@ -108,7 +120,27 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
|
||||
)
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = !state.getValue(PERSISTENT) && state.getValue(DISTANCE) > 6
|
||||
override fun getCollisionShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape? {
|
||||
|
||||
if (context is EntityCollisionContext) {
|
||||
val entity = context.entity
|
||||
if (entity != null
|
||||
&& entity is MoLangScriptingEntity
|
||||
&& entity.config.map.getOrDefault("can_path_through_sacc_leaves", DoubleValue.ZERO).asDouble() == 1.0
|
||||
) {
|
||||
return Shapes.empty()
|
||||
}
|
||||
}
|
||||
return super.getCollisionShape(state, level, pos, context)
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean =
|
||||
true //!state.getValue(PERSISTENT) && state.getValue(DISTANCE) > 6
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerLevel, pos: BlockPos, random: RandomSource) {
|
||||
val currentAge = state.getValue(AGE)
|
||||
@@ -123,8 +155,8 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
|
||||
} else if (belowState.block is SaccharineLeafBlock) {
|
||||
val belowAge = belowState.getValue(AGE)
|
||||
if (belowAge < MAX_AGE) {
|
||||
changeAge(state, -1)
|
||||
changeAge(belowState, 1)
|
||||
world.setBlock(pos, changeAge(state, -1), UPDATE_CLIENTS)
|
||||
world.setBlock(belowPos, changeAge(belowState, 1), UPDATE_CLIENTS)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
@@ -217,7 +249,15 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
|
||||
|
||||
override fun isPathfindable(state: BlockState, type: PathComputationType): Boolean = false
|
||||
|
||||
override fun useItemOn(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hand: InteractionHand, hit: BlockHitResult): ItemInteractionResult {
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hit: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
if (!state.getValue(WATERLOGGED)) {
|
||||
val itemStack = player.getItemInHand(hand)
|
||||
val isGlassBottle = itemStack.`is`(Items.GLASS_BOTTLE)
|
||||
|
||||
+1
@@ -45,6 +45,7 @@ class CobblemonConfigScreen(
|
||||
|
||||
val clonedConfig = Cobblemon.config.clone()
|
||||
val defaultConfig = CobblemonConfig()
|
||||
val hideServerConfigs = Minecraft.getInstance().currentServer != null && !Minecraft.getInstance().isLocalServer
|
||||
|
||||
private lateinit var layout: HeaderAndFooterLayout
|
||||
private lateinit var searchEdit: EditBox
|
||||
|
||||
+7
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.config.Category
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.config.CobblemonConfigField
|
||||
import com.cobblemon.mod.common.config.CobblemonConfigField.CobblemonConfigSide.SERVER
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import net.minecraft.SharedConstants
|
||||
import net.minecraft.client.Minecraft
|
||||
@@ -32,6 +33,7 @@ import net.minecraft.client.gui.narration.NarratableEntry
|
||||
import kotlin.math.min
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
@@ -62,6 +64,11 @@ class CobblemonConfigVariableList(
|
||||
return@map null
|
||||
}
|
||||
|
||||
val fieldAnnotation = property.javaField!!.getAnnotation(CobblemonConfigField::class.java)
|
||||
if (parent.hideServerConfigs && fieldAnnotation.side == SERVER) {
|
||||
return@map null
|
||||
}
|
||||
|
||||
val entry: CobblemonConfigVariable? =
|
||||
when (val classifier = property.returnType.classifier) {
|
||||
String::class -> CobblemonConfigStringVariable(this, property as KMutableProperty1<out CobblemonConfig, String>)
|
||||
|
||||
+26
-4
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.gui.debug.riding
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourSettings
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
@@ -89,7 +90,7 @@ class RideSettingsEditorGUI(val parentScreen: Screen, val vehicle: PokemonEntity
|
||||
for (variable in expressionVariables) {
|
||||
val (name, value) = variable
|
||||
|
||||
val editBox = EditBox(this.minecraft!!.font, 110, currentY, 300, 20, name.text())
|
||||
val editBox = EditBox(this.minecraft!!.font, 130, currentY, 300, 20, name.text())
|
||||
editBox.setMaxLength(256)
|
||||
editBox.value = value
|
||||
|
||||
@@ -158,6 +159,10 @@ class RideSettingsEditorGUI(val parentScreen: Screen, val vehicle: PokemonEntity
|
||||
val clazz = settings.javaClass
|
||||
val fields = clazz.declaredFields
|
||||
|
||||
val gsField = CobblemonRideSettings.javaClass.declaredFields.firstOrNull { it.type == clazz }
|
||||
gsField?.isAccessible = true
|
||||
val controllerGlobalSettings = gsField?.get(CobblemonRideSettings) as? RidingBehaviourSettings?
|
||||
|
||||
// Iterate through all expression fields and return the list of those that
|
||||
// aer expressions
|
||||
return fields.mapNotNull { field ->
|
||||
@@ -165,10 +170,18 @@ class RideSettingsEditorGUI(val parentScreen: Screen, val vehicle: PokemonEntity
|
||||
field.isAccessible = true // Make private fields accessible
|
||||
val name = field.name
|
||||
// Get the value, cast it, and call .getString() on it
|
||||
val value = (field.get(settings) as Expression).getString()
|
||||
if (field.get(settings) != null) {
|
||||
val value = (field.get(settings) as Expression).getString()
|
||||
|
||||
// Create the pair and return it for the list
|
||||
name to value
|
||||
// Create the pair and return it for the list
|
||||
name to value
|
||||
|
||||
} else {
|
||||
// If the settings field is null then check for a global setting
|
||||
val value = getMatchingGlobalSettingsValue(settings, controllerGlobalSettings, name) ?: "novalue"
|
||||
|
||||
name to value
|
||||
}
|
||||
} else {
|
||||
// If it's not an Expression (they all honestly should be if being used by the
|
||||
// behaviour in question) then ignore
|
||||
@@ -176,4 +189,13 @@ class RideSettingsEditorGUI(val parentScreen: Screen, val vehicle: PokemonEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMatchingGlobalSettingsValue(settings: RidingBehaviourSettings, globalSettings: RidingBehaviourSettings?, fieldName: String): String? {
|
||||
if (globalSettings == null) return null
|
||||
val gssField = globalSettings.javaClass.declaredFields.firstOrNull {
|
||||
it.name == fieldName
|
||||
}
|
||||
gssField?.isAccessible = true
|
||||
return (gssField?.get(globalSettings) as? Expression)?.getString()
|
||||
}
|
||||
}
|
||||
+27
-21
@@ -25,6 +25,8 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FriendshipFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.features.FullnessFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.pokemon.EVs
|
||||
import com.cobblemon.mod.common.pokemon.IVs
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
@@ -74,6 +76,7 @@ class StatWidget(
|
||||
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
|
||||
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
|
||||
private val statsChartPentagonResource = cobblemonResource("textures/gui/summary/summary_stats_chart_pentagon.png")
|
||||
private val statsChartPentagonHighlightResource = cobblemonResource("textures/gui/summary/summary_stats_chart_pentagon_highlight.png")
|
||||
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
|
||||
private val statsOtherSidebarsResource = cobblemonResource("textures/gui/summary/summary_stats_other_sidebars.png")
|
||||
private val statsOtherSidebarArrowLeft = cobblemonResource("textures/gui/summary/summary_stats_other_sidebar_arrow_left.png")
|
||||
@@ -290,33 +293,34 @@ class StatWidget(
|
||||
}
|
||||
} else {
|
||||
if (renderPentagonStats) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsChartPentagonResource,
|
||||
x= (x + 20.5) / SCALE,
|
||||
y = (y + 22) / SCALE,
|
||||
width = 186,
|
||||
height = 176,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
pokemon.form.riding.behaviours?.let {
|
||||
val behaviours = it.entries.toList()
|
||||
val selectedBehaviour = behaviours[rideBehaviourIndex]
|
||||
val canSwitchStyle = behaviours.size > 1 && centerHovered(pMouseX, pMouseY)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (canSwitchStyle) statsChartPentagonHighlightResource else statsChartPentagonResource,
|
||||
x= (x + 20.5) / SCALE,
|
||||
y = (y + 22) / SCALE,
|
||||
width = 186,
|
||||
height = 176,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
val pentagonColour = when (selectedBehaviour.key) {
|
||||
RidingStyle.AIR -> {
|
||||
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
|
||||
if (canSwitchStyle)
|
||||
Vector3f(70F/255F, 235F/225F, 195F/255F)
|
||||
else Vector3f(40F/255F, 205F/255F, 165F/255F)
|
||||
}
|
||||
RidingStyle.LIQUID -> {
|
||||
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
|
||||
if (canSwitchStyle)
|
||||
Vector3f(95F/255F, 165F/255F, 1F)
|
||||
else Vector3f(65F/255F, 135F/255F, 1F)
|
||||
}
|
||||
else -> {
|
||||
if (behaviours.size > 1 && centerHovered(pMouseX, pMouseY))
|
||||
if (canSwitchStyle)
|
||||
Vector3f(1F, 195F/255F, 30F/255F)
|
||||
else Vector3f(1F, 165F/255F, 0F)
|
||||
}
|
||||
@@ -353,8 +357,8 @@ class StatWidget(
|
||||
context = context,
|
||||
labels = RidingStat.entries.toList().map { stat ->
|
||||
if (statLabelsHovered(pentagonVerticesOffset, pMouseX, pMouseY))
|
||||
"${floor((pokemon.getRideStat(selectedBehaviour.key, stat) / (selectedBehaviour.value.stats[stat]?.endInclusive ?: 1)) * 100)}%".text()
|
||||
else floor(pokemon.getRideStat(selectedBehaviour.key, stat)).toString().text()
|
||||
"+${floor(pokemon.getRideBoost(stat) / pokemon.getMaxRideBoost(stat) * 100)}%".text()
|
||||
else floor(pokemon.getRideStat(selectedBehaviour.key, stat)).toString().text()
|
||||
},
|
||||
verticesOffset = pentagonVerticesOffset,
|
||||
offsetY = 5.5
|
||||
@@ -389,7 +393,7 @@ class StatWidget(
|
||||
colour = Vector3f(216F/255, 100F/255, 1F)
|
||||
)
|
||||
EV -> drawStatPolygon(
|
||||
statLabels.values.map { (pokemon.evs[it]?.toFloat() ?: 0F) / 252F },
|
||||
statLabels.values.map { (pokemon.evs[it]?.toFloat() ?: 0F) / EVs.MAX_STAT_VALUE.toFloat() },
|
||||
colour = Vector3f(1F, 1F, 100F/255F)
|
||||
)
|
||||
}
|
||||
@@ -403,9 +407,9 @@ class StatWidget(
|
||||
"${
|
||||
if (pokemon.ivs.isHyperTrained(stat.value)) "${pokemon.ivs[stat.value]} (${pokemon.ivs.hyperTrainedIVs[stat.value]})"
|
||||
else pokemon.ivs[stat.value].toString()
|
||||
}/31".text()
|
||||
}/${IVs.MAX_VALUE}".text()
|
||||
}
|
||||
EV -> "${pokemon.evs.getOrDefault(stat.value)}/252".text()
|
||||
EV -> "${pokemon.evs.getOrDefault(stat.value)}/${EVs.MAX_STAT_VALUE}".text()
|
||||
else -> stat.key
|
||||
}
|
||||
} else { stat.key }
|
||||
@@ -490,14 +494,14 @@ class StatWidget(
|
||||
STATS -> (if (stat == Stats.HP) "${pokemon.currentHealth} / ${pokemon.maxHealth}" else pokemon.getStat(stat).toString()).text()
|
||||
IV -> {
|
||||
(if (asPercent) {
|
||||
"${floor((((if (pokemon.ivs.isHyperTrained(stat)) pokemon.ivs.hyperTrainedIVs[stat] else pokemon.ivs[stat]) ?: 0) / 31.0) * 100)}%"
|
||||
"${floor((((if (pokemon.ivs.isHyperTrained(stat)) pokemon.ivs.hyperTrainedIVs[stat] else pokemon.ivs[stat]) ?: 0) / IVs.MAX_VALUE.toDouble()) * 100)}%"
|
||||
} else {
|
||||
if (pokemon.ivs.isHyperTrained(stat)) "${pokemon.ivs[stat]} (${pokemon.ivs.hyperTrainedIVs[stat]})"
|
||||
else pokemon.ivs[stat].toString()
|
||||
}).text()
|
||||
}
|
||||
EV -> {
|
||||
(if (asPercent) "${floor((pokemon.evs.getOrDefault(stat) / 252.0) * 100)}%"
|
||||
(if (asPercent) "${floor((pokemon.evs.getOrDefault(stat) / EVs.MAX_STAT_VALUE.toDouble()) * 100)}%"
|
||||
else pokemon.evs.getOrDefault(stat).toString()).text()
|
||||
}
|
||||
else -> "0".text()
|
||||
@@ -558,7 +562,9 @@ class StatWidget(
|
||||
}
|
||||
|
||||
private fun centerHovered(pMouseX: Number, pMouseY: Number): Boolean {
|
||||
return (pMouseX.toInt() in (x + 44)..(x + 90)) && (pMouseY.toInt() in (y + 47)..(y + 93))
|
||||
val centerX = x + (WIDTH / 2)
|
||||
val centerY = y + (HEIGHT / 2)
|
||||
return (pMouseX.toInt() in (centerX - 30)..(centerX + 30)) && (pMouseY.toInt() in (centerY - 30)..(centerY + 30))
|
||||
}
|
||||
|
||||
private fun statLabelsHovered(labelOffsets: List<Pair<Double, Double>>, mouseX: Int, mouseY: Int): Boolean {
|
||||
|
||||
+5
-5
@@ -9,13 +9,12 @@
|
||||
package com.cobblemon.mod.common.client.net.data
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonRideSettings
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.data.RideSettingsSyncPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.client.Minecraft
|
||||
|
||||
object RideSettingsSyncHandler : ServerNetworkPacketHandler<RideSettingsSyncPacket> {
|
||||
override fun handle(packet: RideSettingsSyncPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
object RideSettingsSyncHandler : ClientNetworkPacketHandler<RideSettingsSyncPacket> {
|
||||
override fun handle(packet: RideSettingsSyncPacket, client: Minecraft) {
|
||||
CobblemonRideSettings.bird = packet.bird
|
||||
CobblemonRideSettings.glider = packet.glider
|
||||
CobblemonRideSettings.helicopter = packet.helicopter
|
||||
@@ -30,5 +29,6 @@ object RideSettingsSyncHandler : ServerNetworkPacketHandler<RideSettingsSyncPack
|
||||
CobblemonRideSettings.boat = packet.boat
|
||||
CobblemonRideSettings.burst = packet.burst
|
||||
CobblemonRideSettings.dolphin = packet.dolphin
|
||||
CobblemonRideSettings.submarine = packet.submarine
|
||||
}
|
||||
}
|
||||
+26
-7
@@ -10,22 +10,41 @@ package com.cobblemon.mod.common.client.net.orientation
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.duck.RidePassenger
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.Mth
|
||||
|
||||
object S2CUpdateOrientationHandler : ClientNetworkPacketHandler<ClientboundUpdateOrientationPacket> {
|
||||
override fun handle(packet: ClientboundUpdateOrientationPacket, client: Minecraft) {
|
||||
client.executeIfPossible {
|
||||
val level = client.level ?: return@executeIfPossible
|
||||
val entity = level.getEntity(packet.entityId)
|
||||
if (entity is OrientationControllable) {
|
||||
packet.active?.let {
|
||||
entity.orientationController.active = it
|
||||
if (!it) {
|
||||
entity.orientationController.reset()
|
||||
val player = client.player ?: return@executeIfPossible
|
||||
val vehicle = level.getEntity(packet.entityId)
|
||||
val vehicleController = if (vehicle is OrientationControllable) vehicle.orientationController else return@executeIfPossible
|
||||
|
||||
// If the client is also the driver of this entity then don't update it as the driver is the source
|
||||
// of truth on vehicle orientation updates.
|
||||
if (vehicle.controllingPassenger != player) {
|
||||
packet.active?.let { shouldUseCustomOrientation ->
|
||||
|
||||
val playerIsPassenger = vehicle.passengers.contains(player)
|
||||
|
||||
// If the player has just switched to riding a custom orientation ride then set their
|
||||
// ride x and y rots local to the vehicle rots(if the controller wasn't active and will now be active)
|
||||
// This ensures that on transition your camera stays in the same spot
|
||||
if (!vehicleController.isActive() && shouldUseCustomOrientation && playerIsPassenger) {
|
||||
// Set local to the vehicle x and yrot
|
||||
val playerRotater = player as RidePassenger?
|
||||
if (playerRotater != null) {
|
||||
playerRotater.`cobblemon$setRideXRot`(Mth.wrapDegrees(player.getXRot() - vehicle.getXRot()))
|
||||
playerRotater.`cobblemon$setRideYRot`(Mth.wrapDegrees(player.getYRot() - vehicle.getYRot()))
|
||||
}
|
||||
}
|
||||
|
||||
vehicle.orientationController.active = shouldUseCustomOrientation
|
||||
}
|
||||
entity.orientationController.updateOrientation { _ -> packet.orientation }
|
||||
vehicle.orientationController.updateOrientation { _ -> packet.orientation }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+65
-75
@@ -15,23 +15,23 @@ import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository.getPoser
|
||||
import com.cobblemon.mod.common.duck.RidePassenger
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.mixin.accessor.CameraAccessor
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Camera
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.level.ClipContext
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
object MountedCameraRenderer
|
||||
{
|
||||
fun getCameraPosition(
|
||||
instance: Camera,
|
||||
entity: Entity,
|
||||
driver: Entity,
|
||||
vehicle: Entity,
|
||||
x: Double,
|
||||
y: Double,
|
||||
@@ -51,99 +51,86 @@ object MountedCameraRenderer
|
||||
Mth.lerp(instance.partialTickTime.toDouble(), vehicle.zOld, vehicle.z)
|
||||
)
|
||||
setup(vehicle, instance.partialTickTime)
|
||||
val rollable = entity as OrientationControllable
|
||||
val controller = rollable.getOrientationController()
|
||||
val rollable = vehicle as OrientationControllable
|
||||
val vehicleController = rollable.getOrientationController()
|
||||
|
||||
val model = getPoser(vehicle.pokemon.species.resourceIdentifier, FloatingState())
|
||||
|
||||
val locatorName = delegate.getSeatLocator(driver)
|
||||
val locator = delegate.locatorStates[locatorName] ?: return null
|
||||
|
||||
// First person and view bobbing are handled the same way.
|
||||
if (!instance.isDetached || Cobblemon.config.thirdPersonViewBobbing) {
|
||||
val delegate = vehicle.delegate as PokemonClientDelegate
|
||||
val locatorName = delegate.getSeatLocator(entity)
|
||||
val locator = delegate.locatorStates[locatorName]
|
||||
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
|
||||
var offset = Vector3f(0f, (currEyeHeight - (driver.bbHeight / 2)).toFloat(), 0f)
|
||||
var eyeOffset = Vector3f(0f, (currEyeHeight - (driver.bbHeight / 2)).toFloat(), 0f)
|
||||
|
||||
if (locator == null) {
|
||||
return null
|
||||
}
|
||||
// Get additional offset from poser and add to the eyeHeight offset
|
||||
val shouldFlip = !(vehicleController.active && vehicleController.orientation != null) // Do not flip the offset for 3rd person reverse unless we are using normal mc camera rotation.
|
||||
|
||||
val locatorOffset = Vec3(locator.matrix.getTranslation(Vector3f()))
|
||||
|
||||
// Get Eye height
|
||||
val currEyeHeight: Double = Mth.lerp(instance.partialTickTime.toDouble(), eyeHeight, eyeHeightOld)
|
||||
var offset = Vector3f(0f, (currEyeHeight - (entity.bbHeight / 2)).toFloat(), 0f)
|
||||
|
||||
// Get additional offset from poser and add to the eyeHeight offset
|
||||
if (!instance.isDetached) {
|
||||
offset.add(
|
||||
getFirstPersonOffset(model, locatorName)
|
||||
)
|
||||
} else {
|
||||
offset.add(
|
||||
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffset, locatorName)
|
||||
)
|
||||
}
|
||||
|
||||
if (controller.isActive()) {
|
||||
offset = controller.getRenderOrientation(instance.partialTickTime).transform(offset)
|
||||
} else {
|
||||
offset = Axis.YP.rotationDegrees(180f - instance.yRot).transform(offset)
|
||||
}
|
||||
|
||||
val pos = locatorOffset.add(entityPos).toVector3f()
|
||||
val offsetDistance = offset.length()
|
||||
val offsetDirection = offset.mul(1 / offsetDistance)
|
||||
|
||||
// Use getMaxZoom to calculate clipping
|
||||
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
|
||||
|
||||
return Vec3(offsetDirection.mul(maxZoom).add(pos))
|
||||
eyeOffset.add(getFirstPersonOffset(model, locatorName))
|
||||
if (!instance.isDetached) {
|
||||
offset.add(
|
||||
getFirstPersonOffset(model, locatorName)
|
||||
)
|
||||
} else if( Cobblemon.config.thirdPersonViewBobbing) {
|
||||
offset.add(
|
||||
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffset, locatorName, shouldFlip)
|
||||
)
|
||||
} else {
|
||||
val xRot = (-1 * Math.toRadians(instance.xRot.toDouble())).toFloat()
|
||||
offset.add(
|
||||
getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffsetNoViewBobbing, locatorName, shouldFlip)
|
||||
)
|
||||
}
|
||||
|
||||
// get orientation based on OrientationController or normal rotations
|
||||
val orientation =
|
||||
if (controller.isActive() && controller.orientation != null)
|
||||
controller.orientation!!
|
||||
else
|
||||
Matrix3f()
|
||||
.rotateY(Math.toRadians((180f - instance.yRot).toDouble()).toFloat())
|
||||
.rotateX(xRot)
|
||||
// Grab the current camera rotation to use as the offset rotation.
|
||||
val offsetRot = instance.rotation()
|
||||
offset = offsetRot.transform(offset)
|
||||
eyeOffset = offsetRot.transform(eyeOffset)
|
||||
|
||||
// Get pivot from poser
|
||||
val pivotOffsets = model.thirdPersonPivotOffset
|
||||
val pokemonCenter = Vector3f(0f, entity.bbHeight/2, 0f)
|
||||
val eyeLocatorOffset = Vec3(locator.matrix.getTranslation(Vector3f()))
|
||||
val eyePos = eyeLocatorOffset.add(entityPos).toVector3f()
|
||||
|
||||
var pivot = Vector3f(pokemonCenter)
|
||||
val locatorName = delegate.getSeatLocator(entity)
|
||||
if (pivotOffsets.containsKey(locatorName)) {
|
||||
pivot = pivotOffsets[locatorName]!!.toVector3f()
|
||||
pivot.sub(pokemonCenter)
|
||||
orientation.transform(pivot)
|
||||
pivot.add(pokemonCenter)
|
||||
// Get the camera position. If 3rd person viewbobbing is enabled or the player is
|
||||
// in first person then base the camera position off the seat locator offset
|
||||
val pos = if (!instance.isDetached || Cobblemon.config.thirdPersonViewBobbing) {
|
||||
val locatorOffset = Vec3(locator.matrix.getTranslation(Vector3f()))
|
||||
locatorOffset.add(entityPos).toVector3f()
|
||||
} else {
|
||||
val pokemonCenter = Vector3f(0f, driver.bbHeight/2, 0f)
|
||||
entityPos.toVector3f().add(pokemonCenter)
|
||||
}
|
||||
|
||||
val pos = entityPos.toVector3f().add(pivot)
|
||||
val offsetDistance = offset.length()
|
||||
val offsetDirection = offset.mul(1 / offsetDistance)
|
||||
|
||||
// Get offset from poser
|
||||
val offset = getThirdPersonOffset(thirdPersonReverse, model.thirdPersonCameraOffsetNoViewBobbing, locatorName)
|
||||
val eyeOffsetDistance = eyeOffset.length()
|
||||
val eyeOffsetDirection = eyeOffset.mul(1 / eyeOffsetDistance)
|
||||
|
||||
val rotatedOffset = orientation.transform(offset)
|
||||
val offsetDistance = rotatedOffset.length()
|
||||
val offsetDirection = rotatedOffset.mul(1 / offsetDistance)
|
||||
// Use getMaxZoom to calculate clipping
|
||||
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
|
||||
val eyeMaxZoom: Float = getMaxZoom(eyeOffsetDistance, eyeOffsetDirection, eyePos, instance)
|
||||
|
||||
// Use getMaxZoom to calculate clipping
|
||||
val maxZoom: Float = getMaxZoom(offsetDistance, offsetDirection, pos, instance)
|
||||
val playerRotater = driver as RidePassenger
|
||||
playerRotater.`cobblemon$setRideEyePos`(Vec3(eyeOffsetDirection.mul(eyeMaxZoom).add(eyePos)).add(0.0, 2.0, 0.0))
|
||||
return Vec3(offsetDirection.mul(maxZoom).add(pos))
|
||||
}
|
||||
|
||||
return Vec3(offsetDirection.mul(maxZoom).add(pos))
|
||||
}
|
||||
fun resetDriverRotations(
|
||||
instance: Camera,
|
||||
driver: Entity,
|
||||
) {
|
||||
val eulerAngs = Quaternionf(instance.rotation()).getEulerAnglesYXZ(Vector3f())
|
||||
val playerRotater = driver as RidePassenger
|
||||
// Backticks because kotlin explodes otherwise due to syntax
|
||||
playerRotater.`cobblemon$setRideXRot`(-eulerAngs.x() * Mth.RAD_TO_DEG)
|
||||
playerRotater.`cobblemon$setRideYRot`(180.0f - (eulerAngs.y() * Mth.RAD_TO_DEG))
|
||||
}
|
||||
|
||||
|
||||
private fun getThirdPersonOffset(
|
||||
thirdPersonReverse: Boolean,
|
||||
cameraOffsets: Map<String, Vec3>,
|
||||
locatorName: String
|
||||
locatorName: String,
|
||||
shouldFlip: Boolean = true
|
||||
): Vector3f {
|
||||
val offset = if (thirdPersonReverse && cameraOffsets.containsKey(locatorName + "_reverse")) {
|
||||
cameraOffsets[locatorName + "_reverse"]!!.toVector3f()
|
||||
@@ -152,7 +139,10 @@ object MountedCameraRenderer
|
||||
} else {
|
||||
Vector3f(0f, 2f, 4f)
|
||||
}
|
||||
if (thirdPersonReverse) offset.z *= -1
|
||||
|
||||
// Don't need to account for this since orientation is derived from camera rotation and that z is already flipped.
|
||||
// Flip only when not on a rollable.
|
||||
if (thirdPersonReverse && shouldFlip) offset.z *= -1
|
||||
return offset
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -747,9 +747,8 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
// If scale is 0 we start getting NaNs
|
||||
scale.coerceAtLeast(0.01f)
|
||||
if (entity.passengers.isNotEmpty() && entity.controllingPassenger is OrientationControllable
|
||||
&& (entity.controllingPassenger as OrientationControllable).orientationController.active){
|
||||
val controllingPassenger = entity.controllingPassenger as OrientationControllable
|
||||
val controller = controllingPassenger.orientationController
|
||||
&& (entity as OrientationControllable).orientationController.active){
|
||||
val controller = entity.orientationController
|
||||
val transformationMatrix = Matrix4f()
|
||||
val center = Vector3f(0f, entity.bbHeight/2, 0f)
|
||||
transformationMatrix.translate(center)
|
||||
|
||||
-67
@@ -1,67 +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.pokemon.gen5
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
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 BeheeyemModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("beheeyem")
|
||||
override val head = getPart("head")
|
||||
override val rightArm = getPart("arm_right")
|
||||
override val leftArm = getPart("arm_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
|
||||
override var portraitScale = 3.0F
|
||||
override var portraitTranslation = Vec3(-0.34, -0.8, 0.0)
|
||||
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
|
||||
|
||||
lateinit var sleep: Pose
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("beheeyem", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(bedrock("beheeyem", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
transformTicks = 10,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("beheeyem", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
transformTicks = 5,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("beheeyem", "ground_walk")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +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.pokemon.gen5
|
||||
|
||||
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.CobblemonPose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ChandelureModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("chandelure")
|
||||
override val head = getPart("head")
|
||||
|
||||
override var portraitScale = 1.85F
|
||||
override var portraitTranslation = Vec3(-0.3, -0.19, 0.0)
|
||||
|
||||
override var profileScale = 0.81F
|
||||
override var profileTranslation = Vec3(0.0, 0.68, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
lateinit var hover: CobblemonPose
|
||||
lateinit var flying: CobblemonPose
|
||||
lateinit var battle_idle: CobblemonPose
|
||||
lateinit var sleep: CobblemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("chandelure", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("chandelure", "blink") }
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES - PoseType.HOVER,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { !it.isBattling },
|
||||
animations = arrayOf(
|
||||
bedrock("chandelure", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("chandelure", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
hover = registerPose(
|
||||
poseName = "hover",
|
||||
poseType = PoseType.HOVER,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("chandelure", "air_idle")
|
||||
)
|
||||
)
|
||||
|
||||
flying = registerPose(
|
||||
poseName = "flying",
|
||||
poseType = PoseType.FLY,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("chandelure", "air_fly")
|
||||
)
|
||||
)
|
||||
|
||||
battle_idle = registerPose(
|
||||
poseName = "battle_idle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
animations = arrayOf(
|
||||
bedrock("chandelure", "battle_idle")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("chandelure", "faint") else null
|
||||
}
|
||||
-113
@@ -1,113 +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.pokemon.gen5
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
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.ModelPartTransformation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ElgyemModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("elgyem")
|
||||
override val head = getPart("head")
|
||||
override val rightArm = getPart("arm_right")
|
||||
override val leftArm = getPart("arm_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
override val leftLeg = getPart("leg_left")
|
||||
|
||||
override var portraitScale = 2.5F
|
||||
override var portraitTranslation = Vec3(-0.2, -0.8, 0.0)
|
||||
|
||||
override var profileScale = 0.85F
|
||||
override var profileTranslation = Vec3(0.0, 0.5, 0.0)
|
||||
|
||||
lateinit var sleep: Pose
|
||||
lateinit var standing: Pose
|
||||
lateinit var walk: Pose
|
||||
lateinit var battleidle: Pose
|
||||
lateinit var shoulderLeft: Pose
|
||||
lateinit var shoulderRight: Pose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("elgyem", "cry") }
|
||||
|
||||
val shoulderOffset = 5.6
|
||||
|
||||
override fun registerPoses() {
|
||||
sleep = registerPose(
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(bedrock("elgyem", "sleep"))
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
transformTicks = 10,
|
||||
condition = { !it.isBattling },
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("elgyem", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
transformTicks = 5,
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("elgyem", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
battleidle = registerPose(
|
||||
poseName = "battle_idle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
transformTicks = 10,
|
||||
condition = { it.isBattling },
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("elgyem", "battle_idle")
|
||||
)
|
||||
)
|
||||
|
||||
shoulderLeft = registerPose(
|
||||
poseType = PoseType.SHOULDER_LEFT,
|
||||
//quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("elgyem", "ground_idle")
|
||||
),
|
||||
transformedParts = arrayOf(
|
||||
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset)
|
||||
)
|
||||
)
|
||||
|
||||
shoulderRight = registerPose(
|
||||
poseType = PoseType.SHOULDER_RIGHT,
|
||||
//quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("elgyem", "ground_idle")
|
||||
),
|
||||
transformedParts = arrayOf(
|
||||
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset)
|
||||
)
|
||||
)
|
||||
}
|
||||
override fun getFaintAnimation(state: PosableState) = if (state.isPosedIn(standing, walk, battleidle, sleep)) bedrockStateful("elgyem", "faint") else null
|
||||
}
|
||||
-102
@@ -1,102 +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.pokemon.gen5
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
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.pose.CobblemonPose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class LampentModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("lampent")
|
||||
override val head = getPart("head")
|
||||
|
||||
override var portraitScale = 2.2F
|
||||
override var portraitTranslation = Vec3(-0.19, -0.17, 0.0)
|
||||
|
||||
override var profileScale = 0.8F
|
||||
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
lateinit var hover: CobblemonPose
|
||||
lateinit var flying: CobblemonPose
|
||||
lateinit var battle_idle: CobblemonPose
|
||||
lateinit var sleep: CobblemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("lampent", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("lampent", "blink") }
|
||||
|
||||
sleep = registerPose(
|
||||
poseName = "sleep",
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(
|
||||
bedrock("lampent", "sleep")
|
||||
)
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES - PoseType.HOVER,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { !it.isBattling },
|
||||
animations = arrayOf(
|
||||
bedrock("lampent", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES - PoseType.FLY,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("lampent", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
hover = registerPose(
|
||||
poseName = "hover",
|
||||
poseType = PoseType.HOVER,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("lampent", "air_idle")
|
||||
)
|
||||
)
|
||||
|
||||
flying = registerPose(
|
||||
poseName = "flying",
|
||||
poseType = PoseType.FLY,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("lampent", "air_fly")
|
||||
)
|
||||
)
|
||||
|
||||
battle_idle = registerPose(
|
||||
poseName = "battle_idle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
animations = arrayOf(
|
||||
bedrock("lampent", "battle_idle")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("lampent", "faint") else null
|
||||
}
|
||||
-110
@@ -1,110 +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.pokemon.gen5
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
|
||||
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.pose.CobblemonPose
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.util.isBattling
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class LitwickModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("litwick")
|
||||
override val head = getPart("head")
|
||||
|
||||
override var portraitScale = 2.33F
|
||||
override var portraitTranslation = Vec3(0.01, -1.9, 0.0)
|
||||
|
||||
override var profileScale = 0.86F
|
||||
override var profileTranslation = Vec3(0.0, 0.44, 0.0)
|
||||
|
||||
lateinit var standing: CobblemonPose
|
||||
lateinit var walk: CobblemonPose
|
||||
lateinit var sleep: CobblemonPose
|
||||
lateinit var battle_idle: CobblemonPose
|
||||
lateinit var shoulderLeft: CobblemonPose
|
||||
lateinit var shoulderRight: CobblemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { bedrockStateful("litwick", "cry") }
|
||||
|
||||
val shoulderOffset = 5.5
|
||||
val wax = getPart("wax_trail")
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("litwick", "blink") }
|
||||
|
||||
sleep = registerPose(
|
||||
poseName = "sleep",
|
||||
poseType = PoseType.SLEEP,
|
||||
animations = arrayOf(
|
||||
bedrock("litwick", "sleep")
|
||||
)
|
||||
)
|
||||
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
|
||||
condition = { !it.isBattling },
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("litwick", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("litwick", "ground_walk")
|
||||
)
|
||||
)
|
||||
|
||||
battle_idle = registerPose(
|
||||
poseName = "battle_idle",
|
||||
poseTypes = PoseType.STATIONARY_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
condition = { it.isBattling },
|
||||
animations = arrayOf(
|
||||
bedrock("litwick", "battle_idle")
|
||||
)
|
||||
)
|
||||
|
||||
shoulderLeft = registerPose(
|
||||
poseType = PoseType.SHOULDER_LEFT,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("litwick", "ground_idle")
|
||||
),
|
||||
transformedParts = arrayOf(
|
||||
wax.createTransformation().withVisibility(visibility = false)
|
||||
)
|
||||
)
|
||||
|
||||
shoulderRight = registerPose(
|
||||
poseType = PoseType.SHOULDER_RIGHT,
|
||||
quirks = arrayOf(blink),
|
||||
animations = arrayOf(
|
||||
bedrock("litwick", "ground_idle")
|
||||
),
|
||||
transformedParts = arrayOf(
|
||||
wax.createTransformation().withVisibility(visibility = false)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// override fun getFaintAnimation(
|
||||
// pokemonEntity: PokemonEntity,
|
||||
// state: PosableState<PokemonEntity>
|
||||
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("litwick", "faint") else null
|
||||
}
|
||||
-5
@@ -361,8 +361,6 @@ object VaryingModelRepository {
|
||||
inbuilt("chespin", :: ChespinModel)
|
||||
inbuilt("quilladin", :: QuilladinModel)
|
||||
inbuilt("chesnaught", :: ChesnaughtModel)
|
||||
inbuilt("elgyem", :: ElgyemModel)
|
||||
inbuilt("beheeyem", :: BeheeyemModel)
|
||||
inbuilt("pineco", :: PinecoModel)
|
||||
inbuilt("forretress", :: ForretressModel)
|
||||
inbuilt("lotad", :: LotadModel)
|
||||
@@ -554,9 +552,6 @@ object VaryingModelRepository {
|
||||
inbuilt("gurdurr", ::GurdurrModel)
|
||||
inbuilt("conkeldurr", ::ConkeldurrModel)
|
||||
inbuilt("clodsire", ::ClodsireModel)
|
||||
inbuilt("litwick", ::LitwickModel)
|
||||
inbuilt("lampent", ::LampentModel)
|
||||
inbuilt("chandelure", ::ChandelureModel)
|
||||
inbuilt("gimmighoulchest", ::GimmighoulChestModel)
|
||||
inbuilt("drifloon", ::DrifloonModel)
|
||||
inbuilt("lileep", ::LileepModel)
|
||||
|
||||
+7
-13
@@ -42,14 +42,9 @@ import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.DoubleRange
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.math.remap
|
||||
import com.google.common.primitives.Floats
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.math.Axis
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.Font.DisplayMode
|
||||
@@ -67,11 +62,8 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Math
|
||||
import org.joml.Matrix4f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import org.joml.Vector4f
|
||||
import org.joml.*
|
||||
import kotlin.math.*
|
||||
|
||||
class PokemonRenderer(
|
||||
context: EntityRendererProvider.Context
|
||||
@@ -189,12 +181,14 @@ class PokemonRenderer(
|
||||
buffer: MultiBufferSource,
|
||||
packedLight: Int
|
||||
) {
|
||||
val driver = entity.firstPassenger ?: return
|
||||
val rollable = driver as? OrientationControllable ?: return
|
||||
val rollable = entity as? OrientationControllable ?: return
|
||||
val controller = rollable.orientationController
|
||||
poseMatrix.pushPose()
|
||||
|
||||
if (!DISABLE_ROLLING_DEBUG && controller.active) {
|
||||
// Allow the ride controller to modify its rotations using partialTick
|
||||
entity.applyRenderRotation(partialTicks)
|
||||
|
||||
val matrix = poseMatrix.last().pose()
|
||||
val yaw = Mth.rotLerp(partialTicks, entity.yBodyRotO, entity.yBodyRot)
|
||||
val center = Vector3f(0f, entity.bbHeight/2, 0f)
|
||||
|
||||
-3
@@ -23,9 +23,6 @@ object CobblemonTooltipGenerator : TooltipGenerator() {
|
||||
val resultLines = mutableListOf<Component>()
|
||||
|
||||
if (stack.item.builtInRegistryHolder().unwrapKey().isPresent && stack.item.builtInRegistryHolder().unwrapKey().get().location().namespace == Cobblemon.MODID) {
|
||||
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
|
||||
return null
|
||||
}
|
||||
val language = Language.getInstance()
|
||||
val key = this.baseLangKeyForItem(stack)
|
||||
if (language.has(key)) {
|
||||
|
||||
-6
@@ -31,9 +31,6 @@ object FishingBaitTooltipGenerator : TooltipGenerator() {
|
||||
private val fishingBaitItemClass by lazy { lang("item_class.fishing_bait").blue() }
|
||||
|
||||
override fun generateCategoryTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
|
||||
return null
|
||||
}
|
||||
if (!SpawnBaitEffects.isFishingBait(stack)) {
|
||||
return null
|
||||
}
|
||||
@@ -41,9 +38,6 @@ object FishingBaitTooltipGenerator : TooltipGenerator() {
|
||||
}
|
||||
|
||||
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
|
||||
return null
|
||||
}
|
||||
val resultLines = mutableListOf<Component>()
|
||||
|
||||
if (SpawnBaitEffects.isFishingBait(stack)) resultLines.add(this.fishingBaitItemClass)
|
||||
|
||||
-3
@@ -23,9 +23,6 @@ import net.minecraft.world.item.ItemStack
|
||||
|
||||
object FishingRodTooltipGenerator : TooltipGenerator() {
|
||||
override fun generateTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
|
||||
return null
|
||||
}
|
||||
val resultLines = mutableListOf<Component>()
|
||||
|
||||
val rod = (stack.item as? PokerodItem)?.pokeRodId?.let { PokeRods.getPokeRod(it) } ?: return null
|
||||
|
||||
-4
@@ -61,10 +61,6 @@ object SeasoningTooltipGenerator : TooltipGenerator() {
|
||||
}
|
||||
|
||||
override fun generateAdditionalTooltip(stack: ItemStack, lines: MutableList<Component>): MutableList<Component>? {
|
||||
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val isSeasoningIngredient = Seasonings.isSeasoning(stack)
|
||||
val flavors = stack.get(CobblemonItemComponents.FLAVOUR)?.flavours
|
||||
val food = stack.get(CobblemonItemComponents.FOOD)
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.client.tooltips
|
||||
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.core.component.DataComponents
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
@@ -22,6 +23,12 @@ object TooltipManager {
|
||||
}
|
||||
|
||||
fun generateTooltips(stack: ItemStack, lines: MutableList<Component>, hasShiftDown: Boolean) {
|
||||
// This will need to be changed in 1.21.5 as 25w04a changed the following:
|
||||
// "Removed the hide_tooltip and hide_additional_tooltip components in favor of the new tooltip_display component."
|
||||
if (stack.get(DataComponents.HIDE_ADDITIONAL_TOOLTIP) != null) {
|
||||
return
|
||||
}
|
||||
|
||||
val standardLines = tooltipGenerators.flatMap {
|
||||
val innerLines = mutableListOf<Component>()
|
||||
val regularTooltip = it.generateTooltip(stack, lines)
|
||||
|
||||
@@ -12,6 +12,8 @@ import com.cobblemon.mod.common.api.drop.ItemDropMethod
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.calculators.CaptureCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.client.gui.portrait.PortraitStyle
|
||||
import com.cobblemon.mod.common.config.CobblemonConfigField.CobblemonConfigSide.CLIENT
|
||||
import com.cobblemon.mod.common.config.CobblemonConfigField.CobblemonConfigSide.SERVER
|
||||
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
|
||||
@@ -19,9 +21,9 @@ import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class CobblemonConfig {
|
||||
companion object {
|
||||
@@ -37,123 +39,123 @@ class CobblemonConfig {
|
||||
|
||||
var lastSavedVersion: String = "0.0.1"
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "max_pokemon_level")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "max_pokemon_level", SERVER)
|
||||
@IntConstraint(min = 1, max = 1000)
|
||||
var maxPokemonLevel = 100
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "max_pokemon_friendship")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "max_pokemon_friendship", SERVER)
|
||||
@IntConstraint(min = 0, max = 1000)
|
||||
var maxPokemonFriendship = 255
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "announce_drop_items")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "announce_drop_items", SERVER)
|
||||
var announceDropItems = true
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "default_drop_item_method")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "default_drop_item_method", SERVER)
|
||||
var defaultDropItemMethod = ItemDropMethod.ON_ENTITY
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "drops_after_death_animation")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "drops_after_death_animation", SERVER)
|
||||
var dropAfterDeathAnimation = false
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "ambient_pokemon_cry_ticks")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "ambient_pokemon_cry_ticks", SERVER)
|
||||
@LastChangedVersion("1.4.0")
|
||||
var ambientPokemonCryTicks = 1080
|
||||
|
||||
@CobblemonConfigField(Category.Storage, lang = "default_box_count")
|
||||
@CobblemonConfigField(Category.Storage, lang = "default_box_count", SERVER)
|
||||
@IntConstraint(min = 1, max = 1000)
|
||||
@LastChangedVersion("1.7.0")
|
||||
var defaultBoxCount = 40
|
||||
@CobblemonConfigField(Category.Storage, lang = "pokemon_save_interval_seconds")
|
||||
@CobblemonConfigField(Category.Storage, lang = "pokemon_save_interval_seconds", SERVER)
|
||||
@IntConstraint(min = 1, max = 120)
|
||||
var pokemonSaveIntervalSeconds = 30
|
||||
|
||||
@CobblemonConfigField(Category.Storage, lang = "storage_format")
|
||||
@CobblemonConfigField(Category.Storage, lang = "storage_format", SERVER)
|
||||
var storageFormat = "nbt"
|
||||
|
||||
@CobblemonConfigField(Category.Storage, lang = "prevent_complete_party_deposit")
|
||||
@CobblemonConfigField(Category.Storage, lang = "prevent_complete_party_deposit", SERVER)
|
||||
var preventCompletePartyDeposit = false
|
||||
|
||||
@CobblemonConfigField(Category.Storage, lang = "mongo_db_connection_string")
|
||||
@CobblemonConfigField(Category.Storage, lang = "mongo_db_connection_string", SERVER)
|
||||
var mongoDBConnectionString = "mongodb://localhost:27017"
|
||||
@CobblemonConfigField(Category.Storage, lang = "mongo_db_database_name")
|
||||
@CobblemonConfigField(Category.Storage, lang = "mongo_db_database_name", SERVER)
|
||||
var mongoDBDatabaseName = "cobblemon"
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_vertical_correction_blocks")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_vertical_correction_blocks", SERVER)
|
||||
@IntConstraint(min = 1, max = 200)
|
||||
var maxVerticalCorrectionBlocks = 64
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "minimum_level_range_max")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "minimum_level_range_max", SERVER)
|
||||
@IntConstraint(min = 1, max = 1000)
|
||||
var minimumLevelRangeMax = 10
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "enable_spawning")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "enable_spawning", SERVER)
|
||||
var enableSpawning = true
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "minimum_distance_between_entities")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "minimum_distance_between_entities", SERVER)
|
||||
var minimumDistanceBetweenEntities = 8.0
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_nearby_blocks_horizontal_range")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_nearby_blocks_horizontal_range", SERVER)
|
||||
var maxNearbyBlocksHorizontalRange = 4
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_nearby_blocks_vertical_range")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_nearby_blocks_vertical_range", SERVER)
|
||||
var maxNearbyBlocksVerticalRange = 2
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_vertical_space")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "max_vertical_space", SERVER)
|
||||
var maxVerticalSpace = 8
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "spawning_zone_diameter")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "spawning_zone_diameter", SERVER)
|
||||
@SerializedName("spawningZoneDiameter", alternate = ["worldSliceDiameter"])
|
||||
var spawningZoneDiameter = 8
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "spawning_zone_height")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "spawning_zone_height", SERVER)
|
||||
@SerializedName("spawningZoneHeight", alternate = ["worldSliceHeight"])
|
||||
var spawningZoneHeight = 16
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "ticks_between_spawn_attempts")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "ticks_between_spawn_attempts", SERVER)
|
||||
var ticksBetweenSpawnAttempts = 20F
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "minimum_spawning_zone_distance_from_player")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "minimum_spawning_zone_distance_from_player", SERVER)
|
||||
@SerializedName("minimumSpawningZoneDistanceFromPlayer", alternate = ["minimumSliceDistanceFromPlayer"])
|
||||
var minimumSpawningZoneDistanceFromPlayer = 16F
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "maximum_spawning_zone_distance_from_player")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "maximum_spawning_zone_distance_from_player", SERVER)
|
||||
@SerializedName("maximumSpawningZoneDistanceFromPlayer", alternate = ["maximumSliceDistanceFromPlayer"])
|
||||
var maximumSpawningZoneDistanceFromPlayer = 16 * 4F
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "maximum_spawns_per_pass")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "maximum_spawns_per_pass", SERVER)
|
||||
var maximumSpawnsPerPass = 8
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "export_spawn_config")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "export_spawn_config", SERVER)
|
||||
var exportSpawnConfig = false
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "save_pokemon_to_world")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "save_pokemon_to_world", SERVER)
|
||||
var savePokemonToWorld = true
|
||||
|
||||
@CobblemonConfigField(Category.Starter, lang = "export_starter_config")
|
||||
@CobblemonConfigField(Category.Starter, lang = "export_starter_config", SERVER)
|
||||
var exportStarterConfig = false
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "auto_update_showdown")
|
||||
@CobblemonConfigField(Category.Battles, lang = "auto_update_showdown", SERVER)
|
||||
var autoUpdateShowdown = true
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "default_flee_distance")
|
||||
@CobblemonConfigField(Category.Battles, lang = "default_flee_distance", SERVER)
|
||||
var defaultFleeDistance = 16F * 2
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "allow_experience_from_pvp")
|
||||
@CobblemonConfigField(Category.Battles, lang = "allow_experience_from_pvp", SERVER)
|
||||
var allowExperienceFromPvP = true
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "experience_share_multiplier")
|
||||
@CobblemonConfigField(Category.Battles, lang = "experience_share_multiplier", SERVER)
|
||||
var experienceShareMultiplier = .5
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "lucky_egg_multiplier")
|
||||
@CobblemonConfigField(Category.Battles, lang = "lucky_egg_multiplier", SERVER)
|
||||
var luckyEggMultiplier = 1.5
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "allow_spectating")
|
||||
@CobblemonConfigField(Category.Battles, lang = "allow_spectating", SERVER)
|
||||
var allowSpectating = true
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "experience_multiplier")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "experience_multiplier", SERVER)
|
||||
var experienceMultiplier = 2F
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "pokemon_per_chunk")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "pokemon_per_chunk", SERVER)
|
||||
var pokemonPerChunk = 1F
|
||||
|
||||
@CobblemonConfigField(Category.PassiveStatus, lang = "passive_statuses")
|
||||
@CobblemonConfigField(Category.PassiveStatus, lang = "passive_statuses", SERVER)
|
||||
var passiveStatuses = mutableMapOf(
|
||||
Statuses.POISON.configEntry(),
|
||||
Statuses.POISON_BADLY.configEntry(),
|
||||
@@ -163,165 +165,165 @@ class CobblemonConfig {
|
||||
Statuses.BURN.configEntry()
|
||||
)
|
||||
|
||||
@CobblemonConfigField(Category.Healing, lang = "infinite_healer_charge")
|
||||
@CobblemonConfigField(Category.Healing, lang = "infinite_healer_charge", SERVER)
|
||||
var infiniteHealerCharge = false
|
||||
|
||||
@CobblemonConfigField(Category.Healing, lang = "max_healer_charge")
|
||||
@CobblemonConfigField(Category.Healing, lang = "max_healer_charge", SERVER)
|
||||
var maxHealerCharge = 6.0f
|
||||
|
||||
@CobblemonConfigField(Category.Healing, lang = "seconds_to_charge_healing_machine")
|
||||
@CobblemonConfigField(Category.Healing, lang = "seconds_to_charge_healing_machine", SERVER)
|
||||
var secondsToChargeHealingMachine = 900.0
|
||||
|
||||
@CobblemonConfigField(Category.Healing, lang = "default_faint_timer")
|
||||
@CobblemonConfigField(Category.Healing, lang = "default_faint_timer", SERVER)
|
||||
var defaultFaintTimer = 300
|
||||
|
||||
@CobblemonConfigField(Category.Healing, lang = "faint_awaken_health_percent")
|
||||
@CobblemonConfigField(Category.Healing, lang = "faint_awaken_health_percent", SERVER)
|
||||
var faintAwakenHealthPercent = 0.2f
|
||||
|
||||
@CobblemonConfigField(Category.Healing, lang = "heal_percent")
|
||||
@CobblemonConfigField(Category.Healing, lang = "heal_percent", SERVER)
|
||||
var healPercent = 0.05
|
||||
|
||||
@CobblemonConfigField(Category.Healing, lang = "heal_timer")
|
||||
@CobblemonConfigField(Category.Healing, lang = "heal_timer", SERVER)
|
||||
var healTimer = 60
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "base_apricorn_tree_generation_chance")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "base_apricorn_tree_generation_chance", SERVER)
|
||||
var baseApricornTreeGenerationChance = 0.1F
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_level_label")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_level_label", CLIENT)
|
||||
var displayEntityLevelLabel = true
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_name_label")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_name_label", CLIENT)
|
||||
var displayEntityNameLabel = true
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_name_for_unknown_pokemon")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_name_for_unknown_pokemon", CLIENT)
|
||||
var displayNameForUnknownPokemon = false
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_labels_when_crouching_only")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "display_entity_labels_when_crouching_only", CLIENT)
|
||||
var displayEntityLabelsWhenCrouchingOnly = false
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "shiny_rate")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "shiny_rate", SERVER)
|
||||
var shinyRate = 8192F
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "shiny_notice_particles_distance")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "shiny_notice_particles_distance", CLIENT)
|
||||
var shinyNoticeParticlesDistance = 24F
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "capture_calculator")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "capture_calculator", SERVER)
|
||||
var captureCalculator: CaptureCalculator = CobblemonCaptureCalculator
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "player_damage_pokemon")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "player_damage_pokemon", SERVER)
|
||||
var playerDamagePokemon = true
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "apple_leftovers_chance")
|
||||
@CobblemonConfigField(Category.World, lang = "apple_leftovers_chance", SERVER)
|
||||
var appleLeftoversChance = 0.025
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "max_roots_in_area")
|
||||
@CobblemonConfigField(Category.World, lang = "max_roots_in_area", SERVER)
|
||||
var maxRootsInArea = 5
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "big_root_propagation_chance")
|
||||
@CobblemonConfigField(Category.World, lang = "big_root_propagation_chance", SERVER)
|
||||
var bigRootPropagationChance = 0.1
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "energy_root_chance")
|
||||
@CobblemonConfigField(Category.World, lang = "energy_root_chance", SERVER)
|
||||
var energyRootChance = 0.25
|
||||
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "max_dynamax_level")
|
||||
@CobblemonConfigField(Category.Pokemon, lang = "max_dynamax_level", SERVER)
|
||||
@IntConstraint(min = 0, max = 10)
|
||||
var maxDynamaxLevel = 10
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "tera_type_rate")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "tera_type_rate", SERVER)
|
||||
var teraTypeRate = 20F
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "default_pastured_pokemon_limit")
|
||||
@CobblemonConfigField(Category.World, lang = "default_pastured_pokemon_limit", SERVER)
|
||||
var defaultPasturedPokemonLimit = 16
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "pasture_block_update_ticks")
|
||||
@CobblemonConfigField(Category.World, lang = "pasture_block_update_ticks", SERVER)
|
||||
var pastureBlockUpdateTicks = 40
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "pasture_max_wander_distance")
|
||||
@CobblemonConfigField(Category.World, lang = "pasture_max_wander_distance", SERVER)
|
||||
var pastureMaxWanderDistance = 64
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "pasture_max_per_chunk")
|
||||
@CobblemonConfigField(Category.World, lang = "pasture_max_per_chunk", SERVER)
|
||||
var pastureMaxPerChunk = 4F
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "max_inserted_fossil_items")
|
||||
@CobblemonConfigField(Category.World, lang = "max_inserted_fossil_items", SERVER)
|
||||
var maxInsertedFossilItems = 2
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "walking_in_battle_animations")
|
||||
@CobblemonConfigField(Category.Battles, lang = "walking_in_battle_animations", CLIENT)
|
||||
var walkingInBattleAnimations = false
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "battle_wild_max_distance")
|
||||
@CobblemonConfigField(Category.Battles, lang = "battle_wild_max_distance", SERVER)
|
||||
var battleWildMaxDistance = 12F
|
||||
|
||||
@CobblemonConfigField(Category.World, lang = "trade_max_distance")
|
||||
@CobblemonConfigField(Category.World, lang = "trade_max_distance", SERVER)
|
||||
var tradeMaxDistance = 12F
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "battle_pvp_max_distance")
|
||||
@CobblemonConfigField(Category.Battles, lang = "battle_pvp_max_distance", SERVER)
|
||||
@SerializedName("battlePvPMaxDistance", alternate = ["BattlePvPMaxDistance"])
|
||||
var battlePvPMaxDistance = 32F
|
||||
|
||||
@CobblemonConfigField(Category.Battles, lang = "battle_spectate_max_distance")
|
||||
@CobblemonConfigField(Category.Battles, lang = "battle_spectate_max_distance", SERVER)
|
||||
var battleSpectateMaxDistance = 64F
|
||||
|
||||
@CobblemonConfigField(Category.Pokedex, lang = "max_pokedex_scanning_detection_range")
|
||||
@CobblemonConfigField(Category.Pokedex, lang = "max_pokedex_scanning_detection_range", SERVER)
|
||||
var maxPokedexScanningDetectionRange = 10.0
|
||||
|
||||
@CobblemonConfigField(Category.Pokedex, lang = "hide_unimplemented_pokemon_in_the_pokedex")
|
||||
@CobblemonConfigField(Category.Pokedex, lang = "hide_unimplemented_pokemon_in_the_pokedex", CLIENT)
|
||||
var hideUnimplementedPokemonInThePokedex = false
|
||||
|
||||
@CobblemonConfigField(Category.Interface, lang = "summary_pokemon_follow_cursor")
|
||||
@CobblemonConfigField(Category.Interface, lang = "summary_pokemon_follow_cursor", CLIENT)
|
||||
var summaryPokemonFollowCursor = true
|
||||
|
||||
@CobblemonConfigField(Category.Interface, lang = "party_portrait_animations")
|
||||
@CobblemonConfigField(Category.Interface, lang = "party_portrait_animations", CLIENT)
|
||||
var partyPortraitAnimations = PortraitStyle.NEVER_ANIMATE
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "third_person_view_bobbing")
|
||||
@CobblemonConfigField(Category.Riding, lang = "third_person_view_bobbing", CLIENT)
|
||||
var thirdPersonViewBobbing = true
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "invert_roll")
|
||||
@CobblemonConfigField(Category.Riding, lang = "invert_roll", CLIENT)
|
||||
var invertRoll = false
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "invert_pitch")
|
||||
@CobblemonConfigField(Category.Riding, lang = "invert_pitch", CLIENT)
|
||||
var invertPitch = false
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "invert_yaw")
|
||||
@CobblemonConfigField(Category.Riding, lang = "invert_yaw", CLIENT)
|
||||
var invertYaw = false
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "x_axis_sensitivity")
|
||||
@CobblemonConfigField(Category.Riding, lang = "x_axis_sensitivity", CLIENT)
|
||||
var xAxisSensitivity = 1.0
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "y_axis_sensitivity")
|
||||
@CobblemonConfigField(Category.Riding, lang = "y_axis_sensitivity", CLIENT)
|
||||
var yAxisSensitivity = 1.0
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "swap_x_and_y_axes")
|
||||
@CobblemonConfigField(Category.Riding, lang = "swap_x_and_y_axes", CLIENT)
|
||||
var swapXAndYAxes = false
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "automatic_righting_delay")
|
||||
@CobblemonConfigField(Category.Riding, lang = "automatic_righting_delay", CLIENT)
|
||||
var rightingDelay = -1.0
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "disable_roll")
|
||||
@CobblemonConfigField(Category.Riding, lang = "disable_roll", CLIENT)
|
||||
var disableRoll = false
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "display_controls_duration_seconds")
|
||||
@CobblemonConfigField(Category.Riding, lang = "display_controls_duration_seconds", CLIENT)
|
||||
var displayControlSeconds = 3
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "infinite_ride_stamina")
|
||||
@CobblemonConfigField(Category.Riding, lang = "infinite_ride_stamina", SERVER)
|
||||
var infiniteRideStamina = false
|
||||
|
||||
@CobblemonConfigField(Category.Riding, lang = "remember_riding_camera")
|
||||
@CobblemonConfigField(Category.Riding, lang = "remember_riding_camera", CLIENT)
|
||||
var rememberRidingCamera = true
|
||||
|
||||
@CobblemonConfigField(Category.Debug, lang = "enable_debug_keys")
|
||||
@CobblemonConfigField(Category.Debug, lang = "enable_debug_keys", CLIENT)
|
||||
var enableDebugKeys = true
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_near_distance")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_near_distance", SERVER)
|
||||
var despawnerNearDistance = 32f
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_far_distance")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_far_distance", SERVER)
|
||||
var despawnerFarDistance = 96f
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_min_age_ticks")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_min_age_ticks", SERVER)
|
||||
var despawnerMinAgeTicks = 600
|
||||
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_max_age_ticks")
|
||||
@CobblemonConfigField(Category.Spawning, lang = "despawner_max_age_ticks", SERVER)
|
||||
var despawnerMaxAgeTicks = 3600
|
||||
|
||||
fun clone(): CobblemonConfig {
|
||||
|
||||
@@ -13,4 +13,10 @@ package com.cobblemon.mod.common.config
|
||||
annotation class CobblemonConfigField(
|
||||
val category: Category,
|
||||
val lang: String,
|
||||
)
|
||||
val side: CobblemonConfigSide,
|
||||
) {
|
||||
enum class CobblemonConfigSide {
|
||||
CLIENT,
|
||||
SERVER,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ package com.cobblemon.mod.common.config.starter
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.config.Category
|
||||
import com.cobblemon.mod.common.config.LastChangedVersion
|
||||
import com.cobblemon.mod.common.config.CobblemonConfigField
|
||||
import com.cobblemon.mod.common.config.CobblemonConfigField.CobblemonConfigSide.SERVER
|
||||
import com.cobblemon.mod.common.config.LastChangedVersion
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.google.gson.GsonBuilder
|
||||
|
||||
@@ -24,17 +25,17 @@ class StarterConfig {
|
||||
.create()
|
||||
}
|
||||
|
||||
@CobblemonConfigField(Category.Starter, lang = "allow_starter_on_join")
|
||||
@CobblemonConfigField(Category.Starter, lang = "allow_starter_on_join", SERVER)
|
||||
var allowStarterOnJoin = true
|
||||
|
||||
@CobblemonConfigField(Category.Starter, lang = "prompt_starter_once_only")
|
||||
@CobblemonConfigField(Category.Starter, lang = "prompt_starter_once_only", SERVER)
|
||||
@LastChangedVersion("1.5.0")
|
||||
var promptStarterOnceOnly = true
|
||||
|
||||
@CobblemonConfigField(Category.Starter, lang = "use_config_starters")
|
||||
@CobblemonConfigField(Category.Starter, lang = "use_config_starters", SERVER)
|
||||
var useConfigStarters = false
|
||||
|
||||
@CobblemonConfigField(Category.Starter, lang = "starters")
|
||||
@CobblemonConfigField(Category.Starter, lang = "starters", SERVER)
|
||||
var starters = mutableListOf(
|
||||
StarterCategory(
|
||||
name = "Kanto",
|
||||
|
||||
@@ -25,6 +25,7 @@ interface OmniPathingEntity {
|
||||
fun canSwimInLava(): Boolean
|
||||
fun canWalkOnLava(): Boolean
|
||||
fun canSwimUnderFluid(fluidState: FluidState): Boolean
|
||||
fun canPathThroughSaccLeaves(): Boolean
|
||||
fun canFly(): Boolean
|
||||
fun isFlying(): Boolean
|
||||
fun setFlying(state: Boolean)
|
||||
|
||||
@@ -8,13 +8,16 @@
|
||||
|
||||
package com.cobblemon.mod.common.entity.ai
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.entity.OmniPathingEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.ai.OmniPathNodeMaker
|
||||
import com.cobblemon.mod.common.util.deleteNode
|
||||
import com.cobblemon.mod.common.util.getWaterAndLavaIn
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.tags.BlockTags
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
@@ -181,7 +184,9 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
|
||||
target = blockPos
|
||||
}
|
||||
|
||||
val path = if (!this.world.getBlockState(target).isSolid) {
|
||||
val blockState = this.world.getBlockState(target)
|
||||
val path = if (!blockState.isSolid
|
||||
|| (blockState.`is`(BlockTags.LEAVES) && blockState.block == CobblemonBlocks.SACCHARINE_LEAVES && (this.entity as PokemonEntity).canPathThroughSaccLeaves())) {
|
||||
findPath(target, distance)
|
||||
} else {
|
||||
blockPos = target.above()
|
||||
@@ -264,6 +269,9 @@ class OmniPathNavigation(val world: Level, val entity: Mob) : GroundPathNavigati
|
||||
// If we can fly and we're airborne, return the current Y position
|
||||
return vec.y
|
||||
}
|
||||
if (world.getBlockState(blockPos).block == CobblemonBlocks.SACCHARINE_LEAVES && (this.entity as PokemonEntity).canPathThroughSaccLeaves()) {
|
||||
return vec.y + 0.5
|
||||
}
|
||||
return if ((canFloat()) && blockGetter.getFluidState(blockPos).`is`(FluidTags.WATER)) vec.y + 0.5 else super.getGroundY(vec)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.entity.npc
|
||||
import com.cobblemon.mod.common.CobblemonBehaviours
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.config.AddVariablesConfig
|
||||
import com.cobblemon.mod.common.api.ai.config.ApplyBehaviours
|
||||
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
|
||||
import com.cobblemon.mod.common.api.npc.NPCClass
|
||||
@@ -22,6 +23,7 @@ object NPCBrain {
|
||||
if (npcEntity.behavioursAreCustom) {
|
||||
behaviourConfigurations = listOf(ApplyBehaviours().apply { behaviours.addAll(npcEntity.behaviours) })
|
||||
}
|
||||
behaviourConfigurations = behaviourConfigurations + AddVariablesConfig(npcClass.config)
|
||||
|
||||
val ctx = BehaviourConfigurationContext()
|
||||
ctx.addMemories(CobblemonMemories.DIALOGUES)
|
||||
|
||||
@@ -708,6 +708,10 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canPathThroughSaccLeaves(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canFly(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
+2
-2
@@ -393,7 +393,8 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
|
||||
private fun breakFree() {
|
||||
val pokemon = capturingPokemon ?: return
|
||||
pokemon.setPos(position())
|
||||
pokemon.setPositionSafely(position())
|
||||
|
||||
pokemon.beamMode = 2
|
||||
pokemon.isInvisible = false
|
||||
pokemon.isSilent = false
|
||||
@@ -490,7 +491,6 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
|
||||
fun beginCapture() {
|
||||
// We have hit the ground, time to stop falling and start shaking! Calculate capture.
|
||||
capturingPokemon?.setPositionSafely(position())
|
||||
val thrower = owner as LivingEntity
|
||||
val captureResult = Cobblemon.config.captureCalculator.processCapture(thrower, this, capturingPokemon!!).let {
|
||||
val event = PokeBallCaptureCalculatedEvent(thrower = thrower, pokemonEntity = capturingPokemon!!, pokeBallEntity = this, captureResult = it)
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.drop.DropTable
|
||||
import com.cobblemon.mod.common.api.entity.Despawner
|
||||
@@ -165,6 +166,7 @@ import net.minecraft.world.entity.ai.attributes.AttributeSupplier
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes
|
||||
import net.minecraft.world.entity.ai.control.MoveControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
import net.minecraft.world.entity.animal.Animal
|
||||
@@ -1186,6 +1188,7 @@ open class PokemonEntity(
|
||||
if (!this.canRide(player)) return@ifRidingAvailableSupply false
|
||||
if (tethering != null) return@ifRidingAvailableSupply false
|
||||
if (seats.isEmpty()) return@ifRidingAvailableSupply false
|
||||
if ((owner as? ServerPlayer)?.isInBattle() == true) return@ifRidingAvailableSupply false
|
||||
if (this.owner != player && this.passengers.isEmpty()) return@ifRidingAvailableSupply false
|
||||
return@ifRidingAvailableSupply behaviour.isActive(settings, state, this)
|
||||
}
|
||||
@@ -1244,11 +1247,19 @@ open class PokemonEntity(
|
||||
}
|
||||
|
||||
override fun checkDespawn() {
|
||||
if (pokemon.getOwnerUUID() == null && !isPersistenceRequired && (!this.pokemon.canDropHeldItem || this.pokemon.heldItem.isEmpty) && despawner.shouldDespawn(this) ) {
|
||||
if (pokemon.getOwnerUUID() == null && !isPersistenceRequired && despawner.shouldDespawn(this) ) {
|
||||
discard()
|
||||
}
|
||||
}
|
||||
|
||||
override fun isPersistenceRequired(): Boolean {
|
||||
return super.isPersistenceRequired()
|
||||
|| (this.pokemon.canDropHeldItem && !this.pokemon.heldItem.isEmpty)
|
||||
|| this.brain.checkMemory(CobblemonMemories.HIVE_LOCATION, MemoryStatus.VALUE_PRESENT)
|
||||
|| this.brain.checkMemory(CobblemonMemories.HIVE_COOLDOWN, MemoryStatus.VALUE_PRESENT)
|
||||
|| this.brain.checkMemory(CobblemonMemories.NEARBY_SACC_LEAVES, MemoryStatus.VALUE_PRESENT)
|
||||
}
|
||||
|
||||
fun setBehaviourFlag(flag: PokemonBehaviourFlag, on: Boolean) {
|
||||
entityData.set(BEHAVIOUR_FLAGS, setBitForByte(entityData.get(BEHAVIOUR_FLAGS), flag.bit, on))
|
||||
}
|
||||
@@ -2177,9 +2188,19 @@ open class PokemonEntity(
|
||||
}
|
||||
|
||||
this.yRotO = this.yRot
|
||||
val rotation = behaviour.rotation(settings, state, this, driver)
|
||||
|
||||
setRot(rotation.y, rotation.x)
|
||||
if (this is OrientationControllable) {
|
||||
val controller = this.orientationController
|
||||
if (!controller.isActive()) {
|
||||
val rotation = behaviour.rotation(settings, state, this, driver)
|
||||
// TODO: Find a better solution than setting the vehicle xrot to zero.
|
||||
// The problem is that nothing actually effects the vehicle/pokemon xrot so when it gets set to
|
||||
// -45 degrees by a rollable ride controller it gets saved off and not modified until you try
|
||||
// and takeoff again. And at that point you snap to -45 pitch in the orientationControllers matrix
|
||||
setRot(rotation.y, 0.0f)
|
||||
}
|
||||
}
|
||||
|
||||
this.yHeadRot = this.yRot
|
||||
this.yBodyRot = this.yRot
|
||||
this.passengers.filterIsInstance<LivingEntity>()
|
||||
@@ -2300,6 +2321,12 @@ open class PokemonEntity(
|
||||
return loc.compareTo(resourceLocation) == 0
|
||||
}
|
||||
|
||||
fun applyRenderRotation(partialTicks: Float) {
|
||||
ifRidingAvailable { behaviour, settings, state ->
|
||||
behaviour.applyRenderRotation(settings, state, this, partialTicks)
|
||||
}
|
||||
}
|
||||
|
||||
var jumpInputStrength: Int = 0 // move this
|
||||
override fun onPlayerJump(strength: Int) {
|
||||
// See if this controls the hot bar element
|
||||
@@ -2430,6 +2457,7 @@ open class PokemonEntity(
|
||||
override fun canWalkOnWater() = exposedForm.behaviour.moving.swim.canWalkOnWater
|
||||
override fun canFly() = exposedForm.behaviour.moving.fly.canFly
|
||||
override fun canSwimInLava() = exposedForm.behaviour.moving.swim.canSwimInLava
|
||||
override fun canPathThroughSaccLeaves() = this.config.getMap().getOrDefault("can_path_through_sacc_leaves", DoubleValue.ZERO).asDouble() == 1.0
|
||||
override fun canWalkOnLava() = exposedForm.behaviour.moving.swim.canWalkOnLava
|
||||
override fun entityOnGround() = onGround()
|
||||
|
||||
|
||||
+50
-40
@@ -9,17 +9,22 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.ai.sensors
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.PoiTypeTags
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor
|
||||
import net.minecraft.world.level.block.BeehiveBlock
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiRecord
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
|
||||
|
||||
class BeeHiveSensor : Sensor<PokemonEntity>(300) {
|
||||
override fun requires() = setOf(CobblemonMemories.HIVE_LOCATION)
|
||||
override fun requires() = setOf(CobblemonMemories.HIVE_LOCATION, CobblemonMemories.HIVE_BLACKLIST)
|
||||
override fun doTick(world: ServerLevel, entity: PokemonEntity) {
|
||||
val brain = entity.brain
|
||||
val currentHive = brain.getMemory(CobblemonMemories.HIVE_LOCATION).orElse(null)
|
||||
@@ -27,35 +32,30 @@ class BeeHiveSensor : Sensor<PokemonEntity>(300) {
|
||||
if (currentHive != null) {
|
||||
val state = world.getBlockState(currentHive)
|
||||
|
||||
if (!isValidHiveOrLeaf(state) || !hasReachableAdjacentSide(world, entity, currentHive)) {
|
||||
brain.eraseMemory(CobblemonMemories.HIVE_LOCATION)
|
||||
} else if (isAtMaxHoney(state)) {
|
||||
if (!isValidHive(state) || !hasReachableAdjacentSide(world, currentHive)) {
|
||||
brain.eraseMemory(CobblemonMemories.HIVE_LOCATION)
|
||||
} else {
|
||||
return // We already have a valid hive, no need to search for another
|
||||
}
|
||||
}
|
||||
|
||||
// Search for nearest hive/nest
|
||||
val searchRadius = 16 // is this too big for us to use?
|
||||
val centerPos = entity.blockPosition()
|
||||
|
||||
var closestHivePos: BlockPos? = null
|
||||
var closestDistance = Double.MAX_VALUE
|
||||
|
||||
BlockPos.betweenClosedStream(
|
||||
centerPos.offset(-searchRadius, -2, -searchRadius),
|
||||
centerPos.offset(searchRadius, 2, searchRadius)
|
||||
).forEach { pos ->
|
||||
val state = world.getBlockState(pos)
|
||||
val list = findNearbyHivesWithSpace(entity)
|
||||
|
||||
if (isValidHiveOrLeaf(state) && !isAtMaxHoney(state) && hasReachableAdjacentSide(world, entity, pos)) {
|
||||
val distance = pos.distToCenterSqr(centerPos.x + 0.5, centerPos.y + 0.5, centerPos.z + 0.5)
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance
|
||||
closestHivePos = pos.immutable()
|
||||
if (!list.isEmpty()) {
|
||||
val blackList = brain.getMemory(CobblemonMemories.HIVE_BLACKLIST).orElse(emptyList())
|
||||
for (blockPos in list) {
|
||||
if (blackList.contains(blockPos)) continue
|
||||
if (hasReachableAdjacentSide(world, blockPos)) {
|
||||
closestHivePos = blockPos
|
||||
break
|
||||
}
|
||||
}
|
||||
if (closestHivePos == null) {
|
||||
brain.eraseMemory(CobblemonMemories.HIVE_BLACKLIST)
|
||||
closestHivePos = list.first()
|
||||
}
|
||||
}
|
||||
|
||||
if (closestHivePos != null) {
|
||||
@@ -63,37 +63,47 @@ class BeeHiveSensor : Sensor<PokemonEntity>(300) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAtMaxHoney(state: net.minecraft.world.level.block.state.BlockState): Boolean {
|
||||
return when {
|
||||
isHiveBlock(state) -> state.getValue(BeehiveBlock.HONEY_LEVEL) == BeehiveBlock.MAX_HONEY_LEVELS
|
||||
isSaccharineLeafBlock(state) -> state.getValue(SaccharineLeafBlock.AGE) == SaccharineLeafBlock.MAX_AGE
|
||||
else -> true // Unknown block type
|
||||
}
|
||||
private fun isValidHive(state: net.minecraft.world.level.block.state.BlockState): Boolean {
|
||||
return isHiveBlock(state)
|
||||
}
|
||||
|
||||
private fun isValidHiveOrLeaf(state: net.minecraft.world.level.block.state.BlockState): Boolean {
|
||||
return isHiveBlock(state) || isSaccharineLeafBlock(state)
|
||||
}
|
||||
|
||||
private fun isSaccharineLeafBlock(state: net.minecraft.world.level.block.state.BlockState): Boolean {
|
||||
return state.block is SaccharineLeafBlock
|
||||
}
|
||||
|
||||
private fun isHiveBlock(state: net.minecraft.world.level.block.state.BlockState): Boolean {
|
||||
return state.`is`(Blocks.BEEHIVE) || state.`is`(Blocks.BEE_NEST)
|
||||
}
|
||||
|
||||
private fun hasReachableAdjacentSide(world: ServerLevel, entity: PokemonEntity, pos: BlockPos): Boolean {
|
||||
private fun hasReachableAdjacentSide(world: ServerLevel, pos: BlockPos): Boolean {
|
||||
for (dir in Direction.entries) {
|
||||
val adjacentPos = pos.relative(dir)
|
||||
if (world.isEmptyBlock(adjacentPos)) {
|
||||
val nav = entity.navigation
|
||||
val path = nav.createPath(adjacentPos, 0)
|
||||
if (path != null && path.canReach()) {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun findNearbyHivesWithSpace(entity: PokemonEntity): List<BlockPos> {
|
||||
val blockPos = entity.blockPosition()
|
||||
val poiManager = (entity.level() as ServerLevel).poiManager
|
||||
val stream = poiManager.getInRange(
|
||||
{ holder -> holder.`is`(PoiTypeTags.BEE_HOME) },
|
||||
blockPos,
|
||||
20,
|
||||
PoiManager.Occupancy.ANY
|
||||
)
|
||||
return stream
|
||||
.map(PoiRecord::getPos)
|
||||
.filter { pos -> doesHiveHaveSpace(pos, entity.level()) }
|
||||
.sorted { pos1, pos2 -> pos1.distSqr(blockPos).toInt() - pos2.distSqr(blockPos).toInt() }
|
||||
.toList()
|
||||
}
|
||||
|
||||
private fun doesHiveHaveSpace(hivePos: BlockPos, world: Level): Boolean {
|
||||
val blockEntity: BlockEntity? = world.getBlockEntity(hivePos)
|
||||
if (blockEntity is BeehiveBlockEntity) {
|
||||
return !blockEntity.isFull
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
-16
@@ -10,46 +10,73 @@ package com.cobblemon.mod.common.entity.pokemon.ai.sensors
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.distanceTo
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.BlockTags
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.DoublePlantBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf
|
||||
|
||||
class FlowerSensor : Sensor<PokemonEntity>(120) {
|
||||
override fun requires() = setOf(CobblemonMemories.NEARBY_FLOWERS)
|
||||
|
||||
override fun requires() = setOf(CobblemonMemories.NEARBY_FLOWER)
|
||||
|
||||
override fun doTick(world: ServerLevel, entity: PokemonEntity) {
|
||||
val brain = entity.brain
|
||||
|
||||
val searchRadius = 32
|
||||
val centerPos = entity.blockPosition()
|
||||
val flowers = mutableListOf<BlockPos>()
|
||||
// Check curr memory
|
||||
val currPos = brain.getMemorySafely(CobblemonMemories.NEARBY_FLOWER).orElse(null)
|
||||
if (currPos != null && isFlower(entity.level().getBlockState(currPos)) && entity.distanceTo(currPos) <= 32) {
|
||||
return
|
||||
}
|
||||
|
||||
val searchRadius = 5
|
||||
val centerPos = entity.blockPosition()
|
||||
var flowerPos: BlockPos? = null
|
||||
var shortestDist = Double.MAX_VALUE
|
||||
BlockPos.betweenClosedStream(
|
||||
centerPos.offset(-searchRadius, -2, -searchRadius),
|
||||
centerPos.offset(searchRadius, 2, searchRadius)
|
||||
).forEach { pos ->
|
||||
val state = world.getBlockState(pos)
|
||||
if (isFlower(state) && isPathfindableTo(entity, pos)) {
|
||||
flowers += pos.immutable()
|
||||
if (isFlower(state)) {
|
||||
val distance = pos.distSqr(Vec3i(centerPos.x, centerPos.y, centerPos.z))
|
||||
if (distance < shortestDist) {
|
||||
flowerPos = BlockPos(pos)
|
||||
shortestDist = distance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flowers.isEmpty()) {
|
||||
brain.setMemory(CobblemonMemories.NEARBY_FLOWERS, flowers)
|
||||
|
||||
if (flowerPos != null) {
|
||||
brain.setMemory(CobblemonMemories.NEARBY_FLOWER, flowerPos)
|
||||
} else {
|
||||
brain.eraseMemory(CobblemonMemories.NEARBY_FLOWERS)
|
||||
brain.eraseMemory(CobblemonMemories.NEARBY_FLOWER)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isFlower(state: BlockState): Boolean {
|
||||
return state.`is`(BlockTags.FLOWERS)
|
||||
}
|
||||
|
||||
private fun isPathfindableTo(entity: PokemonEntity, pos: BlockPos): Boolean {
|
||||
val nav = entity.navigation
|
||||
val path = nav.createPath(pos, 0)
|
||||
return path != null && path.canReach()
|
||||
// Borrowed from Bee.class
|
||||
if (state.hasProperty(BlockStateProperties.WATERLOGGED) && state.getValue(
|
||||
BlockStateProperties.WATERLOGGED
|
||||
) as Boolean
|
||||
) {
|
||||
return false
|
||||
} else if (state.`is`(BlockTags.FLOWERS)) {
|
||||
if (state.`is`(Blocks.SUNFLOWER)) {
|
||||
return state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.entity.pokemon.ai.sensors
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.distanceTo
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Vec3i
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import kotlin.time.measureTime
|
||||
|
||||
class SacLeavesSensor : Sensor<PokemonEntity>(120) {
|
||||
|
||||
override fun requires() = setOf(CobblemonMemories.NEARBY_SACC_LEAVES)
|
||||
|
||||
override fun doTick(world: ServerLevel, entity: PokemonEntity) {
|
||||
val brain = entity.brain
|
||||
|
||||
val searchRadius = 7
|
||||
val tooFarDistance = 30
|
||||
val wayTooFarDistance = 64
|
||||
val currPos: BlockPos? = brain.getMemorySafely(CobblemonMemories.NEARBY_SACC_LEAVES).orElse(null)
|
||||
|
||||
if (currPos != null && isValidLeafBlock(entity.level().getBlockState(currPos))) {
|
||||
val distance = entity.distanceTo(currPos)
|
||||
if (distance <= tooFarDistance) {
|
||||
return
|
||||
} else if (distance > wayTooFarDistance) {
|
||||
brain.eraseMemory(CobblemonMemories.NEARBY_SACC_LEAVES)
|
||||
}
|
||||
}
|
||||
|
||||
val centerPos = entity.blockPosition()
|
||||
var leavesPos: BlockPos? = null
|
||||
var shortestDist = Double.MAX_VALUE
|
||||
BlockPos.betweenClosedStream(
|
||||
centerPos.offset(-searchRadius, -2, -searchRadius),
|
||||
centerPos.offset(searchRadius, searchRadius, searchRadius)
|
||||
).forEach { pos ->
|
||||
val state = world.getBlockState(pos)
|
||||
if (isValidLeafBlock(state)) {
|
||||
val distance = pos.distSqr(Vec3i(centerPos.x, centerPos.y, centerPos.z))
|
||||
if (distance < shortestDist) {
|
||||
leavesPos = BlockPos(pos)
|
||||
shortestDist = distance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (leavesPos != null) {
|
||||
brain.setMemory(CobblemonMemories.NEARBY_SACC_LEAVES, leavesPos)
|
||||
} else {
|
||||
brain.eraseMemory(CobblemonMemories.NEARBY_SACC_LEAVES)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValidLeafBlock(state: BlockState): Boolean {
|
||||
if (state.block !is SaccharineLeafBlock) return false
|
||||
if (state.hasProperty(BlockStateProperties.WATERLOGGED) && state.getValue(
|
||||
BlockStateProperties.WATERLOGGED
|
||||
) as Boolean
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return state.getValue(SaccharineLeafBlock.AGE) != SaccharineLeafBlock.MAX_AGE
|
||||
}
|
||||
}
|
||||
-77
@@ -1,77 +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.entity.pokemon.ai.tasks
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object PathToBeeHiveTask {
|
||||
|
||||
fun create(): OneShot<in LivingEntity> {
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.registered(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.POLLINATED),
|
||||
it.present(CobblemonMemories.HIVE_LOCATION),
|
||||
it.absent(CobblemonMemories.HIVE_COOLDOWN)
|
||||
).apply(it) { lookTarget, walkTarget, pollinated, hiveMemory, hiveCooldown ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
val hiveLocation = it.get(hiveMemory)
|
||||
val sidesByClosest = listOf(
|
||||
hiveLocation.north(),
|
||||
hiveLocation.south(),
|
||||
hiveLocation.east(),
|
||||
hiveLocation.west()
|
||||
).sortedBy { it.distSqr(entity.blockPosition()) }
|
||||
|
||||
var openSide = BlockPos.ZERO
|
||||
for (side in sidesByClosest) {
|
||||
if (world.getBlockState(side).isAir) {
|
||||
openSide = side
|
||||
break
|
||||
}
|
||||
}
|
||||
if (openSide == BlockPos.ZERO) {
|
||||
if (world.getBlockState(hiveLocation.above()).isAir) {
|
||||
// If the above position is also air, we can use it
|
||||
openSide = hiveLocation.above() // Fallback to above if no open sides found
|
||||
} else if (world.getBlockState(hiveLocation.below()).isAir) {
|
||||
// If the below position is also air, we can use it
|
||||
openSide = hiveLocation.below() // Fallback to below if no open sides found
|
||||
} else {
|
||||
// If no open sides or above/below positions are found, we cannot proceed
|
||||
return@Trigger false
|
||||
}
|
||||
}
|
||||
val targetVec = Vec3.atCenterOf(openSide)
|
||||
|
||||
// Set path target toward hive
|
||||
walkTarget.set(WalkTarget(targetVec, 0.35F, 0))
|
||||
lookTarget.set(BlockPosTracker(targetVec.add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-16
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.ai.tasks
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
@@ -20,31 +21,26 @@ import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object PathToFlowerTask {
|
||||
|
||||
|
||||
fun create(): OneShot<in LivingEntity> {
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.registered(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.NEARBY_FLOWERS),
|
||||
it.absent(CobblemonMemories.POLLINATED),
|
||||
it.present(CobblemonMemories.NEARBY_FLOWER),
|
||||
it.registered(CobblemonMemories.HAS_NECTAR),
|
||||
it.absent(CobblemonMemories.PATH_TO_NEARBY_FLOWER_COOLDOWN),
|
||||
it.absent(CobblemonMemories.HIVE_COOLDOWN)
|
||||
).apply(it) { lookTarget, walkTarget, flowerMemory, pollinated, hiveCooldown ->
|
||||
).apply(it) { lookTarget, walkTarget, flowerMemory, pollinated, flowerCooldown, hiveCooldown ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
val flowerLocations = it.get(flowerMemory).map(Vec3::atCenterOf)
|
||||
if (flowerLocations.isEmpty()) {
|
||||
return@Trigger false
|
||||
}
|
||||
val sortedByDistance = flowerLocations.sortedBy { it.distanceTo(entity.position()) }
|
||||
val closestHalf = sortedByDistance.subList(0, flowerLocations.size / 2 + 1)
|
||||
if (closestHalf.isEmpty()) {
|
||||
return@Trigger false
|
||||
}
|
||||
val targetVec = closestHalf.random()
|
||||
if (entity !is PathfinderMob || !entity.isAlive || world.isNight || world.isRaining || entity.brain.getMemorySafely(CobblemonMemories.HAS_NECTAR).orElse(false)) return@Trigger false
|
||||
|
||||
val flowerLocation = it.get(flowerMemory)
|
||||
val targetVec = Vec3.atCenterOf(flowerLocation)
|
||||
|
||||
// if we're really close to one then forget it, pollination is gonna occur
|
||||
if (flowerLocations.any { it.distanceToSqr(entity.x, entity.y, entity.z) <= 1.0 }) {
|
||||
if (targetVec.distanceToSqr(entity.x, entity.y, entity.z) <= 1.0 ) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
|
||||
+15
-14
@@ -10,33 +10,34 @@ package com.cobblemon.mod.common.entity.pokemon.ai.tasks
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.entity.ai.memory.WalkTarget
|
||||
|
||||
object PathToSacLeafTask {
|
||||
|
||||
|
||||
object PollinateFlowerTask {
|
||||
fun create(): OneShot<in LivingEntity> {
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(CobblemonMemories.NEARBY_FLOWERS),
|
||||
it.registered(MemoryModuleType.LOOK_TARGET),
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.absent(CobblemonMemories.POLLINATED),
|
||||
it.absent(CobblemonMemories.HIVE_COOLDOWN)
|
||||
).apply(it) { flowerMemory, walkTarget, pollinated, hiveCooldown ->
|
||||
it.present(CobblemonMemories.HAS_NECTAR),
|
||||
it.present(CobblemonMemories.NEARBY_SACC_LEAVES)
|
||||
).apply(it) { lookTarget, walkTarget, pollinated, saccLeavesPos ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob || !entity.isAlive) return@Trigger false
|
||||
|
||||
val flowerLocations = it.get(flowerMemory)
|
||||
if (flowerLocations.any { it.distSqr(entity.blockPosition()) < 1 }) {
|
||||
entity.brain.setMemoryWithExpiry(CobblemonMemories.POLLINATED, true, 20 * 20L) // 20 seconds to dump the pollen
|
||||
if (entity is PokemonEntity) {
|
||||
entity.pokemon.updateAspects()
|
||||
}
|
||||
if (entity !is PathfinderMob || !entity.isAlive || entity !is PokemonEntity || !it.get(pollinated) || world.isRaining || world.isNight) {
|
||||
return@Trigger false
|
||||
}
|
||||
val targetPos = it.get(saccLeavesPos)
|
||||
walkTarget.set(WalkTarget(targetPos, 0.35F, 0))
|
||||
lookTarget.set(BlockPosTracker(targetPos.toVec3d().add(0.0, entity.eyeHeight.toDouble(), 0.0)))
|
||||
|
||||
return@Trigger true
|
||||
}
|
||||
+26
-19
@@ -9,31 +9,33 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.ai.tasks
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
|
||||
import net.minecraft.world.entity.ai.behavior.declarative.Trigger
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.level.block.BeehiveBlock
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object PlaceHoneyInHiveTask {
|
||||
fun create(): OneShot<in LivingEntity> {
|
||||
|
||||
fun create(): OneShot<PokemonEntity> {
|
||||
return BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.absent(MemoryModuleType.WALK_TARGET),
|
||||
it.present(CobblemonMemories.POLLINATED),
|
||||
it.registered(CobblemonMemories.HAS_NECTAR),
|
||||
it.present(CobblemonMemories.HIVE_LOCATION),
|
||||
it.absent(CobblemonMemories.HIVE_COOLDOWN)
|
||||
it.absent(CobblemonMemories.HIVE_COOLDOWN),
|
||||
).apply(it) { walkTarget, pollinated, hiveMemory, hiveCooldown ->
|
||||
Trigger { world, entity, time ->
|
||||
if (entity !is PathfinderMob) {
|
||||
if (entity !is PathfinderMob || !wantsToEnterHive(entity)) {
|
||||
return@Trigger false
|
||||
}
|
||||
|
||||
val hiveCooldown = 1200L
|
||||
val hiveLocation = it.get(hiveMemory)
|
||||
val targetVec = Vec3.atCenterOf(hiveLocation)
|
||||
|
||||
@@ -45,19 +47,13 @@ object PlaceHoneyInHiveTask {
|
||||
val state = world.getBlockState(hiveLocation)
|
||||
val block = state.block
|
||||
if (block is BeehiveBlock) {
|
||||
val currentLevel = state.getValue(BeehiveBlock.HONEY_LEVEL)
|
||||
if (currentLevel < BeehiveBlock.MAX_HONEY_LEVELS) {
|
||||
world.setBlock(hiveLocation, state.setValue(BeehiveBlock.HONEY_LEVEL, currentLevel + 1), 3)
|
||||
entity.brain.setMemoryWithExpiry(CobblemonMemories.HIVE_COOLDOWN, true, hiveCooldown)
|
||||
entity.brain.eraseMemory(CobblemonMemories.POLLINATED)
|
||||
}
|
||||
} else if (block is SaccharineLeafBlock) {
|
||||
val currentAge = state.getValue(SaccharineLeafBlock.AGE)
|
||||
if (currentAge < SaccharineLeafBlock.MAX_AGE) {
|
||||
world.setBlock(hiveLocation, state.setValue(SaccharineLeafBlock.AGE, currentAge + 1), 3)
|
||||
entity.brain.setMemoryWithExpiry(CobblemonMemories.HIVE_COOLDOWN, true, hiveCooldown)
|
||||
entity.brain.eraseMemory(CobblemonMemories.POLLINATED)
|
||||
val blockEntity = world.getBlockEntity(hiveLocation)
|
||||
if (blockEntity !is BeehiveBlockEntity) return@Trigger false
|
||||
if (blockEntity.isFull) {
|
||||
// Erase Hive memory and give the bee a moment to locate a new hive
|
||||
entity.brain.eraseMemory(CobblemonMemories.HIVE_LOCATION)
|
||||
}
|
||||
blockEntity.addOccupant(entity)
|
||||
}
|
||||
|
||||
return@Trigger true
|
||||
@@ -65,4 +61,15 @@ object PlaceHoneyInHiveTask {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun wantsToEnterHive( entity: PokemonEntity) : Boolean {
|
||||
val result = !entity.brain.checkMemory(CobblemonMemories.HIVE_COOLDOWN, MemoryStatus.VALUE_PRESENT)
|
||||
&& (entity.level().isRaining || entity.level().isNight || entity.brain.getMemory(CobblemonMemories.HAS_NECTAR).orElse(false))
|
||||
if (result) {
|
||||
val blockPos = entity.brain.getMemorySafely(CobblemonMemories.HIVE_LOCATION).orElse(null)
|
||||
val blockEntity = entity.level().getBlockEntity(blockPos)
|
||||
return blockEntity is BeehiveBlockEntity && !blockEntity.isFireNearby
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -102,13 +102,11 @@ class AprijuiceItem(val type: Apricorn): CobblemonItem(Properties().stacksTo(16)
|
||||
if (!canUseOnPokemon(stack, pokemon)) {
|
||||
return InteractionResultHolder.fail(stack)
|
||||
}
|
||||
val boosts = getBoosts(stack, pokemon)
|
||||
// Feed the Pokémon 1 fullness point
|
||||
pokemon.feedPokemon(1)
|
||||
|
||||
boosts.forEach { (stat, value) ->
|
||||
pokemon.addRideBoost(stat, value)
|
||||
}
|
||||
pokemon.feedPokemon(1)
|
||||
|
||||
val boosts = getBoosts(stack, pokemon)
|
||||
pokemon.addRideBoosts(boosts)
|
||||
|
||||
stack.consume(1, player)
|
||||
|
||||
|
||||
+7
-2
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinSettings
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.SubmarineSettings
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
@@ -39,7 +40,8 @@ class RideSettingsSyncPacket(
|
||||
val vehicle: VehicleSettings,
|
||||
val boat: BoatSettings,
|
||||
val burst: BurstSettings,
|
||||
val dolphin: DolphinSettings
|
||||
val dolphin: DolphinSettings,
|
||||
val submarine: SubmarineSettings
|
||||
) : NetworkPacket<RideSettingsSyncPacket> {
|
||||
override val id = ID
|
||||
|
||||
@@ -56,6 +58,7 @@ class RideSettingsSyncPacket(
|
||||
boat.encode(buffer)
|
||||
burst.encode(buffer)
|
||||
dolphin.encode(buffer)
|
||||
submarine.encode(buffer)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -73,7 +76,8 @@ class RideSettingsSyncPacket(
|
||||
vehicle = VehicleSettings(),
|
||||
boat = BoatSettings(),
|
||||
burst = BurstSettings(),
|
||||
dolphin = DolphinSettings()
|
||||
dolphin = DolphinSettings(),
|
||||
submarine = SubmarineSettings()
|
||||
).apply {
|
||||
bird.decode(buffer)
|
||||
glider.decode(buffer)
|
||||
@@ -87,6 +91,7 @@ class RideSettingsSyncPacket(
|
||||
boat.decode(buffer)
|
||||
burst.decode(buffer)
|
||||
dolphin.decode(buffer)
|
||||
submarine.decode(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -15,9 +15,13 @@ import com.cobblemon.mod.common.util.writeMatrix3f
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import org.joml.Matrix3f
|
||||
|
||||
class ServerboundUpdateOrientationPacket internal constructor(val orientation: Matrix3f?) : NetworkPacket<ServerboundUpdateOrientationPacket> {
|
||||
class ServerboundUpdateOrientationPacket internal constructor(
|
||||
val entity: Int,
|
||||
val orientation: Matrix3f?
|
||||
) : NetworkPacket<ServerboundUpdateOrientationPacket> {
|
||||
override val id = ID
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeInt(entity)
|
||||
buffer.writeBoolean(orientation != null)
|
||||
if (orientation != null) {
|
||||
buffer.writeMatrix3f(orientation)
|
||||
@@ -27,8 +31,9 @@ class ServerboundUpdateOrientationPacket internal constructor(val orientation: M
|
||||
companion object {
|
||||
val ID = cobblemonResource("c2s_update_orientation")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): ServerboundUpdateOrientationPacket {
|
||||
val entity = buffer.readInt()
|
||||
val orientation = if (buffer.readBoolean()) buffer.readMatrix3f() else null
|
||||
return ServerboundUpdateOrientationPacket(orientation)
|
||||
return ServerboundUpdateOrientationPacket(entity, orientation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class ServerboundUpdateRiderRotationPacket internal constructor(
|
||||
val riderXRot: Float,
|
||||
val riderYRot: Float,
|
||||
val rideEyePos: Vec3
|
||||
) : NetworkPacket<ServerboundUpdateRiderRotationPacket> {
|
||||
override val id = ID
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeFloat(riderXRot)
|
||||
buffer.writeFloat(riderYRot)
|
||||
buffer.writeVec3(rideEyePos)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("c2s_update_rider_rotation")
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): ServerboundUpdateRiderRotationPacket {
|
||||
val riderXRot = buffer.readFloat()
|
||||
val riderYRot = buffer.readFloat()
|
||||
val rideEyePos = buffer.readVec3()
|
||||
return ServerboundUpdateRiderRotationPacket(riderXRot, riderYRot, rideEyePos)
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -10,15 +10,19 @@ package com.cobblemon.mod.common.net.serverhandling.orientation
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import kotlin.math.atan2
|
||||
|
||||
object OrientationPacketHandler : ServerNetworkPacketHandler<ServerboundUpdateOrientationPacket> {
|
||||
override fun handle(packet: ServerboundUpdateOrientationPacket, server: MinecraftServer, player: ServerPlayer) {
|
||||
if (player is OrientationControllable) {
|
||||
player.orientationController.updateOrientation { _ -> packet.orientation }
|
||||
}
|
||||
val entity = player.level().getEntity(packet.entity) ?: return
|
||||
|
||||
if (entity !is PokemonEntity) return
|
||||
if (entity.controllingPassenger != player) return
|
||||
if (entity !is OrientationControllable) return
|
||||
|
||||
entity.orientationController.updateOrientation { _ -> packet.orientation }
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.RidePassenger
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateRiderRotationPacket
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
|
||||
object ServerboundUpdateRiderRotationHandler : ServerNetworkPacketHandler<ServerboundUpdateRiderRotationPacket> {
|
||||
|
||||
override fun handle(
|
||||
packet: ServerboundUpdateRiderRotationPacket,
|
||||
server: MinecraftServer,
|
||||
player: ServerPlayer
|
||||
) {
|
||||
if (player is RidePassenger) {
|
||||
player.`cobblemon$setRideXRot`(packet.riderXRot)
|
||||
player.`cobblemon$setRideYRot`(packet.riderYRot)
|
||||
player.`cobblemon$setRideEyePos`(packet.rideEyePos)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1867,19 +1867,40 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
}
|
||||
val max = getMaxRideBoost(stat)
|
||||
rideBoosts[stat] = (getRideBoost(stat) + boostAmount).coerceAtMost(max)
|
||||
onChange(RideBoostsUpdatePacket({ this }, rideBoosts))
|
||||
onChange(RideBoostsUpdatePacket({ this }, getRideBoosts()))
|
||||
return true
|
||||
}
|
||||
|
||||
fun addRideBoosts(boosts: Map<RidingStat, Float>) {
|
||||
var changed = false
|
||||
|
||||
for (boost in boosts) {
|
||||
val (stat, boostAmount) = boost
|
||||
|
||||
if (!canAddRideBoost(stat)) {
|
||||
continue
|
||||
}
|
||||
|
||||
val max = getMaxRideBoost(stat)
|
||||
rideBoosts[stat] = (getRideBoost(stat) + boostAmount).coerceAtMost(max)
|
||||
|
||||
changed = true
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
onChange(RideBoostsUpdatePacket({ this }, getRideBoosts()))
|
||||
}
|
||||
}
|
||||
|
||||
fun setRideBoost(stat: RidingStat, boost: Float) {
|
||||
rideBoosts[stat] = boost.coerceIn(0F, getMaxRideBoost(stat))
|
||||
onChange(RideBoostsUpdatePacket({ this }, rideBoosts))
|
||||
onChange(RideBoostsUpdatePacket({ this }, getRideBoosts()))
|
||||
}
|
||||
|
||||
fun setRideBoosts(boosts: Map<RidingStat, Float>) {
|
||||
rideBoosts.clear()
|
||||
rideBoosts.putAll(boosts.mapValues { it.value.coerceIn(0F, getMaxRideBoost(it.key)) })
|
||||
onChange(RideBoostsUpdatePacket({ this }, rideBoosts))
|
||||
onChange(RideBoostsUpdatePacket({ this }, getRideBoosts()))
|
||||
}
|
||||
|
||||
fun getExperienceToNextLevel() = getExperienceToLevel(level + 1)
|
||||
|
||||
@@ -14,10 +14,11 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
class BlockBehavior {
|
||||
private val immuneToSweetBerryBushBlock = false
|
||||
private val canStandOnPowderSnow = false
|
||||
|
||||
val canPathThroughSaccLeaves = false
|
||||
@Transient
|
||||
val struct = ObjectValue(this).also {
|
||||
it.addFunction("immune_to_sweet_berry_bush_block") { DoubleValue(immuneToSweetBerryBushBlock) }
|
||||
it.addFunction("can_stand_on_powder_snow") { DoubleValue(canStandOnPowderSnow) }
|
||||
it.addFunction("can_path_through_sacc_leaves") { DoubleValue(canPathThroughSaccLeaves) }
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
|
||||
class CombatBehaviour {
|
||||
var willDefendSelf = false
|
||||
var willDefendSelf = true
|
||||
var willFlee = true
|
||||
var willDefendOwner = false
|
||||
var fightsMelee = true
|
||||
|
||||
@@ -8,15 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.pokemon.ai
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.entity.OmniPathingEntity
|
||||
import com.cobblemon.mod.common.util.canFit
|
||||
import com.google.common.collect.Maps
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
||||
import java.util.EnumSet
|
||||
import java.util.function.Predicate
|
||||
import kotlin.math.max
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.tags.BlockTags
|
||||
@@ -29,15 +27,14 @@ import net.minecraft.world.level.block.BaseRailBlock
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.FenceGateBlock
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.pathfinder.Node
|
||||
import net.minecraft.world.level.pathfinder.NodeEvaluator
|
||||
import net.minecraft.world.level.pathfinder.PathComputationType
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.level.pathfinder.PathfindingContext
|
||||
import net.minecraft.world.level.pathfinder.*
|
||||
import net.minecraft.world.level.pathfinder.Target
|
||||
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
import kotlin.math.max
|
||||
|
||||
|
||||
/**
|
||||
* A path node maker that constructs paths knowing that the entity might be capable of
|
||||
@@ -88,6 +85,7 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
Long2ObjectFunction<PathType?> { this.getPathTypeOfMob(currentContext, x, y, z, entity) }
|
||||
)
|
||||
}
|
||||
|
||||
fun doesBlockHavePartialCollision(pathType: PathType): Boolean {
|
||||
return pathType == PathType.FENCE || pathType == PathType.DOOR_WOOD_CLOSED || pathType == PathType.DOOR_IRON_CLOSED
|
||||
}
|
||||
@@ -96,7 +94,15 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
return max(1.125, mob.maxUpStep().toDouble())
|
||||
}
|
||||
|
||||
protected fun findAcceptedNodeWalk(x: Int, y: Int, z: Int, verticalDeltaLimit: Int, nodeFloorLevel: Double, direction: Direction?, pathType: PathType?): Node? {
|
||||
protected fun findAcceptedNodeWalk(
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
verticalDeltaLimit: Int,
|
||||
nodeFloorLevel: Double,
|
||||
direction: Direction?,
|
||||
pathType: PathType?
|
||||
): Node? {
|
||||
var node: Node? = null
|
||||
val mutableBlockPos = BlockPos.MutableBlockPos()
|
||||
val d: Double = this.getFloorLevel(mutableBlockPos.set(x, y, z))
|
||||
@@ -113,12 +119,23 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
if (f >= 0.0f) {
|
||||
node = this.getNodeAndUpdateCostToMax(x, y, z, pathType2, f)
|
||||
}
|
||||
if ( pathType?.let { doesBlockHavePartialCollision(it) } == true && node != null && node.costMalus >= 0.0f && !this.canReachWithoutCollision(node)) {
|
||||
if (pathType?.let { doesBlockHavePartialCollision(it) } == true && node != null && node.costMalus >= 0.0f && !this.canReachWithoutCollision(
|
||||
node
|
||||
)) {
|
||||
node = null
|
||||
}
|
||||
if (pathType2 != PathType.WALKABLE ) {
|
||||
if (pathType2 != PathType.WALKABLE) {
|
||||
if ((node == null || node.costMalus < 0.0f) && verticalDeltaLimit > 0 && (pathType2 != PathType.FENCE || canWalkOverFences()) && pathType2 != PathType.UNPASSABLE_RAIL && pathType2 != PathType.TRAPDOOR && pathType2 != PathType.POWDER_SNOW) {
|
||||
node = this.tryJumpOn(x, y, z, verticalDeltaLimit, nodeFloorLevel, direction!!, pathType!!, mutableBlockPos)
|
||||
node = this.tryJumpOn(
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
verticalDeltaLimit,
|
||||
nodeFloorLevel,
|
||||
direction!!,
|
||||
pathType!!,
|
||||
mutableBlockPos
|
||||
)
|
||||
} else if (pathType2 == PathType.WATER && !canFloat()) {
|
||||
node = this.tryFindFirstNonWaterBelow(x, y, z, node)
|
||||
} else if (pathType2 == PathType.OPEN) {
|
||||
@@ -143,7 +160,11 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
|
||||
private fun canReachWithoutCollision(node: Node): Boolean {
|
||||
var aABB = mob.boundingBox
|
||||
var vec3 = Vec3(node.x.toDouble() - mob.x + aABB.xsize / 2.0, node.y.toDouble() - mob.y + aABB.ysize / 2.0, node.z.toDouble() - mob.z + aABB.zsize / 2.0)
|
||||
var vec3 = Vec3(
|
||||
node.x.toDouble() - mob.x + aABB.xsize / 2.0,
|
||||
node.y.toDouble() - mob.y + aABB.ysize / 2.0,
|
||||
node.z.toDouble() - mob.z + aABB.zsize / 2.0
|
||||
)
|
||||
val i = Mth.ceil(vec3.length() / aABB.size)
|
||||
vec3 = vec3.scale((1.0f / i.toFloat()).toDouble())
|
||||
for (j in 1..i) {
|
||||
@@ -187,13 +208,22 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
}
|
||||
|
||||
private fun getNodeAndUpdateCostToMax(x: Int, y: Int, z: Int, pathType: PathType, malus: Float): Node? {
|
||||
val node = this.getNode(x, y, z) ?: super.getNode(x,y,z) ?: return null
|
||||
val node = this.getNode(x, y, z) ?: super.getNode(x, y, z) ?: return null
|
||||
node.type = pathType
|
||||
node.costMalus = max(node.costMalus.toDouble(), malus.toDouble()).toFloat()
|
||||
return node
|
||||
}
|
||||
|
||||
private fun tryJumpOn(x: Int, y: Int, z: Int, verticalDeltaLimit: Int, nodeFloorLevel: Double, direction: Direction, pathType: PathType, pos: BlockPos.MutableBlockPos): Node? {
|
||||
private fun tryJumpOn(
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
verticalDeltaLimit: Int,
|
||||
nodeFloorLevel: Double,
|
||||
direction: Direction,
|
||||
pathType: PathType,
|
||||
pos: BlockPos.MutableBlockPos
|
||||
): Node? {
|
||||
val node = findAcceptedNodeWalk(x, y + 1, z, verticalDeltaLimit - 1, nodeFloorLevel, direction, pathType)
|
||||
return if (node == null) {
|
||||
null
|
||||
@@ -205,7 +235,20 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
val d = (x - direction.stepX).toDouble() + 0.5
|
||||
val e = (z - direction.stepZ).toDouble() + 0.5
|
||||
val f = mob.bbWidth.toDouble() / 2.0
|
||||
val aABB = AABB(d - f, this.getFloorLevel(pos.set(d, (y + 1).toDouble(), e)) + 0.001, e - f, d + f, mob.bbHeight.toDouble() + this.getFloorLevel(pos.set(node.x.toDouble(), node.y.toDouble(), node.z.toDouble())) - 0.002, e + f)
|
||||
val aABB = AABB(
|
||||
d - f,
|
||||
this.getFloorLevel(pos.set(d, (y + 1).toDouble(), e)) + 0.001,
|
||||
e - f,
|
||||
d + f,
|
||||
mob.bbHeight.toDouble() + this.getFloorLevel(
|
||||
pos.set(
|
||||
node.x.toDouble(),
|
||||
node.y.toDouble(),
|
||||
node.z.toDouble()
|
||||
)
|
||||
) - 0.002,
|
||||
e + f
|
||||
)
|
||||
if (this.hasCollisions(aABB)) null else node
|
||||
}
|
||||
}
|
||||
@@ -227,7 +270,9 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
if ((canFloat()) && blockGetter.getFluidState(pos).`is`(FluidTags.WATER)) {
|
||||
return pos.y.toDouble() + 0.5
|
||||
}
|
||||
return if ((canFloat()) && blockGetter.getFluidState(pos).`is`(FluidTags.WATER)) pos.y.toDouble() + 0.5 else WalkNodeEvaluator.getFloorLevel(blockGetter, pos)
|
||||
return if ((canFloat()) && blockGetter.getFluidState(pos)
|
||||
.`is`(FluidTags.WATER)
|
||||
) pos.y.toDouble() + 0.5 else WalkNodeEvaluator.getFloorLevel(blockGetter, pos)
|
||||
}
|
||||
|
||||
override fun getNeighbors(successors: Array<Node?>, node: Node): Int {
|
||||
@@ -241,11 +286,20 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
|
||||
// Non-diagonal surroundings in 3d space
|
||||
for (direction in Direction.entries) {
|
||||
var pathNode : Node?
|
||||
var pathNode: Node?
|
||||
if (mob.isInWater || canFly()) {
|
||||
pathNode = this.getNode(node.x + direction.stepX, node.y + direction.stepY, node.z + direction.stepZ) ?: continue
|
||||
pathNode = this.getNode(node.x + direction.stepX, node.y + direction.stepY, node.z + direction.stepZ)
|
||||
?: continue
|
||||
} else {
|
||||
pathNode = findAcceptedNodeWalk(node.x + direction.stepX, node.y + direction.stepY, node.z + direction.stepZ, if (direction == Direction.DOWN || direction == Direction.UP) 0 else 1, d, direction, node.type) ?: continue
|
||||
pathNode = findAcceptedNodeWalk(
|
||||
node.x + direction.stepX,
|
||||
node.y + direction.stepY,
|
||||
node.z + direction.stepZ,
|
||||
if (direction == Direction.DOWN || direction == Direction.UP) 0 else 1,
|
||||
d,
|
||||
direction,
|
||||
node.type
|
||||
) ?: continue
|
||||
}
|
||||
map[direction] = pathNode
|
||||
if (!hasNotVisited(pathNode, node)) {
|
||||
@@ -259,21 +313,34 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
val direction2 = direction.clockWise
|
||||
val x = node.x + direction.stepX + direction2.stepX
|
||||
val z = node.z + direction.stepZ + direction2.stepZ
|
||||
var pathNode2 : Node?
|
||||
var pathNode2: Node?
|
||||
if (mob.isInWater || canFly()) {
|
||||
pathNode2 = this.getNode(x, node.y, z) ?: continue
|
||||
} else {
|
||||
pathNode2 = findAcceptedNodeWalk(x, node.y, z, if (direction == Direction.DOWN || direction == Direction.UP) 0 else 1, d, direction, node.type) ?: continue
|
||||
pathNode2 = findAcceptedNodeWalk(
|
||||
x,
|
||||
node.y,
|
||||
z,
|
||||
if (direction == Direction.DOWN || direction == Direction.UP) 0 else 1,
|
||||
d,
|
||||
direction,
|
||||
node.type
|
||||
) ?: continue
|
||||
}
|
||||
// Skip 'inaccessible' diagonals if we're pathing from a blocked node since we're trying to get unstuck
|
||||
if (isAccessibleDiagonal(pathNode2, map[direction], map[direction2]) || (node.type == PathType.BLOCKED && !pathNode2.closed)) {
|
||||
if (isAccessibleDiagonal(
|
||||
pathNode2,
|
||||
map[direction],
|
||||
map[direction2]
|
||||
) || (node.type == PathType.BLOCKED && !pathNode2.closed)
|
||||
) {
|
||||
successors[i++] = pathNode2
|
||||
}
|
||||
}
|
||||
if (canFly() || mob.isInWater) {
|
||||
// Upward non-diagonals
|
||||
for (direction in Direction.Plane.HORIZONTAL.iterator()) {
|
||||
var pathNode2 : Node? = null
|
||||
var pathNode2: Node? = null
|
||||
pathNode2 = getNode(node.x + direction.stepX, node.y + 1, node.z + direction.stepZ) ?: continue
|
||||
|
||||
if (upIsOpen && hasNotVisited(pathNode2, node)) {
|
||||
@@ -285,8 +352,12 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
// Upward diagonals
|
||||
for (direction in Direction.Plane.HORIZONTAL.iterator()) {
|
||||
val direction2 = direction.clockWise
|
||||
var pathNode2 : Node? = null
|
||||
pathNode2 = getNode(node.x + direction.stepX + direction2.stepX, node.y + 1, node.z + direction.stepZ + direction2.stepZ) ?: continue
|
||||
var pathNode2: Node? = null
|
||||
pathNode2 = getNode(
|
||||
node.x + direction.stepX + direction2.stepX,
|
||||
node.y + 1,
|
||||
node.z + direction.stepZ + direction2.stepZ
|
||||
) ?: continue
|
||||
|
||||
if (isAccessibleDiagonal(pathNode2, upperMap[direction], upperMap[direction2])) {
|
||||
successors[i++] = pathNode2
|
||||
@@ -309,7 +380,11 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
// Downward diagonals
|
||||
for (direction in Direction.Plane.HORIZONTAL.iterator()) {
|
||||
val direction2 = direction.clockWise
|
||||
val pathNode2 = getNode(node.x + direction.stepX + direction2.stepX, node.y - 1, node.z + direction.stepZ + direction2.stepZ) ?: continue
|
||||
val pathNode2 = getNode(
|
||||
node.x + direction.stepX + direction2.stepX,
|
||||
node.y - 1,
|
||||
node.z + direction.stepZ + direction2.stepZ
|
||||
) ?: continue
|
||||
if (isAccessibleDiagonal(pathNode2, lowerMap[direction], lowerMap[direction2])) {
|
||||
successors[i++] = pathNode2
|
||||
}
|
||||
@@ -352,7 +427,12 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
}
|
||||
|
||||
fun isAccessibleDiagonal(pathNode: Node, vararg borderNodes: Node?): Boolean {
|
||||
return borderNodes.all{ it != null && hasNotVisited(pathNode, it) } && borderNodes.all { it != null && it.costMalus >= 0.0F }
|
||||
return borderNodes.all {
|
||||
it != null && hasNotVisited(
|
||||
pathNode,
|
||||
it
|
||||
)
|
||||
} && borderNodes.all { it != null && it.costMalus >= 0.0F }
|
||||
}
|
||||
|
||||
fun isValidPathType(type: PathType): Boolean {
|
||||
@@ -385,7 +465,9 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
}
|
||||
|
||||
fun addNodePos(x: Int, y: Int, z: Int): PathType {
|
||||
return nodePosToType.computeIfAbsent(BlockPos.asLong(x, y, z), Long2ObjectFunction { getPathTypeOfMob(currentContext, x, y, z, mob) })
|
||||
return nodePosToType.computeIfAbsent(
|
||||
BlockPos.asLong(x, y, z),
|
||||
Long2ObjectFunction { getPathTypeOfMob(currentContext, x, y, z, mob) })
|
||||
}
|
||||
|
||||
override fun getPathType(pfContext: PathfindingContext, x: Int, y: Int, z: Int): PathType {
|
||||
@@ -411,7 +493,7 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
var figuredNode = if (blockState.`is`(BlockTags.FENCES) || blockState.`is`(BlockTags.WALLS)) {
|
||||
PathType.FENCE
|
||||
} else if (blockStateBelow.block is FenceGateBlock) {
|
||||
if(blockStateBelow.getValue(FenceGateBlock.OPEN)) {
|
||||
if (blockStateBelow.getValue(FenceGateBlock.OPEN)) {
|
||||
PathType.OPEN
|
||||
} else {
|
||||
PathType.FENCE
|
||||
@@ -440,6 +522,8 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
// PathType.WALKABLE
|
||||
// } else if (blockState.canPathfindThrough(world, pos, NavigationType.AIR) && !blockStateBelow.isSideSolid(world, below, Direction.UP, SideShapeType.FULL)) {
|
||||
// PathType.OPEN
|
||||
} else if (blockState.`is`(BlockTags.LEAVES) && blockState.block == CobblemonBlocks.SACCHARINE_LEAVES && canPathThroughLeaves()) {
|
||||
return PathType.OPEN
|
||||
} else PathType.BLOCKED
|
||||
|
||||
if (figuredNode == PathType.OPEN && pos.y >= pfContext.level().getMinBuildHeight() + 1) {
|
||||
@@ -470,10 +554,15 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
val sizeX = (mob.boundingBox.maxX - mob.boundingBox.minX).toInt() + 1
|
||||
val sizeY = (mob.boundingBox.maxY - mob.boundingBox.minY).toInt() + 1
|
||||
val sizeZ = (mob.boundingBox.maxZ - mob.boundingBox.minZ).toInt() + 1
|
||||
val type = findNearbyNodeTypes(pfContext, x, y, z, sizeX, sizeY, sizeZ, canOpenDoors, canPassDoors, set, PathType.BLOCKED,
|
||||
val type = findNearbyNodeTypes(
|
||||
pfContext, x, y, z, sizeX, sizeY, sizeZ, canOpenDoors, canPassDoors, set, PathType.BLOCKED,
|
||||
BlockPos(x, y, z)
|
||||
)
|
||||
|
||||
if (PathType.LEAVES in set) {
|
||||
return PathType.OPEN
|
||||
}
|
||||
|
||||
if (PathType.FENCE in set) {
|
||||
return PathType.FENCE
|
||||
}
|
||||
@@ -574,7 +663,11 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
} else if (type == PathType.RAIL && block !is BaseRailBlock && pfContext.getBlockState(pos.below()).block !is BaseRailBlock) {
|
||||
PathType.UNPASSABLE_RAIL
|
||||
} else if (type == PathType.LEAVES) {
|
||||
PathType.BLOCKED
|
||||
if (blockState.block === CobblemonBlocks.SACCHARINE_LEAVES && canPathThroughLeaves()) {
|
||||
PathType.OPEN
|
||||
} else {
|
||||
PathType.BLOCKED
|
||||
}
|
||||
} else type
|
||||
}
|
||||
|
||||
@@ -594,13 +687,13 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
}
|
||||
}
|
||||
|
||||
fun canSwimInWater(): Boolean {
|
||||
return if (this.mob is OmniPathingEntity) {
|
||||
(this.mob as OmniPathingEntity).canSwimInWater()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fun canSwimInWater(): Boolean {
|
||||
return if (this.mob is OmniPathingEntity) {
|
||||
(this.mob as OmniPathingEntity).canSwimInWater()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun canSwimUnderFluid(fluidState: FluidState): Boolean {
|
||||
return if (this.mob is OmniPathingEntity) {
|
||||
@@ -610,6 +703,14 @@ class OmniPathNodeMaker : NodeEvaluator() {
|
||||
}
|
||||
}
|
||||
|
||||
fun canPathThroughLeaves() : Boolean {
|
||||
return if (this.mob is OmniPathingEntity) {
|
||||
(this.mob as OmniPathingEntity).canPathThroughSaccLeaves()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun canWalkOnWater(): Boolean {
|
||||
return if (this.mob is OmniPathingEntity) {
|
||||
(this.mob as OmniPathingEntity).canWalkOnWater()
|
||||
|
||||
@@ -195,8 +195,8 @@ object PokemonBrain {
|
||||
CobblemonMemories.RECENTLY_ATE_GRASS,
|
||||
CobblemonMemories.HIVE_LOCATION,
|
||||
CobblemonMemories.HIVE_COOLDOWN,
|
||||
CobblemonMemories.NEARBY_FLOWERS,
|
||||
CobblemonMemories.POLLINATED,
|
||||
CobblemonMemories.NEARBY_FLOWER,
|
||||
CobblemonMemories.HAS_NECTAR,
|
||||
CobblemonMemories.RECENTLY_ATE_GRASS,
|
||||
CobblemonMemories.HERD_LEADER,
|
||||
CobblemonMemories.HERD_SIZE,
|
||||
|
||||
+49
-45
@@ -100,53 +100,57 @@ object CobblemonHeldItemManager : BaseCobblemonHeldItemManager() {
|
||||
return this.showdownIdOf(itemStack.item)
|
||||
}
|
||||
|
||||
override fun handleStartInstruction(pokemon: BattlePokemon, battle: PokemonBattle, battleMessage: BattleMessage) {
|
||||
val itemID = battleMessage.effectAt(1)?.id ?: return
|
||||
val consumeHeldItems = this.shouldConsumeItem(pokemon, battle, itemID)
|
||||
if (battleMessage.hasOptionalArgument("silent")) {
|
||||
if (consumeHeldItems) {
|
||||
this.take(pokemon, itemID)
|
||||
}
|
||||
return
|
||||
}
|
||||
val effect = battleMessage.effect()
|
||||
val battlerName = pokemon.getName()
|
||||
val itemName = this.nameOf(itemID)
|
||||
// Airballoon is the only item using the null effect gimmick
|
||||
if (effect == null) {
|
||||
battle.broadcastChatMessage(battleLang("item.$itemID", battlerName, itemName))
|
||||
return
|
||||
}
|
||||
val sourceName = battleMessage.battlePokemonFromOptional(battle)?.getName() ?: Component.literal("UNKNOWN")
|
||||
val effectId = effect.id
|
||||
val text = when (effectId) {
|
||||
"magician", "pickpocket", "covet", "thief" -> battleLang("item.thief", battlerName, itemName, sourceName) // The "source" is actually the target here
|
||||
"pickup", "recycle" -> battleLang("item.recycle", battlerName, itemName)
|
||||
"switcheroo", "trick" -> battleLang("item.trick", battlerName, itemName)
|
||||
else -> battleLang("item.$effectId", battlerName, itemName, sourceName)
|
||||
}
|
||||
battle.broadcastChatMessage(text)
|
||||
// If it's a take and give effect, we don't want to follow through if we are not consuming held items
|
||||
if (this.takeItemEffect.contains(effectId) && this.giveItemEffect.contains(effectId) && !consumeHeldItems) {
|
||||
return
|
||||
}
|
||||
// Block item swapping in PVP until we have a rule
|
||||
if (battle.isPvP && !consumeHeldItems) {
|
||||
return
|
||||
}
|
||||
// Block stealing from NPCs
|
||||
if (battle.isPvN) {
|
||||
return
|
||||
}
|
||||
// if items aren't consumed, then we don't want to give them to wild pokemon (dupe)
|
||||
if (this.giveItemEffect.contains(effectId) && (pokemon.actor is PlayerBattleActor || consumeHeldItems)) {
|
||||
this.give(pokemon, itemID)
|
||||
}
|
||||
// allow players to steal wild held items
|
||||
if (this.takeItemEffect.contains(effectId) && (pokemon.actor !is PlayerBattleActor || consumeHeldItems)) {
|
||||
battleMessage.actorAndActivePokemonFromOptional(battle)?.second?.battlePokemon?.let { this.take(it, itemID) }
|
||||
override fun handleStartInstruction(pokemon: BattlePokemon, battle: PokemonBattle, battleMessage: BattleMessage) {
|
||||
val itemID = battleMessage.effectAt(1)?.id ?: return
|
||||
val consumeHeldItems = this.shouldConsumeItem(pokemon, battle, itemID)
|
||||
if (battleMessage.hasOptionalArgument("silent")) {
|
||||
if (consumeHeldItems) {
|
||||
this.take(pokemon, itemID)
|
||||
}
|
||||
return
|
||||
}
|
||||
val effect = battleMessage.effect()
|
||||
val battlerName = pokemon.getName()
|
||||
val itemName = this.nameOf(itemID)
|
||||
// Airballoon is the only item using the null effect gimmick
|
||||
if (effect == null) {
|
||||
battle.broadcastChatMessage(battleLang("item.$itemID", battlerName, itemName))
|
||||
return
|
||||
}
|
||||
val sourceName = battleMessage.battlePokemonFromOptional(battle)?.getName() ?: Component.literal("UNKNOWN")
|
||||
val effectId = effect.id
|
||||
val text = when (effectId) {
|
||||
"magician", "pickpocket", "covet", "thief" -> battleLang("item.thief", battlerName, itemName, sourceName) // The "source" is actually the target here
|
||||
"pickup", "recycle" -> battleLang("item.recycle", battlerName, itemName)
|
||||
"switcheroo", "trick" -> battleLang("item.trick", battlerName, itemName)
|
||||
else -> battleLang("item.$effectId", battlerName, itemName, sourceName)
|
||||
}
|
||||
battle.broadcastChatMessage(text)
|
||||
// If it's a take and give effect, we don't want to follow through if we are not consuming held items
|
||||
if (this.takeItemEffect.contains(effectId) && this.giveItemEffect.contains(effectId) && !consumeHeldItems) {
|
||||
return
|
||||
}
|
||||
// Prevent duping
|
||||
if (battle.isPvP && this.takeItemEffect.contains(effectId) || this.giveItemEffect.contains(effectId)) {
|
||||
return
|
||||
}
|
||||
// Block item swapping in PVP until we have a rule
|
||||
if (battle.isPvP && !consumeHeldItems) {
|
||||
return
|
||||
}
|
||||
// Block stealing from NPCs
|
||||
if (battle.isPvN) {
|
||||
return
|
||||
}
|
||||
// if items aren't consumed, then we don't want to give them to wild pokemon (dupe)
|
||||
if (this.giveItemEffect.contains(effectId) && (pokemon.actor is PlayerBattleActor || consumeHeldItems)) {
|
||||
this.give(pokemon, itemID)
|
||||
}
|
||||
// allow players to steal wild held items
|
||||
if (this.takeItemEffect.contains(effectId) && (pokemon.actor !is PlayerBattleActor || consumeHeldItems)) {
|
||||
battleMessage.actorAndActivePokemonFromOptional(battle)?.second?.battlePokemon?.let { this.take(it, itemID) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleEndInstruction(pokemon: BattlePokemon, battle: PokemonBattle, battleMessage: BattleMessage) {
|
||||
val itemID = battleMessage.effectAt(1)?.id ?: return
|
||||
|
||||
+1
@@ -50,6 +50,7 @@ object RidingBehaviourSettingsAdapter : JsonDeserializer<RidingBehaviourSettings
|
||||
MinekartBehaviour.KEY to MinekartSettings::class.java,
|
||||
HoverBehaviour.KEY to HoverSettings::class.java,
|
||||
RocketBehaviour.KEY to RocketSettings::class.java,
|
||||
SubmarineBehaviour.KEY to SubmarineSettings::class.java,
|
||||
CompositeBehaviour.KEY to CompositeSettings::class.java,
|
||||
|
||||
/*
|
||||
|
||||
+8
-7
@@ -21,7 +21,7 @@
|
||||
"per_update_expression": "variable.radius = 0.6;"
|
||||
},
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 30,
|
||||
"spawn_rate": 60,
|
||||
"max_particles": 10000
|
||||
},
|
||||
"minecraft:emitter_lifetime_expression": {
|
||||
@@ -29,14 +29,14 @@
|
||||
},
|
||||
"minecraft:emitter_shape_sphere": {
|
||||
"offset": [0, 0, -1],
|
||||
"radius": "1.25+(v.particle_random_4*1.25)",
|
||||
"radius": "0.85+(v.particle_random_4*1.25)",
|
||||
"surface_only": true,
|
||||
"direction": ["math.random(-0.1,0.1)*3", "math.random(-0.1,0.1)*1.25", "-0.5-math.random(-0.1,0.1)*2"]
|
||||
"direction": ["math.random(-0.1,0.1)*1", "math.random(-0.1,0.1)*1", "-math.random(-0.1,0.1)*1"]
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 1
|
||||
"max_lifetime": 1.25
|
||||
},
|
||||
"minecraft:particle_initial_speed": 1,
|
||||
"minecraft:particle_initial_speed": 0.3,
|
||||
"minecraft:particle_motion_dynamic": {
|
||||
"linear_drag_coefficient": "0.25+v.particle_random_4"
|
||||
},
|
||||
@@ -50,8 +50,9 @@
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [9, 9],
|
||||
"step_UV": [9, 0],
|
||||
"frames_per_second": 20,
|
||||
"max_frame": 11
|
||||
"frames_per_second": 12,
|
||||
"max_frame": 11,
|
||||
"loop": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+310
-70
@@ -87,6 +87,9 @@
|
||||
},
|
||||
"cannon_left": {
|
||||
"position": [0, 0, 3]
|
||||
},
|
||||
"locator_seat_water2": {
|
||||
"rotation": [32.5, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -550,6 +553,9 @@
|
||||
},
|
||||
"cannon_left": {
|
||||
"position": [0, 0, 3]
|
||||
},
|
||||
"locator_seat_water2": {
|
||||
"rotation": [35, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -702,6 +708,12 @@
|
||||
"cannon_left": {
|
||||
"position": [0, 0, 6.7],
|
||||
"scale": [1, 1, 0.9]
|
||||
},
|
||||
"locator_seat_water2": {
|
||||
"rotation": [-65, 0, 0]
|
||||
},
|
||||
"locator_seat_water": {
|
||||
"rotation": [-60, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -801,6 +813,12 @@
|
||||
"cannon_left": {
|
||||
"position": [0, 0, 7],
|
||||
"scale": 0
|
||||
},
|
||||
"locator_seat_water2": {
|
||||
"rotation": [-85, 0, 0]
|
||||
},
|
||||
"locator_seat_water": {
|
||||
"rotation": [-90, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -904,6 +922,12 @@
|
||||
"body": {
|
||||
"rotation": [9.93357, 0.75431, 0.939],
|
||||
"position": [0, -24.2, 0]
|
||||
},
|
||||
"locator_seat_water2": {
|
||||
"rotation": [-67.5, 0, 0]
|
||||
},
|
||||
"locator_seat_water": {
|
||||
"rotation": [-67.5, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1007,6 +1031,12 @@
|
||||
"body": {
|
||||
"rotation": [-2.5, 0, 0],
|
||||
"position": [0, -21, 0]
|
||||
},
|
||||
"locator_seat_water2": {
|
||||
"rotation": [-85, 0, 0]
|
||||
},
|
||||
"locator_seat_water": {
|
||||
"rotation": [-85, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1959,29 +1989,83 @@
|
||||
"eyelid_left": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.0417": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"0.5833": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"0.625": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.0": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.0417": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"1.4583": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"1.5": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.7917": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.8333": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]}
|
||||
"0.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.5833": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.625": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.0": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.4583": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.5": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.7917": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.8333": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eyelid_right": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.0417": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"0.5833": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"0.625": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.0": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.0417": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"1.4583": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"1.5": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.7917": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.8333": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]}
|
||||
"0.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.5833": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.625": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.0": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.4583": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.5": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.7917": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.8333": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2241,25 +2325,67 @@
|
||||
"eyelid_left": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.0417": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"0.25": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"0.2917": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.375": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.4167": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"1.625": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"1.6667": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.25": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.2917": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.375": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.4167": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.625": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.6667": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eyelid_right": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.0417": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"0.25": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"0.2917": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.375": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.4167": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"1.625": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"1.6667": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.25": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.2917": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.375": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.4167": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.625": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.6667": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3372,25 +3498,67 @@
|
||||
"eyelid_left": {
|
||||
"position": {
|
||||
"0.0833": [0, 0, 0],
|
||||
"0.125": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"0.7083": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"0.75": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"2.0833": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"2.125": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"2.2917": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"2.3333": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.125": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.7083": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.75": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"2.0833": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"2.125": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"2.2917": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"2.3333": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eyelid_right": {
|
||||
"position": {
|
||||
"0.0833": [0, 0, 0],
|
||||
"0.125": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"0.7083": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"0.75": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"2.0833": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"2.125": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"2.2917": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"2.3333": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.125": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.7083": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.75": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"2.0833": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"2.125": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"2.2917": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"2.3333": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"foot_right": {
|
||||
@@ -4135,25 +4303,67 @@
|
||||
"eyelid_left": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.0417": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"0.3333": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"0.375": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.2917": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"1.5": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"1.5417": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.3333": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.375": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.25": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.2917": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.5": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.5417": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eyelid_right": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.0417": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"0.3333": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"0.375": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]},
|
||||
"1.2917": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"1.5": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"1.5417": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.0417": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.3333": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.375": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.25": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0, 0, 0]
|
||||
},
|
||||
"1.2917": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.5": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.5417": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"foot_right": {
|
||||
@@ -4730,17 +4940,35 @@
|
||||
"eyelid_left": {
|
||||
"position": {
|
||||
"0.2917": [0, 0, 0],
|
||||
"0.3333": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
|
||||
"1.1667": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"1.2083": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.3333": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.1667": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"1.2083": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eyelid_right": {
|
||||
"position": {
|
||||
"0.2917": [0, 0, 0],
|
||||
"0.3333": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
|
||||
"1.1667": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"1.2083": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.3333": {
|
||||
"pre": [0, 0, 0],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.1667": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"1.2083": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4785,15 +5013,27 @@
|
||||
"eyelid_left": {
|
||||
"position": {
|
||||
"0.0": [0.1, 0, -0.1],
|
||||
"0.25": {"pre": [0.1, 0, -0.1], "post": [0.1, 0, -0.1]},
|
||||
"0.2917": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.25": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0.1, 0, -0.1]
|
||||
},
|
||||
"0.2917": {
|
||||
"pre": [0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eyelid_right": {
|
||||
"position": {
|
||||
"0.0": [-0.1, 0, -0.1],
|
||||
"0.25": {"pre": [-0.1, 0, -0.1], "post": [-0.1, 0, -0.1]},
|
||||
"0.2917": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
|
||||
"0.25": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [-0.1, 0, -0.1]
|
||||
},
|
||||
"0.2917": {
|
||||
"pre": [-0.1, 0, -0.1],
|
||||
"post": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+150
-138
@@ -163,7 +163,7 @@
|
||||
},
|
||||
"animation.exeggutor.ground_walk": {
|
||||
"loop": true,
|
||||
"animation_length": 4.79167,
|
||||
"animation_length": 4.7917,
|
||||
"bones": {
|
||||
"leg_left": {
|
||||
"rotation": {
|
||||
@@ -724,126 +724,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.exeggutor.cry": {
|
||||
"animation_length": 1.5,
|
||||
"bones": {
|
||||
"mouth": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [0, -2, 0],
|
||||
"1.25": [0, -2, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth2": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [0, -5, 0],
|
||||
"1.25": [0, -5, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"jaw": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [0, -3, 0],
|
||||
"1.25": [0, -3, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"head": {
|
||||
"rotation": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [-15, 0, 0],
|
||||
"1.25": [-15, 0, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth_closed": {
|
||||
"position": {
|
||||
"0.0": [0, 0, -0.1],
|
||||
"0.25": {"pre": [0, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, -0.1]}
|
||||
}
|
||||
},
|
||||
"mouth_open": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0.1],
|
||||
"0.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0.1]}
|
||||
}
|
||||
},
|
||||
"head2": {
|
||||
"rotation": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [-15, 0, 0],
|
||||
"1.25": [-15, 0, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth_closed2": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": {"pre": [0, 0, 0], "post": [0, 0, 0.2]},
|
||||
"1.25": {"pre": [0, 0, 0.2], "post": [0, 0, 0]}
|
||||
}
|
||||
},
|
||||
"mouth_open2": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
|
||||
"1.25": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
|
||||
}
|
||||
},
|
||||
"head3": {
|
||||
"rotation": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [-15, 0, 0],
|
||||
"1.25": [-15, 0, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth_closed3": {
|
||||
"position": {
|
||||
"0.0": [0, 0, -0.1],
|
||||
"0.25": {"pre": [0, 0, -0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, -0.1]}
|
||||
}
|
||||
},
|
||||
"mouth_open3": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0.1],
|
||||
"0.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0.1]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.exeggutor.battle_idle": {
|
||||
"loop": true,
|
||||
"animation_length": 1.58333,
|
||||
"animation_length": 1.5833,
|
||||
"bones": {
|
||||
"leg_left": {
|
||||
"rotation": {
|
||||
"0.0": ["-15+math.clamp(-math.sin(q.anim_time*90*2.5+225)*65,-90,0)", -35, 0],
|
||||
"0.0": [-15, -35, 0],
|
||||
"0.5833": [-15, -35, 0],
|
||||
"0.625": ["-15+math.clamp(-math.sin(q.anim_time*90*2.5+225)*65,-90,0)", -35, 0],
|
||||
"1.125": ["-15+math.clamp(-math.sin(q.anim_time*90*2.5+225)*65,-90,0)", -35, 0],
|
||||
"0.7917": [-65, -35, 0],
|
||||
"0.9583": {
|
||||
"pre": [-75, -35, 0],
|
||||
"post": [-75, -35, 0],
|
||||
"lerp_mode": "catmullrom"
|
||||
},
|
||||
"1.125": {
|
||||
"post": [-65, -35, 0],
|
||||
"lerp_mode": "catmullrom"
|
||||
},
|
||||
"1.2917": [-15, -35, 0],
|
||||
"1.5833": [-15, -35, 0]
|
||||
},
|
||||
"position": {
|
||||
"0.0": [
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*0.5,0,90)",
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*3.5,0,90)",
|
||||
"math.clamp(-math.sin(q.anim_time*90*2.5+225)*2,-90,0)"
|
||||
],
|
||||
"0.0": [0, 0, 0],
|
||||
"0.5833": [0, 0, 0],
|
||||
"0.625": [
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*0.5,0,90)",
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*3.5,0,90)",
|
||||
"math.clamp(-math.sin(q.anim_time*90*2.5+225)*2,-90,0)"
|
||||
],
|
||||
"0.625": [0.06, 0.42, -0.24],
|
||||
"0.7083": [0.18, 1.26, -0.72],
|
||||
"0.7917": [0.15, 1.08, -0.62],
|
||||
"0.9167": [0.11, 0.81, -0.47],
|
||||
"1.0417": [0.07, 0.54, -0.31],
|
||||
"1.1667": [0.04, 0.27, -0.16],
|
||||
"1.2917": [0, 0, 0],
|
||||
"1.5833": [0, 0, 0]
|
||||
}
|
||||
@@ -852,7 +762,7 @@
|
||||
"rotation": {
|
||||
"0.0": [20, 0, 0],
|
||||
"0.5833": [20, 0, 0],
|
||||
"0.875": [5, 0, 0],
|
||||
"0.875": [40, 0, 0],
|
||||
"1.125": [20, 0, 0]
|
||||
}
|
||||
},
|
||||
@@ -860,53 +770,55 @@
|
||||
"rotation": {
|
||||
"0.0": [-5, 0, 0],
|
||||
"0.5833": [-5, 0, 0],
|
||||
"0.7083": [30, 0, 0],
|
||||
"0.9167": [10, 0, 0],
|
||||
"1.125": [-5, 0, 0],
|
||||
"1.25": [-5, 0, 0]
|
||||
"0.7083": [17.5, 0, 0],
|
||||
"0.9167": [-5, 0, 0]
|
||||
}
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": {
|
||||
"0.0": ["-15+math.clamp(math.sin(q.anim_time*90*2.5+225)*65,-90,0)", 35, 0],
|
||||
"0.3333": ["-15+math.clamp(math.sin(q.anim_time*90*2.5+225)*65,-90,0)", 35, 0],
|
||||
"0.0": [-65, 35, 0],
|
||||
"0.1667": {
|
||||
"pre": [-75, 35, 0],
|
||||
"post": [-75, 35, 0],
|
||||
"lerp_mode": "catmullrom"
|
||||
},
|
||||
"0.3333": {
|
||||
"post": [-65, 35, 0],
|
||||
"lerp_mode": "catmullrom"
|
||||
},
|
||||
"0.5": [-15, 35, 0],
|
||||
"1.3333": [-15, 35, 0],
|
||||
"1.375": ["-15+math.clamp(math.sin(q.anim_time*90*2.5+225)*65,-90,0)", 35, 0]
|
||||
"1.375": [-15, 35, 0],
|
||||
"1.5833": [-65, 35, 0]
|
||||
},
|
||||
"position": {
|
||||
"0.0": [
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*0.5,-90,0)",
|
||||
"math.clamp(-math.sin(q.anim_time*90*2.5+225)*3.5,0,90)",
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*2,-90,0)"
|
||||
],
|
||||
"0.0": [-0.37, 2.61, -1.49],
|
||||
"0.0417": [-0.37, 2.61, -1.49],
|
||||
"0.125": [-0.3, 2.13, -1.22],
|
||||
"0.25": [-0.2, 1.42, -0.81],
|
||||
"0.375": [-0.1, 0.71, -0.4],
|
||||
"0.5": [0, 0, 0],
|
||||
"1.3333": [0, 0, 0],
|
||||
"1.375": [
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*0.5,-90,0)",
|
||||
"math.clamp(-math.sin(q.anim_time*90*2.5+225)*3.5,0,90)",
|
||||
"math.clamp(math.sin(q.anim_time*90*2.5+225)*2,-90,0)"
|
||||
]
|
||||
"1.375": [0, 0, 0],
|
||||
"1.5833": [-0.37, 2.61, -1.49]
|
||||
}
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": {
|
||||
"0.0": [7, 0, 0],
|
||||
"0.125": [5, 0, 0],
|
||||
"0.0": [35, 0, 0],
|
||||
"0.125": [40, 0, 0],
|
||||
"0.375": [20, 0, 0],
|
||||
"1.375": [20, 0, 0],
|
||||
"1.5833": [7, 0, 0]
|
||||
"1.5833": [35, 0, 0]
|
||||
}
|
||||
},
|
||||
"foot_right": {
|
||||
"rotation": {
|
||||
"0.0": [22, 0, 0],
|
||||
"0.125": [10, 0, 0],
|
||||
"0.3333": [-5, 0, 0],
|
||||
"0.4583": [-5, 0, 0],
|
||||
"0.0": [4, 0, 0],
|
||||
"0.0833": [-5, 0, 0],
|
||||
"1.375": [-5, 0, 0],
|
||||
"1.5": [30, 0, 0],
|
||||
"1.5833": [22, 0, 0]
|
||||
"1.5": [17.5, 0, 0],
|
||||
"1.5833": [8.5, 0, 0]
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
@@ -1063,6 +975,106 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.exeggutor.cry": {
|
||||
"animation_length": 1.5,
|
||||
"bones": {
|
||||
"mouth": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [0, -2, 0],
|
||||
"1.25": [0, -2, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth2": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [0, -5, 0],
|
||||
"1.25": [0, -5, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"jaw": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [0, -3, 0],
|
||||
"1.25": [0, -3, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"head": {
|
||||
"rotation": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [-5, 0, 0],
|
||||
"1.25": [-2.5, 0, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth_closed": {
|
||||
"position": {
|
||||
"0.0": [-1, 0, -0.1],
|
||||
"0.25": {"pre": [-1, 0, -0.1], "post": [-1, 0, 0]},
|
||||
"1.25": {"pre": [-1, 0, 0], "post": [-1, 0, -0.1]}
|
||||
}
|
||||
},
|
||||
"mouth_open": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0.1],
|
||||
"0.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0.1]}
|
||||
}
|
||||
},
|
||||
"head2": {
|
||||
"rotation": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [-5, 0, 0],
|
||||
"1.25": [-2.5, 0, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth_closed2": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": {"pre": [0, 0, 0], "post": [0, 0, 0.2]},
|
||||
"1.25": {"pre": [0, 0, 0.2], "post": [0, 0, 0]}
|
||||
}
|
||||
},
|
||||
"mouth_open2": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": {"pre": [0, 0, 0], "post": [0, 0, -0.1]},
|
||||
"1.25": {"pre": [0, 0, -0.1], "post": [0, 0, 0]}
|
||||
}
|
||||
},
|
||||
"head3": {
|
||||
"rotation": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.25": [-5, 0, 0],
|
||||
"1.25": [-2.5, 0, 0],
|
||||
"1.5": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"mouth_closed3": {
|
||||
"position": {
|
||||
"0.0": [-1, 0, -0.1],
|
||||
"0.25": {"pre": [-1, 0, -0.1], "post": [-1, 0, 0]},
|
||||
"1.25": {"pre": [-1, 0, 0], "post": [-1, 0, -0.1]}
|
||||
}
|
||||
},
|
||||
"mouth_open3": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0.1],
|
||||
"0.25": {"pre": [0, 0, 0.1], "post": [0, 0, 0]},
|
||||
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0.1]}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sound_effects": {
|
||||
"0.25": {
|
||||
"effect": "pokemon.exeggutor.cry"
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.exeggutor.sleep": {
|
||||
"loop": true,
|
||||
"animation_length": 8,
|
||||
@@ -1294,7 +1306,7 @@
|
||||
}
|
||||
},
|
||||
"animation.exeggutor.blink3": {
|
||||
"animation_length": 0.16667,
|
||||
"animation_length": 0.1667,
|
||||
"bones": {
|
||||
"eyelid_right3": {
|
||||
"position": {
|
||||
|
||||
+1780
-304
File diff suppressed because it is too large
Load Diff
+588
-82
@@ -5,47 +5,78 @@
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": ["5+(math.sin(q.anim_time*90*2*1+180)*6)*0.6", 0, "0+(math.sin(q.anim_time*90*2*1+180)*1)*0.6"],
|
||||
"position": [0, "2+(math.cos(q.anim_time*90*2*1+180)*0.8)*0.6", 0]
|
||||
"rotation": ["-5+math.cos(query.anim_time*90*1)*2", 0, "0+math.cos(query.anim_time*90*1/2)*2"],
|
||||
"position": ["0+math.sin(query.anim_time*90*1/2)*0.75", "3+math.sin(query.anim_time*90*1)*0.5", 0]
|
||||
},
|
||||
"torso_top": {
|
||||
"rotation": ["0-(math.sin(q.anim_time*90*2*1+180)*1.5)*0.6", 0, 0]
|
||||
"rotation": ["2.5-math.cos(query.anim_time*90*1-30)*2", 0, "0+math.cos(query.anim_time*90*1/2-60)*2"]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": ["0-(math.sin(q.anim_time*90*2*1+180)*5)*0.6", 0, 0]
|
||||
"rotation": ["0+math.cos(query.anim_time*90*1-30)*2", 0, 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": ["0+(math.sin(q.anim_time*90*2*1+180)*5)*0.6", 0, 0]
|
||||
"rotation": ["27.5-math.cos(query.anim_time*90*1-60)*1", 0, 0]
|
||||
},
|
||||
"head": {
|
||||
"rotation": ["5-(math.sin(q.anim_time*90*2*1+180)*3)*0.6", 0, 0]
|
||||
"eyes_bright": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [0, "(math.sin(q.anim_time*90*2*1+180)*2)*1", "-60-(math.sin(q.anim_time*90*2*1+180)*2)*0.6"]
|
||||
"rotation": [
|
||||
"26.4641+math.cos(query.anim_time*90*1/2-120)*7",
|
||||
"19.3475-math.cos(query.anim_time*90*1/2-60)*6",
|
||||
"-42.5248-math.cos(query.anim_time*90*1-90)*2"
|
||||
]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [0, "0+(math.cos(q.anim_time*90*2*1+180)*1)*0.6", 0]
|
||||
"rotation": [
|
||||
"48.1034+math.cos(query.anim_time*90*1-90)*6",
|
||||
"-28.22+math.cos(query.anim_time*90*1/2-90)*8",
|
||||
"-9.9783-math.cos(query.anim_time*90*1-120)*6"
|
||||
]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [0, 0, -10]
|
||||
"rotation": [12.42995, "-10.0859-math.cos(query.anim_time*90*1-120)*5", "-31.3315+math.cos(query.anim_time*90*1-150)*6"]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [0, "-(math.sin(q.anim_time*90*2*1+180)*2)*1", "60+(math.sin(q.anim_time*90*2*1+180)*2)*0.6"]
|
||||
"rotation": [
|
||||
"26.4641+math.cos(query.anim_time*90*1/2-120)*7",
|
||||
"-19.3475+math.cos(query.anim_time*90*1/2-60)*6",
|
||||
"42.5248+math.cos(query.anim_time*90*1-90)*2"
|
||||
]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [0, "0-(math.sin(q.anim_time*90*2*1+180)*5)*0.6", 0]
|
||||
"rotation": [
|
||||
"48.1034+math.cos(query.anim_time*90*1-90)*6",
|
||||
"28.22-math.cos(query.anim_time*90*1/2-90)*8",
|
||||
"9.9783+math.cos(query.anim_time*90*1-120)*6"
|
||||
]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [0, 0, 10]
|
||||
"rotation": [12.42995, "10.0859+math.cos(query.anim_time*90*1-120)*5", "31.3315-math.cos(query.anim_time*90*1-150)*6"]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": ["-7.5+math.cos(query.anim_time*90*1-60)*2", 0, "0-math.cos(query.anim_time*90*1/2-120)*2"]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": ["0-(math.cos(q.anim_time*90*2*1+180)*3)*0.6", 0, 0]
|
||||
"rotation": ["-3+math.cos(query.anim_time*90*1-90)*2", 0, "0-math.cos(query.anim_time*90*1/2-90)*3"]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": ["0+(math.sin(q.anim_time*90*2*1+180)*3)*0.6", 0, "0-(math.sin(q.anim_time*90*2*1+180)*3)*0.6"]
|
||||
"rotation": ["2.5062+math.cos(query.anim_time*90*1-120)*6", 10.02087, "2.4142-math.cos(query.anim_time*90*1/2-90)*8"]
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": ["22.5+math.cos(query.anim_time*90*1-170)*10", 0, "NaN-math.cos(query.anim_time*90*1/2-150)*6"]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": ["0+(math.sin(q.anim_time*90*2*1+180)*3)*0.6", 0, "0+(math.sin(q.anim_time*90*2*1+180)*3)*0.6"]
|
||||
"rotation": ["2.5062+math.cos(query.anim_time*90*1-120)*6", -10.02087, "-2.4142-math.cos(query.anim_time*90*1/2-90)*8"]
|
||||
},
|
||||
"leg_left2": {
|
||||
"rotation": ["22.5+math.cos(query.anim_time*90*1-170)*10", 0, "-math.cos(query.anim_time*90*1/2-150)*6"]
|
||||
},
|
||||
"eyes_dim": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"head": {
|
||||
"rotation": ["15+math.cos(query.anim_time*90*1)*1", 0, "0-math.cos(query.anim_time*90*1/2-120)*3"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -53,47 +84,157 @@
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": ["17.5-(math.cos(q.anim_time*90*2*1+180)*10)*0.6", 0, "0-(math.sin(q.anim_time*90*2*1+180)*1)*0.6"],
|
||||
"position": [0, "2+(math.cos(q.anim_time*90*2*1+180)*1.5)*0.6", "0+(math.sin(q.anim_time*90*2*1+180)*1.8)*0.6"]
|
||||
"rotation": ["25+math.cos(query.anim_time*90*3/2)*2", "0+math.cos(query.anim_time*90*3/2/2)*4", "0+math.cos(query.anim_time*90*3/2/2-60)*2"],
|
||||
"position": ["0-math.cos(query.anim_time*90*3/2/2-30)*1", "3+math.sin(query.anim_time*90*3/2)*0.7", "0-math.sin(query.anim_time*90*3/2-60)*1"]
|
||||
},
|
||||
"torso_top": {
|
||||
"rotation": ["0+(math.cos(q.anim_time*90*2*1+180)*3)*0.6", 0, 0]
|
||||
"rotation": ["10-math.cos(query.anim_time*90*3/2-30)*2", "0+math.cos(query.anim_time*90*3/2/2)*4", 0]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": ["0-(math.cos(q.anim_time*90*2*1+180)*6)*0.6", 0, 0]
|
||||
"rotation": ["-15+math.cos(query.anim_time*90*3/2-30)*2", "0+math.cos(query.anim_time*90*3/2/2-90)*2", 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": ["0+(math.cos(q.anim_time*90*2*1+180)*6)*0.6", 0, 0]
|
||||
"rotation": ["27.5-math.cos(query.anim_time*90*3/2-60)*1", 0, 0]
|
||||
},
|
||||
"head": {
|
||||
"rotation": ["0+(math.cos(q.anim_time*90*2*1+180)*6)*0.6", 0, 0]
|
||||
"eyes_bright": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [0, "20-(math.sin(q.anim_time*90*2*1+180)*3)*0.6", "-55-(math.sin(q.anim_time*90*2*1+180)*6)*0.6"]
|
||||
"rotation": [
|
||||
"20.433+math.cos(query.anim_time*90*3/2/2-120)*7",
|
||||
"31.8177-math.cos(query.anim_time*90*3/2-60)*6",
|
||||
"-45.313-math.cos(query.anim_time*90*3/2-90)*2"
|
||||
]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": ["-(math.cos(q.anim_time*90*2*1+180)*6)*0.6", 0, "0+(math.cos(q.anim_time*90*2*1+180)*1)*0.6"]
|
||||
"rotation": [
|
||||
"39.442+math.cos(query.anim_time*90*3/2-90)*6",
|
||||
"-27.7035+math.cos(query.anim_time*90*3/2-90)*8",
|
||||
"-8.2892-math.cos(query.anim_time*90*3/2-120)*6"
|
||||
]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [0, 0, -10]
|
||||
"rotation": [19.42631, "0.9236-math.cos(query.anim_time*90*3/2-120)*5", "-39.9562+math.cos(query.anim_time*90*3/2-150)*6"]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [0, "-20+(math.sin(q.anim_time*90*2*1+180)*3)*0.6", "55+(math.sin(q.anim_time*90*2*1+180)*6)*0.6"]
|
||||
"rotation": [
|
||||
"20.433+math.cos(query.anim_time*90*3/2/2-120)*7",
|
||||
"-31.8177+math.cos(query.anim_time*90*3/2-60)*6",
|
||||
"45.313+math.cos(query.anim_time*90*3/2-90)*2"
|
||||
]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": ["(math.cos(q.anim_time*90*2*1+180)*6)*0.6", 0, "0-(math.cos(q.anim_time*90*2*1+180)*1)*0.6"]
|
||||
"rotation": [
|
||||
"39.442+math.cos(query.anim_time*90*3/2-90)*6",
|
||||
"27.7035-math.cos(query.anim_time*90*3/2-90)*8",
|
||||
"8.2892+math.cos(query.anim_time*90*3/2-120)*6"
|
||||
]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [0, 0, 10]
|
||||
"rotation": [19.42631, "-0.9236+math.cos(query.anim_time*90*3/2-120)*5", "39.9562-math.cos(query.anim_time*90*3/2-150)*6"]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": ["2.5+math.cos(query.anim_time*90*3/2-60)*2", 0, "0+math.cos(query.anim_time*90*3/2/2-60)*4"]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": ["-2.5-(math.cos(q.anim_time*90*2*1+180)*6)*0.6", 0, 0]
|
||||
"rotation": ["-8+math.cos(query.anim_time*90*3/2-90)*2", "0+math.cos(query.anim_time*90*3/2/2-120)*2", "0+math.cos(query.anim_time*90*3/2/2-120)*2"]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": ["0-(math.cos(q.anim_time*90*2*1+180)*5)*2", 0, "0-(math.cos(q.anim_time*90*2*1+180)*1)*2"]
|
||||
"rotation": ["4.5574+math.cos(query.anim_time*90*3/2-120)*6", 10.64348, 7.53105]
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": ["22.5+math.cos(query.anim_time*90*3/2-170)*10", 0, 0]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": ["0-(math.cos(q.anim_time*90*2*1+180)*5)*2", 0, "0+(math.cos(q.anim_time*90*2*1+180)*1)*2"]
|
||||
"rotation": ["4.5574+math.cos(query.anim_time*90*3/2-120)*6", -10.64348, -7.53105]
|
||||
},
|
||||
"leg_left2": {
|
||||
"rotation": ["22.5+math.cos(query.anim_time*90*3/2-170)*10", 0, 0]
|
||||
},
|
||||
"eyes_dim": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"head": {
|
||||
"rotation": ["-12.5+math.cos(query.anim_time*90*3/2)*3", "0-math.cos(query.anim_time*90*3/2/2)*4", 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.battle_idle": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": ["-5+math.cos(query.anim_time*90*1)*2", 0, "0+math.cos(query.anim_time*90*1/2)*1"],
|
||||
"position": ["0+math.sin(query.anim_time*90*1/2)*0.5", "3+math.sin(query.anim_time*90*1)*0.5", 0]
|
||||
},
|
||||
"torso_top": {
|
||||
"rotation": ["2.5-math.cos(query.anim_time*90*1-30)*2", 0, "0+math.cos(query.anim_time*90*1/2-60)*2"]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": ["7.5+math.cos(query.anim_time*90*1-30)*2", 0, 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": ["27.5-math.cos(query.anim_time*90*1-60)*1", 0, 0]
|
||||
},
|
||||
"eyes_bright": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [
|
||||
"19.1787+math.cos(query.anim_time*90*1/2-120)*4",
|
||||
"22.2175-math.cos(query.anim_time*90*1-60)*3",
|
||||
"-48.3831-math.cos(query.anim_time*90*1-90)*2"
|
||||
]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": ["38.8131+math.cos(query.anim_time*90*1-90)*6", "-35.024+math.cos(query.anim_time*90*1-90)*4", "3.02+math.cos(query.anim_time*90*1-120)*2"]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [17.41964, "-20.6269-math.cos(query.anim_time*90*1-120)*3", "-23.4374+math.cos(query.anim_time*90*1-150)*8"]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [
|
||||
"19.1787+math.cos(query.anim_time*90*1/2-120)*4",
|
||||
"-22.2175+math.cos(query.anim_time*90*1-60)*3",
|
||||
"48.3831+math.cos(query.anim_time*90*1-90)*2"
|
||||
]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": ["38.8131+math.cos(query.anim_time*90*1-90)*6", "35.024-math.cos(query.anim_time*90*1-90)*4", "-3.02-math.cos(query.anim_time*90*1-120)*2"]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [17.41964, "20.6269+math.cos(query.anim_time*90*1-120)*3", "23.4374-math.cos(query.anim_time*90*1-150)*8"]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": ["-7.5+math.cos(query.anim_time*90*1-60)*2", 0, "0-math.cos(query.anim_time*90*1/2-120)*1"]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": ["-10.5+math.cos(query.anim_time*90*1-90)*2", 0, "-math.cos(query.anim_time*90*1/2-90)*2"]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": ["2.0651+math.cos(query.anim_time*90*1-120)*6", 9.9897, "-0.1242-math.cos(query.anim_time*90*1/2-90)*4"]
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": ["22.5+math.cos(query.anim_time*90*1-170)*10", 0, "NaN-math.cos(query.anim_time*90*1/2-150)*3"]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": ["2.9483+math.cos(query.anim_time*90*1-120)*6", -10.03277, "-4.953-math.cos(query.anim_time*90*1/2-90)*4"]
|
||||
},
|
||||
"leg_left2": {
|
||||
"rotation": ["22.5+math.cos(query.anim_time*90*1-170)*10", 0, "-math.cos(query.anim_time*90*1/2-150)*3"]
|
||||
},
|
||||
"pupil_left": {
|
||||
"position": [0, 0, -0.1],
|
||||
"scale": 0.75
|
||||
},
|
||||
"pupil_right": {
|
||||
"position": [0, 0, -0.1],
|
||||
"scale": 0.75
|
||||
},
|
||||
"eyes_dim": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"head": {
|
||||
"rotation": ["25+math.cos(query.anim_time*90*1)*1", 0, "0-math.cos(query.anim_time*90*1/2-120)*3"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -101,50 +242,341 @@
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": ["0+(math.sin(q.anim_time*90*1+180)*1.5)*0.6", 0, "0+(math.sin(q.anim_time*90*1+180)*3)*0.6"],
|
||||
"position": [0, "1+(math.cos(q.anim_time*90*1+180)*0.8)*0.6", 0]
|
||||
"rotation": [40, 0, 0],
|
||||
"position": [0, 3, 0]
|
||||
},
|
||||
"torso_top": {
|
||||
"rotation": ["0+(math.sin(q.anim_time*90*1+180)*5)*0.6", 0, 0]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": ["0-(math.cos(q.anim_time*90*1+180)*3)*0.6", 0, 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": ["0-(math.cos(q.anim_time*90*1+180)*3)*0.6", 0, 0]
|
||||
},
|
||||
"head": {
|
||||
"rotation": ["25+(math.sin(q.anim_time*90*1+180)*3)*0.6", 0, 0]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [0, "0-(math.cos(q.anim_time*90*1+180)*10)*0.3", "-60+(math.sin(q.anim_time*90*1+180)*5)*0.6"]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [0, "0-(math.cos(q.anim_time*90*1+180)*10)*0.3", 0]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [0, 0, -10]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [0, "0+(math.cos(q.anim_time*90*1+180)*10)*0.3", "60-(math.sin(q.anim_time*90*1+180)*5)*0.6"]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [0, "0+(math.cos(q.anim_time*90*1+180)*10)*0.3", 0]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [0, 0, 10]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": ["0+(math.sin(q.anim_time*90*1+180)*5)*0.6", 0, 0]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": [-2.5, 0, 0]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": [10, 0, 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": [-15, 0, 0]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [30.18785, -13.49583, -55.38615]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [8.0521, -19.65857, 17.64818]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [12.21635, -29.21703, -28.76232]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [30.18785, 13.49583, 55.38615]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [8.0521, 19.65857, -17.64818]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [12.21635, 29.21703, 28.76232]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": [-17.5, 0, 0]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": [-17.5, 0, 0]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": ["0+(math.sin(q.anim_time*90*1+180)*20)*0.6", 0, "0+(math.cos(q.anim_time*90*1+180)*5)*0.6"]
|
||||
"rotation": [-22.5, 0, 0]
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": [10, 0, 0]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": ["0-(math.sin(q.anim_time*90*1+180)*20)*0.6", 0, "0-(math.cos(q.anim_time*90*1+180)*5)*0.6"]
|
||||
"rotation": [-10, 0, 0]
|
||||
},
|
||||
"head": {
|
||||
"rotation": [20, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.alt_sleep": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": [-52.5, 0, 0],
|
||||
"position": [0, 3, 0]
|
||||
},
|
||||
"torso_top": {
|
||||
"rotation": [2.5, 0, 0]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": [5, 0, 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": [27.5, 0, 0]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [-40.58835, 28.2876, -5.84768]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [-23.10066, 18.44452, 79.80343]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [-103.94239, -26.14027, 28.61413]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [-40.58835, -28.2876, 5.84768]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [-23.10066, -18.44452, -79.80343]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [-103.94239, 26.14027, -28.61413]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": [-12.5, 0, 0]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": [-17.5, 0, 0]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": [-19.76968, -1.50179, -17.43745]
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": [10, 0, 0]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": [17.24721, -1.93597, 14.87739]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.blink": {
|
||||
"animation_length": 0.1667,
|
||||
"bones": {
|
||||
"eyes_bright": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0.1],
|
||||
"0.125": [0, 0, 0.1],
|
||||
"0.1667": {"pre": [0, 0, 0.1], "post": [0, 0, 0]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.pose": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": [-2.5, 0, 0],
|
||||
"position": [0, 3, 0]
|
||||
},
|
||||
"torso_top": {
|
||||
"rotation": [2.5, 0, 0]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": [5, 0, 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": [-7.5, 0, 0]
|
||||
},
|
||||
"eyes_bright": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [0.18641, 11.50404, -55.30513]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [24.59434, -22.85086, 0.87852]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [19.6995, -3.27667, -28.1772]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [0.18641, -11.50404, 55.30513]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [24.59434, 22.85086, -0.87852]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [19.6995, 3.27667, 28.1772]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": [-2.5, 0, 0]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": [-17.5, 0, 0]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": [-5, 0, 0]
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": [10, 0, 0]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": [7.5, 0, 0]
|
||||
},
|
||||
"leg_left2": {
|
||||
"rotation": [15, 0, 0]
|
||||
},
|
||||
"eyes_dim": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"head": {
|
||||
"rotation": [10, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.lights_quirk1": {
|
||||
"loop": true,
|
||||
"animation_length": 1.5,
|
||||
"bones": {
|
||||
"right_finger_bright_red2": {
|
||||
"scale": {
|
||||
"0.0": [1.05, 1.05, 1.05],
|
||||
"0.5": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"1.0": [1, 1, 1],
|
||||
"1.5": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]}
|
||||
}
|
||||
},
|
||||
"right_finger_bright_yellow": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.5": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"1.0": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"1.5": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"right_finger_bright_green2": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.5": [1, 1, 1],
|
||||
"1.0": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"1.5": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]}
|
||||
}
|
||||
},
|
||||
"left_finger_bright_red2": {
|
||||
"scale": {
|
||||
"0.0": [1.05, 1.05, 1.05],
|
||||
"0.5": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"1.0": [1, 1, 1],
|
||||
"1.5": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]}
|
||||
}
|
||||
},
|
||||
"left_finger_bright_yellow": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.5": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"1.0": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"1.5": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"left_finger_bright_green2": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.5": [1, 1, 1],
|
||||
"1.0": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"1.5": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.lights_quirk2": {
|
||||
"loop": true,
|
||||
"animation_length": 1,
|
||||
"bones": {
|
||||
"right_finger_bright_red2": {
|
||||
"scale": {
|
||||
"0.0": [1.05, 1.05, 1.05],
|
||||
"0.3333": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"0.6667": [1, 1, 1],
|
||||
"1.0": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]}
|
||||
}
|
||||
},
|
||||
"right_finger_bright_yellow": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.3333": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"0.6667": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"1.0": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"right_finger_bright_green2": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.3333": [1, 1, 1],
|
||||
"0.6667": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"1.0": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]}
|
||||
}
|
||||
},
|
||||
"left_finger_bright_red2": {
|
||||
"scale": {
|
||||
"0.0": [1.05, 1.05, 1.05],
|
||||
"0.3333": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"0.6667": [1, 1, 1],
|
||||
"1.0": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]}
|
||||
}
|
||||
},
|
||||
"left_finger_bright_yellow": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.3333": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"0.6667": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"1.0": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"left_finger_bright_green2": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.3333": [1, 1, 1],
|
||||
"0.6667": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"1.0": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.lights_quirk3": {
|
||||
"loop": true,
|
||||
"animation_length": 0.5,
|
||||
"bones": {
|
||||
"right_finger_bright_red2": {
|
||||
"scale": {
|
||||
"0.0": [1.05, 1.05, 1.05],
|
||||
"0.1667": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"0.3333": [1, 1, 1],
|
||||
"0.5": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]}
|
||||
}
|
||||
},
|
||||
"right_finger_bright_yellow": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.1667": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"0.3333": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"0.5": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"right_finger_bright_green2": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.1667": [1, 1, 1],
|
||||
"0.3333": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"0.5": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]}
|
||||
}
|
||||
},
|
||||
"left_finger_bright_red2": {
|
||||
"scale": {
|
||||
"0.0": [1.05, 1.05, 1.05],
|
||||
"0.1667": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"0.3333": [1, 1, 1],
|
||||
"0.5": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]}
|
||||
}
|
||||
},
|
||||
"left_finger_bright_yellow": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.1667": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"0.3333": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]},
|
||||
"0.5": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"left_finger_bright_green2": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
"0.1667": [1, 1, 1],
|
||||
"0.3333": {"pre": [1, 1, 1], "post": [1.05, 1.05, 1.05]},
|
||||
"0.5": {"pre": [1.05, 1.05, 1.05], "post": [1, 1, 1]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -178,15 +610,6 @@
|
||||
"1.4583": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"head": {
|
||||
"rotation": {
|
||||
"0.0": ["0+(math.cos(q.anim_time*90*2*1+180)*0.8)*0.6", 0, 0],
|
||||
"0.125": [11.5, 0, 0],
|
||||
"0.4167": ["-6-(math.cos(q.anim_time*90*80+180)*2)*2", 0, 0],
|
||||
"0.9583": ["-6+(math.cos(q.anim_time*90*8+180)*2)*0.6", 0, 0],
|
||||
"1.4583": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": {
|
||||
"0.0": [0, 0, 0],
|
||||
@@ -222,11 +645,94 @@
|
||||
"0.9583": [7.3679, 40.2246, -30.8095],
|
||||
"1.4583": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"head": {
|
||||
"rotation": {
|
||||
"0.0": ["0+(math.cos(q.anim_time*90*2*1+180)*0.8)*0.6", 0, 0],
|
||||
"0.125": [11.5, 0, 0],
|
||||
"0.4167": ["-6-(math.cos(q.anim_time*90*80+180)*2)*2", 0, 0],
|
||||
"0.9583": ["-6+(math.cos(q.anim_time*90*8+180)*2)*0.6", 0, 0],
|
||||
"1.4583": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"sound_effects": {
|
||||
"0.04": {
|
||||
"effect": "pokemon.beheeyem.cry"
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.new": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"beheeyem": {
|
||||
"position": [0, 0, "33-query.anim_time*90*0.08"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.beheeyem.pose2": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": [-2.5, 0, 0],
|
||||
"position": [0, 3, 0]
|
||||
},
|
||||
"torso_top": {
|
||||
"rotation": [2.5, 0, 0]
|
||||
},
|
||||
"collar_back": {
|
||||
"rotation": [5, 0, 0]
|
||||
},
|
||||
"collar_front": {
|
||||
"rotation": [-7.5, 0, 0]
|
||||
},
|
||||
"eyes_bright": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [-16.69715, -44.79233, -31.70444]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [24.59434, -22.85086, 0.87852]
|
||||
},
|
||||
"hand_right": {
|
||||
"rotation": [19.6995, -3.27667, -28.1772]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [0.18641, -11.50404, 55.30513]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [24.59434, 22.85086, -0.87852]
|
||||
},
|
||||
"hand_left": {
|
||||
"rotation": [19.6995, 3.27667, 28.1772]
|
||||
},
|
||||
"torso_bottom": {
|
||||
"rotation": [-2.5, 0, 0]
|
||||
},
|
||||
"bottom_coat": {
|
||||
"rotation": [-17.5, 0, 0]
|
||||
},
|
||||
"leg_right": {
|
||||
"rotation": [-5, 0, 0]
|
||||
},
|
||||
"leg_right2": {
|
||||
"rotation": [10, 0, 0]
|
||||
},
|
||||
"leg_left": {
|
||||
"rotation": [7.5, 0, 0]
|
||||
},
|
||||
"leg_left2": {
|
||||
"rotation": [15, 0, 0]
|
||||
},
|
||||
"pupil_left": {
|
||||
"position": [-0.25, 0, -0.1],
|
||||
"scale": 0.75
|
||||
},
|
||||
"pupil_right": {
|
||||
"position": [-0.25, 0, -0.1],
|
||||
"scale": 0.75
|
||||
},
|
||||
"eyes_dim": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"head": {
|
||||
"rotation": [10, 0, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+113
-40
@@ -26,6 +26,9 @@
|
||||
},
|
||||
"eyes": {
|
||||
"scale": [1, "1-math.sin(q.anim_time*90)*0.03", 1]
|
||||
},
|
||||
"wax_trail": {
|
||||
"position": [0, 0.02, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -43,7 +46,7 @@
|
||||
"scale": [1, "1-math.sin(q.anim_time*90*4-135)*0.01", 1]
|
||||
},
|
||||
"flame": {
|
||||
"rotation": ["(math.sin(q.anim_time*90)*6)*(math.sin(q.anim_time*90-90))", 0, "-(math.sin(q.anim_time*90-45)*6)*(math.sin(q.anim_time*90-90))"],
|
||||
"rotation": ["-2.5+(math.sin(q.anim_time*90)*6)*(math.sin(q.anim_time*90-90))", 0, "0-(math.sin(q.anim_time*90-45)*6)*(math.sin(q.anim_time*90-90))"],
|
||||
"scale": [1, "1+math.sin(q.anim_time*90*4+125)*0.1", 1]
|
||||
},
|
||||
"arm_right": {
|
||||
@@ -61,7 +64,10 @@
|
||||
"scale": [1, "1+math.sin(q.anim_time*90*4+90)*0.01", 1]
|
||||
},
|
||||
"wax_trail2": {
|
||||
"position": [0, 0, "0.3-math.sin(q.anim_time*90*4+75)*0.33"]
|
||||
"scale": [1, 1, "1-math.sin(q.anim_time*90*4+75)*0.1"]
|
||||
},
|
||||
"wax_trail": {
|
||||
"position": [0, 0.03, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -100,7 +106,7 @@
|
||||
"position": ["(math.sin(q.anim_time*90*2)*0.5)", 0, 0]
|
||||
},
|
||||
"wax_trail": {
|
||||
"position": ["(math.sin(q.anim_time*90*2-90)*0.2)", 0, 0],
|
||||
"position": ["(math.sin(q.anim_time*90*2-90)*0.2)", 0.02, 0],
|
||||
"scale": ["1+(math.sin(q.anim_time*90*4+45)*0.04)", 1, 1]
|
||||
},
|
||||
"lip": {
|
||||
@@ -141,7 +147,7 @@
|
||||
"scale": [1, 0.9, 1]
|
||||
},
|
||||
"wax_trail": {
|
||||
"position": [0, 0.5, 0],
|
||||
"position": [0, 0.52, 0],
|
||||
"scale": 1.075
|
||||
},
|
||||
"eyelid_right": {
|
||||
@@ -153,62 +159,129 @@
|
||||
"lip": {
|
||||
"rotation": [4.98, 0, 0],
|
||||
"position": [0, 0, -0.008]
|
||||
},
|
||||
"eye_left_open": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"eye_closed_left": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"eye_closed_right": {
|
||||
"position": [0, 0, -0.1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.litwick.blink": {
|
||||
"animation_length": 0.20833,
|
||||
"animation_length": 0.1667,
|
||||
"bones": {
|
||||
"eyelid_right": {
|
||||
"eye_left_open": {
|
||||
"position": {
|
||||
"0.0": [-0.1, 0, -0.1],
|
||||
"0.1667": [-0.1, 0, -0.1],
|
||||
"0.2083": [0, 0, 0]
|
||||
"0.0": [0, 0, 0.1],
|
||||
"0.125": [0, 0, 0.1],
|
||||
"0.1667": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"eyelid_left": {
|
||||
"eye_closed_left": {
|
||||
"position": {
|
||||
"0.0": [0.1, 0, -0.1],
|
||||
"0.1667": [0.1, 0, -0.1],
|
||||
"0.2083": [0, 0, 0]
|
||||
"0.0": [0, 0, -0.1],
|
||||
"0.125": [0, 0, -0.1],
|
||||
"0.1667": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": {
|
||||
"0.0": [0, 0, 0.1],
|
||||
"0.125": [0, 0, 0.1],
|
||||
"0.1667": [0, 0, 0]
|
||||
}
|
||||
},
|
||||
"eye_closed_right": {
|
||||
"position": {
|
||||
"0.0": [0, 0, -0.1],
|
||||
"0.125": [0, 0, -0.1],
|
||||
"0.1667": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.litwick.pose": {
|
||||
"animation.litwick.shock": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"head": {
|
||||
"rotation": [-7.56397, -7.43547, 0.98448]
|
||||
"eye_left_open": {
|
||||
"position": [0, 0.25, 0],
|
||||
"scale": [1, 0.5, 1]
|
||||
},
|
||||
"hair": {
|
||||
"rotation": [-2.5, 0, 0]
|
||||
"eye_right_open": {
|
||||
"position": [0, 0.25, 0]
|
||||
},
|
||||
"flame": {
|
||||
"rotation": [-19.8423, -2.55868, -12.05239]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [0, 0, -50]
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [0, 0, 52.5]
|
||||
},
|
||||
"lip": {
|
||||
"rotation": [-7.5, 0, 0]
|
||||
},
|
||||
"mouth_small": {
|
||||
"position": [0, 0, 0]
|
||||
},
|
||||
"mouth_large": {
|
||||
"position": [0, 0, 0]
|
||||
"mouth_open": {
|
||||
"position": [0, 0, -0.1],
|
||||
"scale": [0.9, 1, 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.litwick.cry": {
|
||||
"animation_length": 1.2,
|
||||
"sound_effects": {
|
||||
"0.04": {
|
||||
"effect": "pokemon.litwick.cry"
|
||||
"animation.litwick.angry": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_expression_left": {
|
||||
"position": [-0.5, 0, -0.1]
|
||||
},
|
||||
"eye_expression_right": {
|
||||
"position": [0.5, 0, -0.1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.litwick.sad": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_expression_left": {
|
||||
"position": [0.5, 0, -0.1]
|
||||
},
|
||||
"eye_expression_right": {
|
||||
"position": [-0.5, 0, -0.1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.litwick.happy": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_left_open": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"eye_happy_left": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"eye_happy_right": {
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"mouth_open": {
|
||||
"position": [0, 0, -0.1],
|
||||
"scale": [0.9, 1, 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.litwick.hurt": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_left_open": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"eye_closed_left": {
|
||||
"rotation": [0, 0, -27.5],
|
||||
"position": [0, 0, -0.1]
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": [0, 0, 0.1]
|
||||
},
|
||||
"eye_closed_right": {
|
||||
"rotation": [0, 0, 27.5],
|
||||
"position": [0, 0, -0.1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+155
-79
@@ -20,7 +20,7 @@
|
||||
"rotation": ["-2.5 + math.sin(q.anim_time*90*2-90)", 5, "7.5 + 5*math.sin(q.anim_time*90*2)"]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [0, 0, "10 + 5*math.sin(q.anim_time*90*2+90)"]
|
||||
"rotation": [0, 0, "5*math.sin(q.anim_time*90*2+90)"]
|
||||
},
|
||||
"arm_left3": {
|
||||
"rotation": [0, 0, "10 + 5*math.sin(q.anim_time*90*2+180)"]
|
||||
@@ -38,7 +38,7 @@
|
||||
"rotation": ["-2.5+math.sin(q.anim_time*90*2-90)", -5, "-7.5 - 5*math.sin(q.anim_time*90*2)"]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [0, 0, "-10 - 5*math.sin(q.anim_time*90*2+90)"]
|
||||
"rotation": [0, 0, "- 5*math.sin(q.anim_time*90*2+90)"]
|
||||
},
|
||||
"arm_right3": {
|
||||
"rotation": [0, 0, "-10 - 5*math.sin(q.anim_time*90*2+180)"]
|
||||
@@ -59,7 +59,13 @@
|
||||
"scale": 0
|
||||
},
|
||||
"body": {
|
||||
"position": [0, 4, 0]
|
||||
"position": [0, 1, 0]
|
||||
},
|
||||
"arm_left7": {
|
||||
"rotation": [0, 0, 57.5]
|
||||
},
|
||||
"arm_right7": {
|
||||
"rotation": [0, 0, -57.5]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -82,7 +88,7 @@
|
||||
"rotation": ["10.2854 + math.sin(q.anim_time*90*2-90)*2", 6.26335, "18.6835 + 5*math.sin(q.anim_time*90*2)"]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [0, 0, "18 + 5*math.sin(q.anim_time*90*2+90)"]
|
||||
"rotation": [0, 0, "10.5 + 5*math.sin(q.anim_time*90*2+90)"]
|
||||
},
|
||||
"arm_left3": {
|
||||
"rotation": [0, 0, "20 + 5*math.sin(q.anim_time*90*2+180)"]
|
||||
@@ -100,7 +106,7 @@
|
||||
"rotation": ["10.2854 + math.sin(q.anim_time*90*2-90)*2", -6.26335, "-18.6835+ 5*math.sin(q.anim_time*90*2)"]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [0, 0, "-18+ 5*math.sin(q.anim_time*90*2+90)"]
|
||||
"rotation": [0, 0, "-10.5+ 5*math.sin(q.anim_time*90*2+90)"]
|
||||
},
|
||||
"arm_right3": {
|
||||
"rotation": [0, 0, "-20+ 5*math.sin(q.anim_time*90*2+180)"]
|
||||
@@ -125,6 +131,12 @@
|
||||
},
|
||||
"head": {
|
||||
"rotation": [0, 0, 0]
|
||||
},
|
||||
"arm_left7": {
|
||||
"rotation": [0, 0, 57.5]
|
||||
},
|
||||
"arm_right7": {
|
||||
"rotation": [-2.50463, -13.04546, -71.15153]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -187,6 +199,12 @@
|
||||
},
|
||||
"body": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"arm_left7": {
|
||||
"rotation": [0, 0, 47.5]
|
||||
},
|
||||
"arm_right7": {
|
||||
"rotation": [0, 0, -47.5]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -209,7 +227,7 @@
|
||||
"rotation": ["10.2854 + math.sin(q.anim_time*90*2-90)*2", 6.26335, "18.6835 + 5*math.sin(q.anim_time*90*2)"]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [0, 0, "18 + 5*math.sin(q.anim_time*90*2+90)"]
|
||||
"rotation": [0, 0, "10.5 + 5*math.sin(q.anim_time*90*2+90)"]
|
||||
},
|
||||
"arm_left3": {
|
||||
"rotation": [0, 0, "20 + 5*math.sin(q.anim_time*90*2+180)"]
|
||||
@@ -227,7 +245,7 @@
|
||||
"rotation": ["10.2854 + math.sin(q.anim_time*90*2-90)*2", -6.26335, "-18.6835+ 5*math.sin(q.anim_time*90*2)"]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [0, 0, "-18+ 5*math.sin(q.anim_time*90*2+90)"]
|
||||
"rotation": [0, 0, "-10.5+ 5*math.sin(q.anim_time*90*2+90)"]
|
||||
},
|
||||
"arm_right3": {
|
||||
"rotation": [0, 0, "-20+ 5*math.sin(q.anim_time*90*2+180)"]
|
||||
@@ -252,6 +270,12 @@
|
||||
},
|
||||
"head": {
|
||||
"rotation": [0, 0, 0]
|
||||
},
|
||||
"arm_left7": {
|
||||
"rotation": [0, 0, 57.5]
|
||||
},
|
||||
"arm_right7": {
|
||||
"rotation": [0, 0, -57.5]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -297,13 +321,13 @@
|
||||
"rotation": [0, 0, "22+5*math.sin(q.anim_time*90*2)*0.5"]
|
||||
},
|
||||
"arm_right3": {
|
||||
"rotation": [0, 0, "35+5*math.sin(q.anim_time*90*2)"]
|
||||
"rotation": [0, 0, "25+5*math.sin(q.anim_time*90*2)"]
|
||||
},
|
||||
"arm_right4": {
|
||||
"rotation": [0, 0, "19.5+5*math.sin(q.anim_time*90*2)*0.5"]
|
||||
},
|
||||
"arm_right5": {
|
||||
"rotation": [0, 0, "30+5*math.sin(q.anim_time*90*2)*0.5"]
|
||||
"rotation": [0, 0, "15+5*math.sin(q.anim_time*90*2)*0.5"]
|
||||
},
|
||||
"arm_right6": {
|
||||
"rotation": [0, 0, "24+5*math.sin(q.anim_time*90*2)*0.5"]
|
||||
@@ -316,6 +340,12 @@
|
||||
},
|
||||
"body": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"arm_left7": {
|
||||
"rotation": [0, 0, 70]
|
||||
},
|
||||
"arm_right7": {
|
||||
"rotation": [0, 0, 12.5]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -379,31 +409,29 @@
|
||||
"body": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"eye_left": {
|
||||
"scale": 0
|
||||
"arm_left7": {
|
||||
"rotation": [0, 0, 67.5]
|
||||
},
|
||||
"eye_right": {
|
||||
"scale": 0
|
||||
"arm_right7": {
|
||||
"rotation": [0, 0, -67.5]
|
||||
},
|
||||
"eye_left_open": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"eye_closed_left": {
|
||||
"position": [0, -4, 0]
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"eye_closed_right": {
|
||||
"position": [0, -4, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.lampent.blink": {
|
||||
"animation_length": 0.16667,
|
||||
"animation_length": 0.1667,
|
||||
"bones": {
|
||||
"eye_left": {
|
||||
"scale": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.125": [0, 0, 0],
|
||||
"0.1667": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"eye_right": {
|
||||
"scale": {
|
||||
"0.0": [0, 0, 0],
|
||||
"0.125": [0, 0, 0],
|
||||
"0.1667": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"eyelid_left": {
|
||||
"scale": {
|
||||
"0.0": [1, 1, 1],
|
||||
@@ -417,68 +445,116 @@
|
||||
"0.125": [1, 1, 1],
|
||||
"0.1667": [1, 1, 1]
|
||||
}
|
||||
},
|
||||
"eye_left_open": {
|
||||
"position": {
|
||||
"0.0": [0, 4, 0],
|
||||
"0.125": {"pre": [0, 4, 0], "post": [0, 4, 0]},
|
||||
"0.1667": {"pre": [0, 4, 0], "post": [0, 0, 0]}
|
||||
}
|
||||
},
|
||||
"eye_closed_left": {
|
||||
"position": {
|
||||
"0.0": [0, -4, 0],
|
||||
"0.125": {"pre": [0, -4, 0], "post": [0, -4, 0]},
|
||||
"0.1667": {"pre": [0, -4, 0], "post": [0, 0, 0]}
|
||||
}
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": {
|
||||
"0.0": [0, 4, 0],
|
||||
"0.125": {"pre": [0, 4, 0], "post": [0, 4, 0]},
|
||||
"0.1667": {"pre": [0, 4, 0], "post": [0, 0, 0]}
|
||||
}
|
||||
},
|
||||
"eye_closed_right": {
|
||||
"position": {
|
||||
"0.0": [0, -4, 0],
|
||||
"0.125": {"pre": [0, -4, 0], "post": [0, -4, 0]},
|
||||
"0.1667": {"pre": [0, -4, 0], "post": [0, 0, 0]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.lampent.pose": {
|
||||
"animation.lampent.shock": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"body": {
|
||||
"rotation": [0, -5, 7.5]
|
||||
"eye_left_open": {
|
||||
"position": [0, 0.25, 0],
|
||||
"scale": [0.7, 0.7, 1]
|
||||
},
|
||||
"head": {
|
||||
"rotation": [14.60945, 2.9986, -5.59467]
|
||||
"eye_left_open2": {
|
||||
"scale": 0
|
||||
},
|
||||
"arm_left": {
|
||||
"rotation": [0, 25, -7.5]
|
||||
"eye_right_open": {
|
||||
"position": [0, 0.25, 0],
|
||||
"scale": [0.7, 0.7, 1]
|
||||
},
|
||||
"arm_left2": {
|
||||
"rotation": [0, 0, -22.5]
|
||||
},
|
||||
"arm_left3": {
|
||||
"rotation": [0, 0, -17.5]
|
||||
},
|
||||
"arm_left4": {
|
||||
"rotation": [0, 0, -22.5]
|
||||
},
|
||||
"arm_left5": {
|
||||
"rotation": [0, 0, -30]
|
||||
},
|
||||
"arm_left6": {
|
||||
"rotation": [0, 0, -28.5]
|
||||
},
|
||||
"arm_right": {
|
||||
"rotation": [0, 0, -5]
|
||||
},
|
||||
"arm_right2": {
|
||||
"rotation": [0, 0, -20]
|
||||
},
|
||||
"arm_right3": {
|
||||
"rotation": [0, 0, -25]
|
||||
},
|
||||
"arm_right4": {
|
||||
"rotation": [0, 0, -40]
|
||||
},
|
||||
"arm_right5": {
|
||||
"rotation": [0, 0, -38.5]
|
||||
},
|
||||
"arm_right6": {
|
||||
"rotation": [0, 0, -36.5]
|
||||
},
|
||||
"swing": {
|
||||
"rotation": [-13.63677, 25.79103, 6.17047],
|
||||
"position": [4.2, 4, 11]
|
||||
},
|
||||
"flame": {
|
||||
"rotation": [0, 22.5, 0]
|
||||
"eye_right_open2": {
|
||||
"position": [-0.25, -0.25, 0],
|
||||
"scale": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.lampent.cry": {
|
||||
"animation_length": 1.2,
|
||||
"sound_effects": {
|
||||
"0.04": {
|
||||
"effect": "pokemon.lampent.cry"
|
||||
"animation.lampent.angry": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_left_open2": {
|
||||
"position": [0.25, -0.25, 0],
|
||||
"scale": [0.5, 0.5, 1]
|
||||
},
|
||||
"eye_right_open2": {
|
||||
"position": [-0.25, -0.25, 0],
|
||||
"scale": [0.5, 0.5, 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.lampent.sad": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_left_open2": {
|
||||
"position": [-0.25, -0.25, 0],
|
||||
"scale": [0.5, 0.5, 1]
|
||||
},
|
||||
"eye_right_open2": {
|
||||
"position": [0.25, -0.25, 0],
|
||||
"scale": [0.5, 0.5, 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.lampent.happy": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_left_open": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"eye_happy_left": {
|
||||
"position": [0, -4, 0]
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"eye_happy_right": {
|
||||
"position": [0, -4, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"animation.lampent.hurt": {
|
||||
"loop": true,
|
||||
"bones": {
|
||||
"eye_left_open": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"eye_closed_left": {
|
||||
"rotation": [0, 0, -20],
|
||||
"position": [0, -4, 0]
|
||||
},
|
||||
"eye_right_open": {
|
||||
"position": [0, 4, 0]
|
||||
},
|
||||
"eye_closed_right": {
|
||||
"rotation": [0, 0, 20],
|
||||
"position": [0, -4, 0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+576
-553
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -7,8 +7,8 @@
|
||||
"texture_width": 256,
|
||||
"texture_height": 256,
|
||||
"visible_bounds_width": 5,
|
||||
"visible_bounds_height": 4.5,
|
||||
"visible_bounds_offset": [0, 1.75, 0]
|
||||
"visible_bounds_height": 6,
|
||||
"visible_bounds_offset": [0, 1, 0]
|
||||
},
|
||||
"bones": [
|
||||
{
|
||||
@@ -296,7 +296,7 @@
|
||||
"parent": "shell",
|
||||
"pivot": [0, 27, 18],
|
||||
"locators": {
|
||||
"seat_water_1": [0, 27, 18]
|
||||
"seat_2": [0, 27, 18]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+8
-8
@@ -31,6 +31,14 @@
|
||||
"top": [0, 38, 0]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "locator_target",
|
||||
"parent": "manectric",
|
||||
"pivot": [0, 15, -17],
|
||||
"locators": {
|
||||
"target": [0, 15, -17]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"parent": "manectric",
|
||||
@@ -44,14 +52,6 @@
|
||||
"middle": [0, 17, 0]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "locator_target",
|
||||
"parent": "body",
|
||||
"pivot": [0, 18, -19],
|
||||
"locators": {
|
||||
"target": [0, 18, -19]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "hips",
|
||||
"parent": "body",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user