mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-21 06:36:25 -04:00
Compare commits
134
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c921496b50 | ||
|
|
a9d1000384 | ||
|
|
225721aec4 | ||
|
|
ae9475959d | ||
|
|
0ecb4d32cf | ||
|
|
52a9e30dc1 | ||
|
|
1198bad42e | ||
|
|
aa18d9f90e | ||
|
|
3833bcf7c1 | ||
|
|
d85dc372a7 | ||
|
|
ca8bcd966f | ||
|
|
12c7318625 | ||
|
|
4f917b94e8 | ||
|
|
9e82cb2516 | ||
|
|
bbf85d5804 | ||
|
|
9daa85750b | ||
|
|
6f7c3b0681 | ||
|
|
d262bb6136 | ||
|
|
7509934394 | ||
|
|
a0ae226506 | ||
|
|
0dde8d27e6 | ||
|
|
559e728bc3 | ||
|
|
dcea1bc67f | ||
|
|
7dc451f344 | ||
|
|
b6ea6a6850 | ||
|
|
768a6df247 | ||
|
|
5c9c8b0b3b | ||
|
|
085972f2d3 | ||
|
|
ddefd1aa24 | ||
|
|
53f441884e | ||
|
|
954c5a1027 | ||
|
|
c26ab8ff60 | ||
|
|
9ea9f9b307 | ||
|
|
fc5ba24e76 | ||
|
|
0fe496a290 | ||
|
|
df0eb21a9e | ||
|
|
79ed7da316 | ||
|
|
908a8b89a6 | ||
|
|
83d4d6f7e9 | ||
|
|
a588906f43 | ||
|
|
e890fd3e9e | ||
|
|
e7c95ac72c | ||
|
|
245765c6af | ||
|
|
f8ae4dcecd | ||
|
|
f694c7c821 | ||
|
|
a4824b6766 | ||
|
|
834533aa6a | ||
|
|
0c9cf2c83d | ||
|
|
fbffeff8dc | ||
|
|
8532ca2d38 | ||
|
|
722bf5ba7b | ||
|
|
ca1cf72d86 | ||
|
|
e12936fb66 | ||
|
|
9b85742c00 | ||
|
|
aceaef14c5 | ||
|
|
c168364ca4 | ||
|
|
db6533795b | ||
|
|
596c0cd8c3 | ||
|
|
b65cec2916 | ||
|
|
63ef406058 | ||
|
|
4856e9feee | ||
|
|
e839813c2a | ||
|
|
c70f2a6684 | ||
|
|
860ec50071 | ||
|
|
93e26fb31f | ||
|
|
23e2e18019 | ||
|
|
a0d5a1fbad | ||
|
|
b1b5ef7236 | ||
|
|
6ab2aa3413 | ||
|
|
16819bb0e7 | ||
|
|
0bf3488a01 | ||
|
|
1f3d3e90cf | ||
|
|
d141e6b46d | ||
|
|
e683dedc2a | ||
|
|
c7d6107cea | ||
|
|
ef7e76dc07 | ||
|
|
5697fa53f0 | ||
|
|
59d3475b31 | ||
|
|
f4f6cbee38 | ||
|
|
1664b917f8 | ||
|
|
55587908a2 | ||
|
|
e1ed483d67 | ||
|
|
c460bac177 | ||
|
|
ad6771f8f4 | ||
|
|
ad1a68cefe | ||
|
|
e8ab8fda71 | ||
|
|
7def27e437 | ||
|
|
f3db0ed61d | ||
|
|
fdcff83a2d | ||
|
|
8815758bc0 | ||
|
|
ea49ea7818 | ||
|
|
1aaea961fb | ||
|
|
8253b96a67 | ||
|
|
725b8bc614 | ||
|
|
6593fcc250 | ||
|
|
f51376f9c3 | ||
|
|
23ecc3dd05 | ||
|
|
9e6f27162e | ||
|
|
6c279e0986 | ||
|
|
76d1291180 | ||
|
|
627f09010b | ||
|
|
9afd0e6fc3 | ||
|
|
1ea361cce9 | ||
|
|
41bf22c43a | ||
|
|
71989292d3 | ||
|
|
f46b13da6a | ||
|
|
875cd2c1b9 | ||
|
|
db972aba4a | ||
|
|
714412246b | ||
|
|
db37721e9f | ||
|
|
c373d24fe1 | ||
|
|
2770cbbe35 | ||
|
|
699a47c633 | ||
|
|
47ebc6c6ca | ||
|
|
bdb47532b9 | ||
|
|
b5a40adcdf | ||
|
|
955422aab7 | ||
|
|
cfa55095aa | ||
|
|
bcac005fb1 | ||
|
|
aa6536b57c | ||
|
|
ab1f169fcc | ||
|
|
ce5928e783 | ||
|
|
e3e40627e8 | ||
|
|
140158f5bb | ||
|
|
58a5412602 | ||
|
|
aeec991e7b | ||
|
|
7751b13611 | ||
|
|
e0cbc0bdc3 | ||
|
|
2f64ab1018 | ||
|
|
6c74d30ce8 | ||
|
|
25956d5f23 | ||
|
|
f6832cd44c | ||
|
|
313e9dadb6 | ||
|
|
1d171268ed |
@@ -42,6 +42,8 @@
|
||||
- Added new optional property `attachment_options` for most EmitterShapes to be attached to the locator/entities scale, rotation, and/or position. Position is true by default.
|
||||
- Galarica Nut bushes now generate on beaches.
|
||||
- Some Pokémon now pitch their bodies in the direction they're moving.
|
||||
- Added new advancements Didn't Stop To Think, We Need To Cook, and Pokémon Jockey!.
|
||||
- Berries can now be smelted into their respective dyes.
|
||||
- Added Syrupy Apples.
|
||||
- Added `/runmolang <molang> [<npc>|<player>|<pokemon>]` command that executes a MoLang expression with the provided options as environment variables, as well as the entity (as `q.entity`) that executed the command.
|
||||
|
||||
@@ -301,6 +303,7 @@
|
||||
- Offset in EmitterShape now ignores scale to be more like Blockbench by default. You can get this behaviour back by adding `"scale": true` in the `attachment_options` property in most EmitterShapes.
|
||||
- Not specifying a dex in `/pokedex printcalculations {player} {dex}` will now print the National Dex statistics instead of showing all dexes. `/pokedex printcalculations {player} all` is how to view all dex statistics in one command.
|
||||
- Removed Braised Vivichoke
|
||||
- Updated Pokémon state icons shown in party interfaces
|
||||
- MoLang `face` function can now be run on any `PosableEntity` (Like Pokémon!) instead of just NPCs.
|
||||
- MoLang `run_action_effect` now works on Pokémon.
|
||||
- Changed MoLang entity function `is_standing_on` to allow for block tags in the list.
|
||||
@@ -455,6 +458,8 @@
|
||||
- Added Molang functions for Pokémon: `pokeball`, `held_item`, `remove_held_item`, `hyper_train_iv`, `validate_moveset`, `initialize_moveset`, and `add_exp`.
|
||||
- Added Molang functions for Pokémon: `aspects`, `form_aspects`, `unlearn_move`, `teach_learnable_moves`, `cosmetic_item`, and `remove_cosmetic_item`.
|
||||
- Added Molang functions for Pokémon: `ability`, `set_iv`, `set_ev`, `teach_move`, and `can_learn_move`.
|
||||
- Added Molang function `q.delete_variable(<struct>, <variable_name>)` to delete a value from a variable structure in MoLang data.
|
||||
- Added Molang function `q.delete_variables(<struct>)` to delete all values from a variable structure in MoLang data.
|
||||
- Adds Flows for `STARTER_CHOSEN`, `SHOULDER_MOUNTED`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
|
||||
- Adds Flows for `POKEMON_HEALED`, `POKEMON_SCANNED`, `BERRY_HARVEST`, `LOOT_DROPPED`, `POKEMON_SEEN`, `COLLECT_EGG`, `HATCH_EGG`, and `EXPERIENCE_GAINED`.
|
||||
- Adds Flows for `POKEMON_CATCH_RATE`, `BAIT_SET`, `BAIT_SET_PRE`, `BAIT_CONSUMED`, `POKEROD_CAST_PRE`, `POKEROD_CAST_POST`, `POKEROD_REEL`, and `BOBBER_SPAWN_POKEMON_PRE`.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface DoubleJump {
|
||||
boolean isDoubleJumping();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.duck;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
public interface PlayerDuck {
|
||||
void setDriverInput(Vector3f driverInput);
|
||||
Vector3f getDriverInput();
|
||||
void setLastSentDriverInput(Vector3f driverInput);
|
||||
Vector3f getLastSentDriverInput();
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
|
||||
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.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.food.FoodProperties;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -61,7 +63,12 @@ import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mixin(Player.class)
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable {
|
||||
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck {
|
||||
|
||||
@Unique
|
||||
private Vector3f cobblemon$driverInput;
|
||||
@Unique
|
||||
private Vector3f cobblemon$lastSentDriverInput;
|
||||
|
||||
@Shadow public abstract CompoundTag getShoulderEntityLeft();
|
||||
|
||||
@@ -81,8 +88,8 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
|
||||
@Shadow @Final private static Map<Pose, EntityDimensions> POSES;
|
||||
@Shadow @Final public static EntityDimensions STANDING_DIMENSIONS;
|
||||
@Unique
|
||||
private final OrientationController cobblemon$orientationController = new OrientationController(this);
|
||||
@Shadow protected int jumpTriggerTime;
|
||||
@Unique private final OrientationController cobblemon$orientationController = new OrientationController(this);
|
||||
|
||||
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
|
||||
super(p_20966_, p_20967_);
|
||||
@@ -279,4 +286,25 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
|
||||
public OrientationController getOrientationController() {
|
||||
return cobblemon$orientationController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDriverInput(Vector3f driverInput) {
|
||||
cobblemon$driverInput = driverInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f getDriverInput() {
|
||||
return cobblemon$driverInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastSentDriverInput(Vector3f lastSentDriverInput) {
|
||||
cobblemon$lastSentDriverInput = lastSentDriverInput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f getLastSentDriverInput() {
|
||||
return cobblemon$lastSentDriverInput;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ import com.cobblemon.mod.common.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.net.NetworkPacket;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.duck.PlayerDuck;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.mixin.accessor.ChunkMapAccessor;
|
||||
import com.cobblemon.mod.common.mixin.accessor.TrackedEntityAccessor;
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.client.pokemon.update.ClientboundUpdateRidingStatePacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@@ -26,6 +28,7 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector3f;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -55,17 +58,20 @@ public abstract class ServerEntityMixin {
|
||||
private void cobblemon$sendChanges(CallbackInfo ci) {
|
||||
cobblemon$sendOrientationChanges();
|
||||
cobblemon$sendRidingStateChanges();
|
||||
cobblemon$sendDriverInput();
|
||||
}
|
||||
|
||||
private void cobblemon$sendRidingStateChanges() {
|
||||
if (!(this.entity instanceof PokemonEntity pokemonEntity)) return;
|
||||
if (pokemonEntity.getRidingState() == null) return;
|
||||
if (pokemonEntity.getRiding() == null) return;
|
||||
var ridingBehaviour = pokemonEntity.getRiding();
|
||||
var ridingState = pokemonEntity.getRidingState();
|
||||
if (pokemonEntity.getRidingController() == null) return;
|
||||
var ridingController = pokemonEntity.getRidingController();
|
||||
if (ridingController.getContext() == null) return;
|
||||
var context = ridingController.getContext();
|
||||
var ridingBehaviour = context.getBehaviour();
|
||||
var ridingState = context.getState();
|
||||
var previousRidingState = pokemonEntity.getPreviousRidingState();
|
||||
if (previousRidingState != null && !ridingState.shouldSync(previousRidingState)) return;
|
||||
cobblemon$broadcast(new ClientboundUpdateRidingStatePacket(pokemonEntity.getId(), ridingBehaviour.getKey(), pokemonEntity.getRidingState(), null));
|
||||
cobblemon$broadcast(new ClientboundUpdateRidingStatePacket(pokemonEntity.getId(), ridingBehaviour, ridingState, null));
|
||||
}
|
||||
|
||||
private void cobblemon$sendOrientationChanges() {
|
||||
@@ -82,6 +88,19 @@ public abstract class ServerEntityMixin {
|
||||
cobblemon$broadcast(new ClientboundUpdateOrientationPacket(currOrientation, currActive, entity.getId()));
|
||||
}
|
||||
|
||||
private void cobblemon$sendDriverInput() {
|
||||
if (!(this.entity instanceof ServerPlayer serverPlayer)) return;
|
||||
if(!(serverPlayer.getVehicle() instanceof PokemonEntity)) return;
|
||||
Vector3f driverInput = ((PlayerDuck)serverPlayer).getDriverInput();
|
||||
if(driverInput == null) return;
|
||||
|
||||
// If no change in input is detected then don't send a new packet
|
||||
Vector3f lastSentDriverInput = ((PlayerDuck)serverPlayer).getLastSentDriverInput();
|
||||
if (driverInput.equals(lastSentDriverInput)) return;
|
||||
((PlayerDuck)serverPlayer).setLastSentDriverInput(driverInput);
|
||||
cobblemon$broadcast(new ClientboundUpdateDriverInputPacket(driverInput, entity.getId()));
|
||||
}
|
||||
|
||||
private void cobblemon$broadcast(NetworkPacket<?> packet) {
|
||||
if (!(entity.level() instanceof ServerLevel level)) return;
|
||||
ChunkMap chunkMap = level.getChunkSource().chunkMap;
|
||||
@@ -97,5 +116,4 @@ public abstract class ServerEntityMixin {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
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 org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -43,4 +46,10 @@ public abstract class ServerPlayerMixin {
|
||||
controllable.getOrientationController().setActive(false);
|
||||
}
|
||||
|
||||
//TODO: Switch to sending out on load instead of preventing saving
|
||||
@ModifyExpressionValue(method = "addAdditionalSaveData", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hasExactlyOnePlayerPassenger()Z"))
|
||||
private boolean cobblemon$cancelSavingPokemonMounts(boolean original, @Local(ordinal=0) Entity entity) {
|
||||
return original && !(entity instanceof PokemonEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.mixin.accessor;
|
||||
|
||||
import net.minecraft.server.level.ServerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
@@ -18,4 +19,7 @@ import java.util.Set;
|
||||
public interface TrackedEntityAccessor {
|
||||
@Accessor("seenBy")
|
||||
Set<ServerPlayerConnection> getSeenBy();
|
||||
|
||||
@Accessor("serverEntity")
|
||||
ServerEntity getServerEntity();
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork;
|
||||
import com.cobblemon.mod.common.DoubleJump;
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.api.orientation.OrientationController;
|
||||
import com.cobblemon.mod.common.api.riding.Seat;
|
||||
@@ -17,17 +18,21 @@ import com.cobblemon.mod.common.client.render.MatrixWrapper;
|
||||
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.entity.pokemon.PokemonEntity;
|
||||
import com.cobblemon.mod.common.net.messages.server.orientation.ServerboundUpdateOrientationPacket;
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.client.CameraType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
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.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;
|
||||
@@ -46,13 +51,23 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(LocalPlayer.class)
|
||||
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable{
|
||||
public abstract class LocalPlayerMixin extends AbstractClientPlayer implements OrientationControllable, DoubleJump {
|
||||
|
||||
@Unique Matrix3f cobblemon$lastOrientation;
|
||||
|
||||
@Unique boolean cobblemon$isDoubleJumping = false;
|
||||
|
||||
@Shadow
|
||||
private float jumpRidingScale;
|
||||
|
||||
@Shadow public Input input;
|
||||
|
||||
@Shadow private int autoJumpTime;
|
||||
|
||||
@Unique private int cobblemon$survivalJumpTriggerTime;
|
||||
|
||||
@Unique private boolean cobblemon$isJumping;
|
||||
|
||||
public LocalPlayerMixin(ClientLevel clientLevel, GameProfile gameProfile) {
|
||||
super(clientLevel, gameProfile);
|
||||
}
|
||||
@@ -83,6 +98,23 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
controllable.getOrientationController().setActive(shouldUseCustomOrientation);
|
||||
}
|
||||
|
||||
@Inject(method = "rideTick", at = @At("HEAD"))
|
||||
private void cobblemon$updateDriverInputRideTick(CallbackInfo ci) {
|
||||
if (Minecraft.getInstance().player != (Object)this) return;
|
||||
if (!(this.getVehicle() instanceof PokemonEntity pokemonEntity)) return;
|
||||
|
||||
// Gather driver Input
|
||||
float vertInput = this.jumping ? 1.0f : (((Player)this).isShiftKeyDown() ? -1.0f : 0.0f);
|
||||
Vector3f driverInput = new Vector3f(Math.signum(this.xxa), vertInput, Math.signum(this.zza));
|
||||
|
||||
// Check if player input has changed. If it has then update the last sent and send it
|
||||
Vector3f lastSentDriverInput = ((PlayerDuck)this).getLastSentDriverInput();
|
||||
if (driverInput.equals(lastSentDriverInput)) return;
|
||||
((PlayerDuck)this).setLastSentDriverInput(driverInput);
|
||||
|
||||
CobblemonNetwork.INSTANCE.sendToServer(new ServerboundUpdateDriverInputPacket(driverInput));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean cobblemon$shouldUseCustomOrientation(LocalPlayer player) {
|
||||
var playerVehicle = player.getVehicle();
|
||||
@@ -115,6 +147,33 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "aiStep", at = @At("HEAD"))
|
||||
private void cobblemon$aiStep(CallbackInfo ci) {
|
||||
this.cobblemon$isDoubleJumping = false;
|
||||
this.cobblemon$isJumping = this.input.jumping;
|
||||
}
|
||||
|
||||
@Inject(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;isSprinting()Z", shift = At.Shift.BEFORE))
|
||||
private void cobblemon$updateDoubleJumping(CallbackInfo ci) {
|
||||
if (!this.cobblemon$isJumping && this.input.jumping && this.autoJumpTime <= 0 && !this.isSwimming()) {
|
||||
if (this.cobblemon$survivalJumpTriggerTime != 0) {
|
||||
this.cobblemon$isDoubleJumping = true;
|
||||
this.cobblemon$survivalJumpTriggerTime = 0;
|
||||
}
|
||||
else {
|
||||
this.cobblemon$survivalJumpTriggerTime = 7;
|
||||
}
|
||||
}
|
||||
else if (this.cobblemon$survivalJumpTriggerTime > 0) {
|
||||
this.cobblemon$survivalJumpTriggerTime--;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDoubleJumping() {
|
||||
return this.cobblemon$isDoubleJumping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HitResult pick(double hitDistance, float partialTicks, boolean hitFluids) {
|
||||
Entity vehicle = this.getVehicle();
|
||||
@@ -127,7 +186,7 @@ public abstract class LocalPlayerMixin extends AbstractClientPlayer implements O
|
||||
return super.pick(hitDistance, partialTicks, hitFluids);
|
||||
}
|
||||
|
||||
Vec3 locatorOffset = new Vec3(locator.getMatrix().getTranslation(new Vector3f()));
|
||||
Vec3 locatorOffset = locator != null ? new Vec3(locator.getMatrix().getTranslation(new Vector3f())) : Vec3.ZERO;
|
||||
|
||||
OrientationController controller = this.getOrientationController();
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@ import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -33,9 +31,9 @@ public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractC
|
||||
super(context, model, shadowRadius);
|
||||
}
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", at = @At(value = "TAIL"))
|
||||
public void cobblemon$clientPlayerIcon(LivingEntity entity, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight, CallbackInfo ci) {
|
||||
ClientPlayerIcon.Companion.onRenderPlayer((Player) entity);
|
||||
@Inject(method = "render", at = @At(value = "TAIL"))
|
||||
public void renderPlayer(AbstractClientPlayer player, float f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, CallbackInfo ci) {
|
||||
ClientPlayerIcon.Companion.onRenderPlayer(player);
|
||||
}
|
||||
|
||||
@WrapOperation(method = "getRenderOffset(Lnet/minecraft/client/player/AbstractClientPlayer;F)Lnet/minecraft/world/phys/Vec3;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/AbstractClientPlayer;isCrouching()Z"))
|
||||
|
||||
@@ -89,6 +89,7 @@ object CobblemonBehaviours : JsonDataRegistry<CobblemonBehaviour> {
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
autoPokemonBehaviours.clear()
|
||||
autoNPCBehaviours.clear()
|
||||
val data = mutableMapOf<ResourceLocation, CobblemonBehaviour>()
|
||||
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.PokeCakeBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.PCBlockEntity
|
||||
@@ -88,35 +87,23 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
BlockEntityType.Builder.of(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val INCENSE_SWEET: BlockEntityType<SweetIncenseBlockEntity> = this.create("incense_sweet",
|
||||
BlockEntityType.Builder.of(::SweetIncenseBlockEntity, CobblemonBlocks.INCENSE_SWEET).build(null)
|
||||
)
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET: BlockEntityType<SweetIncenseBlockEntity> = this.create("incense_sweet",
|
||||
// BlockEntityType.Builder.of(::SweetIncenseBlockEntity, CobblemonBlocks.INCENSE_SWEET).build(null)
|
||||
// )
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE: BlockEntityType<CampfireBlockEntity> = this.create("campfire_pot",
|
||||
BlockEntityType.Builder.of(::CampfireBlockEntity, CobblemonBlocks.CAMPFIRE).build(null)
|
||||
BlockEntityType.Builder.of(::CampfireBlockEntity,
|
||||
CobblemonBlocks.CAMPFIRE,
|
||||
CobblemonBlocks.SOUL_CAMPFIRE
|
||||
).build(null)
|
||||
)
|
||||
|
||||
/*@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> =
|
||||
BlockEntityType.Builder.of({ pos, state -> LureCakeBlockEntity(pos, state) },
|
||||
CobblemonBlocks.LURE_CAKE
|
||||
).build(null)*/
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE: BlockEntityType<LureCakeBlockEntity> = this.create(
|
||||
"lure_cake",
|
||||
BlockEntityType.Builder.of(::LureCakeBlockEntity, CobblemonBlocks.LURE_CAKE).build(null)
|
||||
)
|
||||
@JvmField
|
||||
val POKE_CAKE: BlockEntityType<PokeCakeBlockEntity> = this.create(
|
||||
"poke_cake",
|
||||
BlockEntityType.Builder.of(::PokeCakeBlockEntity, CobblemonBlocks.POKE_CAKE).build(null)
|
||||
)
|
||||
@JvmField
|
||||
val CANDLE_POKE_CAKE: BlockEntityType<CandlePokeCakeBlockEntity> = this.create(
|
||||
"candle_poke_cake",
|
||||
BlockEntityType.Builder.of(::CandlePokeCakeBlockEntity, CobblemonBlocks.CANDLE_POKE_CAKE).build(null)
|
||||
val POKE_SNACK: BlockEntityType<PokeSnackBlockEntity> = this.create(
|
||||
"poke_snack",
|
||||
BlockEntityType.Builder.of(::PokeSnackBlockEntity, CobblemonBlocks.POKE_SNACK, CobblemonBlocks.POKE_CAKE).build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.BlockSetType
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument
|
||||
import net.minecraft.world.level.block.state.properties.WoodType
|
||||
@@ -230,8 +231,10 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.CRIMSON_NYLIUM).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val BUGWORT = this.create("bugwort", BugwortBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.COLOR_PURPLE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val BUGWORT = this.create("bugwort", BugwortBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.COLOR_PURPLE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.DIRT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
@@ -240,11 +243,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).mapColor(MapColor.PLANT).ignitedByLava().noCollission().instabreak().sound(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE = this.create("lure_cake", LureCakeBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY)))
|
||||
val POKE_CAKE = this.create("poke_cake", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, false))
|
||||
@JvmField
|
||||
val POKE_CAKE = this.create("poke_cake", PokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE)))
|
||||
@JvmField
|
||||
val CANDLE_POKE_CAKE = this.create("candle_poke_cake", CandlePokeCakeBlock(BlockBehaviour.Properties.ofFullCopy(LURE_CAKE).lightLevel { blockState -> if (blockState.getValue(LIT) == true) 3 else 0 }))
|
||||
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(LIT)) 3 else 0 }, true))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@@ -449,7 +450,10 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel { if ((it.getValue(CampfireBlock.SOUL) as Boolean)) 9 else 14 }))
|
||||
val CAMPFIRE = create("campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{14}, false))
|
||||
|
||||
@JvmField
|
||||
val SOUL_CAMPFIRE = create("soul_campfire", CampfireBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.PODZOL).strength(2.0F).lightLevel{9}, true))
|
||||
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
|
||||
@@ -492,8 +496,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
|
||||
|
||||
@JvmField
|
||||
val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET = create("incense_sweet", SweetIncenseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3f)))
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 13 else 0 }))
|
||||
@@ -517,7 +522,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
|
||||
@JvmField
|
||||
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak().sound(CobblemonSounds.VIVICHOKE_SOUNDS)))
|
||||
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak()))
|
||||
|
||||
@JvmField
|
||||
val GALARICA_NUT_BUSH = this.create("galarica_nut_bush", NutBushBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().sound(SoundType.SWEET_BERRY_BUSH).pushReaction(PushReaction.DESTROY)))
|
||||
@@ -540,11 +545,11 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", BaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(SoundType.GRASS)))
|
||||
val HEARTY_GRAIN_BALE = this.create("hearty_grain_bale", BaleBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).instrument(NoteBlockInstrument.BANJO).strength(0.5F).sound(CobblemonSounds.HEARTY_GRAIN_BALE_SOUNDS)))
|
||||
@JvmField
|
||||
val TATAMI_BLOCK = this.create("tatami_block", HorizontalRotationalBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(SoundType.MOSS_CARPET)))
|
||||
val TATAMI_BLOCK = this.create("tatami_block", HorizontalRotationalBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(CobblemonSounds.TATAMI_BLOCK_SOUNDS)))
|
||||
@JvmField
|
||||
val TATAMI_MAT = this.create("tatami_mat", HorizontalRotationCarpetBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(SoundType.MOSS_CARPET)))
|
||||
val TATAMI_MAT = this.create("tatami_mat", HorizontalRotationCarpetBlock(BlockBehaviour.Properties.of().ignitedByLava().mapColor(MapColor.PLANT).strength(0.1F).sound(CobblemonSounds.TATAMI_MAT_SOUNDS)))
|
||||
|
||||
/**
|
||||
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
|
||||
|
||||
@@ -349,15 +349,15 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SACCHARINE_SAPLING = compostableBlockItem("saccharine_sapling", CobblemonBlocks.SACCHARINE_SAPLING, 0.30f)
|
||||
|
||||
@JvmField
|
||||
val BUGWORT = compostableBlockItem("bugwort", CobblemonBlocks.BUGWORT)
|
||||
// @JvmField
|
||||
// val BUGWORT = compostableBlockItem("bugwort", CobblemonBlocks.BUGWORT) // TODO after 1.7
|
||||
@JvmField
|
||||
val POKE_BAIT = noSettingsItem("poke_bait")
|
||||
|
||||
@JvmField
|
||||
val LURE_CAKE = blockItem("lure_cake", CobblemonBlocks.LURE_CAKE)
|
||||
val POKE_CAKE = this.create("poke_cake", BlockItem(CobblemonBlocks.POKE_CAKE, Item.Properties().stacksTo(1)))
|
||||
@JvmField
|
||||
val POKE_CAKE = blockItem("poke_cake", CobblemonBlocks.POKE_CAKE)
|
||||
val POKE_SNACK = this.create("poke_snack", BlockItem(CobblemonBlocks.POKE_SNACK, Item.Properties().stacksTo(16)))
|
||||
|
||||
val aprijuices = mutableListOf<AprijuiceItem>()
|
||||
@JvmField
|
||||
@@ -386,31 +386,31 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
// FOODS
|
||||
@JvmField
|
||||
val SWEET_HEART = noSettingsItem("sweet_heart") // todo make a SweetHeartItem class for breeding purposes
|
||||
val SWEET_HEART = noSettingsItem("sweet_heart") // TODO make a SweetHeartItem class for breeding purposes
|
||||
|
||||
@JvmField
|
||||
val TASTY_TAIL = create("tasty_tail", foodItem(3, 0.3f))
|
||||
|
||||
@JvmField
|
||||
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 16, 7, 0.343f, false)
|
||||
val PEWTER_CRUNCHIES = regionalFoodItem("pewter_crunchies", 64, 7, 0.343f, false)
|
||||
@JvmField
|
||||
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 16, 8, 0.2f, false)
|
||||
val RAGE_CANDY_BAR = regionalFoodItem("rage_candy_bar", 64, 8, 0.2f, false)
|
||||
@JvmField
|
||||
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 16, 7, 0.343f, false)
|
||||
val LAVA_COOKIE = regionalFoodItem("lava_cookie", 64, 2, 0.3f, false)
|
||||
@JvmField
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 16, 9, 0.1335f, false)
|
||||
val OLD_GATEAU = regionalFoodItem("old_gateau", 64, 9, 0.1335f, false)
|
||||
@JvmField
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 16, 5, .28f, false)
|
||||
val CASTELIACONE = regionalFoodItem("casteliacone", 64, 5, .28f, false)
|
||||
@JvmField
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 16, 8, 0.2f, false)
|
||||
val LUMIOSE_GALETTE = regionalFoodItem("lumiose_galette", 64, 4, 0.4f, false)
|
||||
@JvmField
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 16, 7, 0.343f, false)
|
||||
val BIG_MALASADA = regionalFoodItem("big_malasada", 64, 7, 0.343f, false)
|
||||
@JvmField
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 16, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
|
||||
val SMOKED_TAIL_CURRY = regionalFoodItem("smoked_tail_curry", 64, 10, 0.6f, false, ItemStack(Items.BOWL, 1))
|
||||
@JvmField
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 16, 8, 0.2f, false)
|
||||
val JUBILIFE_MUFFIN = regionalFoodItem("jubilife_muffin", 64, 8, 0.2f, false)
|
||||
@JvmField
|
||||
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 16, 13, 0.5f, false)
|
||||
val OPEN_FACED_SANDWICH = regionalFoodItem("open_faced_sandwich", 64, 13, 0.5f, false)
|
||||
|
||||
// todo we might need to wait on these for later? These impact battles and may be harder to do
|
||||
/*@JvmField
|
||||
@@ -421,20 +421,20 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val TWICE_SPICED_BEETROOT = noSettingsItem("twice_spiced_beetroot") // todo make a TwiceSpiceBeetrootItem class for battle purposes
|
||||
*/
|
||||
@JvmField
|
||||
val POTATO_MOCHI = create("potato_mochi", CobblemonItem(Properties().stacksTo(16)
|
||||
val POTATO_MOCHI = create("potato_mochi", CobblemonItem(Properties().stacksTo(64)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(8)
|
||||
.saturationModifier(0.2375F)
|
||||
.nutrition(4)
|
||||
.saturationModifier(0.4F)
|
||||
.build())))
|
||||
@JvmField
|
||||
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(16)
|
||||
val CANDIED_APPLE = create("candied_apple", CobblemonItem(Properties().stacksTo(64)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(6)
|
||||
.saturationModifier(0.2335F)
|
||||
.usingConvertsTo(Items.STICK)
|
||||
.build())))
|
||||
@JvmField
|
||||
val CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(16)
|
||||
val CANDIED_BERRY = create("candied_berry", CobblemonItem(Properties().stacksTo(64)
|
||||
.food(FoodProperties.Builder()
|
||||
.nutrition(5)
|
||||
.saturationModifier(0.22F)
|
||||
@@ -460,8 +460,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@JvmField
|
||||
val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET)
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET = blockItem("incense_sweet", CobblemonBlocks.INCENSE_SWEET) // TODO after 1.7
|
||||
|
||||
|
||||
// Evolution items
|
||||
@@ -695,7 +695,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SUPERB_REMEDY = compostableItem("superb_remedy", RemedyItem(RemedyItem.SUPERB), 0.85f)
|
||||
@JvmField
|
||||
val MOOMOO_MILK = noSettingsItem("moomoo_milk")
|
||||
val MOOMOO_MILK = create("moomoo_milk", CobblemonItem(Item.Properties().craftRemainder(Items.GLASS_BOTTLE)))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", PotionItem(PotionType.POTION))
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
|
||||
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.PokeSnackBlockParticlesHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.RunPosableMoLangHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SaccharineLogBlockParticlesHandler
|
||||
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
|
||||
@@ -41,6 +42,7 @@ import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHand
|
||||
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.client.net.pokemon.update.PokemonUpdatePacketHandler
|
||||
import com.cobblemon.mod.common.client.net.riding.ClientboundUpdateDriverInputHandler
|
||||
import com.cobblemon.mod.common.client.net.settings.OpenCobblemonConfigEditorHandler
|
||||
import com.cobblemon.mod.common.client.net.settings.ServerSettingsPacketHandler
|
||||
import com.cobblemon.mod.common.client.net.sound.UnvalidatedPlaySoundS2CPacketHandler
|
||||
@@ -77,12 +79,14 @@ import com.cobblemon.mod.common.net.messages.client.cooking.ToggleCookingPotLidP
|
||||
import com.cobblemon.mod.common.net.messages.client.data.*
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SaccharineLogBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.CloseNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.npc.OpenNPCEditorPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateOrientationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
|
||||
@@ -173,6 +177,7 @@ import com.cobblemon.mod.common.net.messages.server.pokemon.update.SetMarkingsPa
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.evolution.AcceptEvolutionPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.DismountPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.riding.ServerboundUpdateDriverInputPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.SwapPCPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
@@ -223,6 +228,7 @@ import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetActiveMarkH
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.SetMarkingsHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.pokemon.update.ServerboundUpdateRidingStateHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DismountPokemonPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.riding.DriverInputPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.RequestStarterScreenHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.starter.SelectStarterPacketHandler
|
||||
import com.cobblemon.mod.common.net.serverhandling.storage.BenchMoveHandler
|
||||
@@ -405,6 +411,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(SpawnSnowstormEntityParticlePacket.ID, SpawnSnowstormEntityParticlePacket::decode, SpawnSnowstormEntityParticleHandler))
|
||||
list.add(PacketRegisterInfo(RunPosableMoLangPacket.ID, RunPosableMoLangPacket::decode, RunPosableMoLangHandler))
|
||||
list.add(PacketRegisterInfo(SaccharineLogBlockParticlesPacket.ID, SaccharineLogBlockParticlesPacket::decode, SaccharineLogBlockParticlesHandler))
|
||||
list.add(PacketRegisterInfo(PokeSnackBlockParticlesPacket.ID, PokeSnackBlockParticlesPacket::decode, PokeSnackBlockParticlesHandler))
|
||||
|
||||
// Hax
|
||||
list.add(PacketRegisterInfo(UnvalidatedPlaySoundS2CPacket.ID, UnvalidatedPlaySoundS2CPacket::decode, UnvalidatedPlaySoundS2CPacketHandler))
|
||||
@@ -465,6 +472,7 @@ object CobblemonNetwork {
|
||||
|
||||
// Riding
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateRidingStatePacket.ID, ClientboundUpdateRidingStatePacket::decode, ClientboundUpdateRidingStateHandler))
|
||||
list.add(PacketRegisterInfo(ClientboundUpdateDriverInputPacket.ID, ClientboundUpdateDriverInputPacket::decode, ClientboundUpdateDriverInputHandler))
|
||||
|
||||
return list
|
||||
}
|
||||
@@ -571,6 +579,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatsPacket.ID, ServerboundUpdateRidingStatsPacket::decode, ServerboundUpdateRidingStatsHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateRidingStatRangePacket.ID, ServerboundUpdateRidingStatRangePacket::decode, ServerboundUpdateRidingStatRangeHandler))
|
||||
list.add(PacketRegisterInfo(DismountPokemonPacket.ID, DismountPokemonPacket::decode, DismountPokemonPacketHandler))
|
||||
list.add(PacketRegisterInfo(ServerboundUpdateDriverInputPacket.ID, ServerboundUpdateDriverInputPacket::decode, DriverInputPacketHandler))
|
||||
|
||||
// Cooking
|
||||
list.add(PacketRegisterInfo(ToggleCookingPotLidPacket.ID, ToggleCookingPotLidPacket::decode, ToggleCookingPotLidHandler))
|
||||
|
||||
@@ -26,11 +26,12 @@ object CobblemonPoiTypes: PlatformRegistry<Registry<PoiType>, ResourceKey<Regist
|
||||
@JvmField
|
||||
val NURSE_KEY = createKey("nurse", CobblemonBlocks.HEALING_MACHINE, 1, 1)
|
||||
@JvmField
|
||||
val LURE_CAKE_KEY = createKey("lure_cake", CobblemonBlocks.LURE_CAKE, 0, 1)
|
||||
val POKE_SNACK_KEY = createKey("poke_snack", CobblemonBlocks.POKE_SNACK, 0, 1)
|
||||
@JvmField
|
||||
val SACCHARINE_LOG_SLATHERED_KEY = createKey("saccharine_log_slathered", CobblemonBlocks.SACCHARINE_LOG_SLATHERED, 0, 1)
|
||||
@JvmField
|
||||
val INCENSE_SWEET_KEY = createKey("incense_sweet", CobblemonBlocks.INCENSE_SWEET, 0, 1)
|
||||
// TODO after 1.7
|
||||
// @JvmField
|
||||
// val INCENSE_SWEET_KEY = createKey("incense_sweet", CobblemonBlocks.INCENSE_SWEET, 0, 1)
|
||||
|
||||
private fun createKey(string: String, block: Block, maxTickets: Int, validRange: Int ): ResourceKey<PoiType> =
|
||||
ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, cobblemonResource(string)).also { resourceKey ->
|
||||
|
||||
@@ -204,6 +204,19 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_STEP = this.create("block.evolution_stone_block.step")
|
||||
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_BREAK = this.create("block.tatami.break")
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_HIT = this.create("block.tatami.hit")
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_PLACE = this.create("block.tatami.place")
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_STEP = this.create("block.tatami.step")
|
||||
@JvmField
|
||||
val TATAMI_MAT_BREAK = this.create("block.tatami_mat.break")
|
||||
@JvmField
|
||||
val TATAMI_MAT_PLACE = this.create("block.tatami_mat.place")
|
||||
|
||||
@JvmField
|
||||
val GIMMIGHOUL_GIVE_ITEM = this.create("pokemon.gimmighoul.give_item")
|
||||
@JvmField
|
||||
@@ -230,6 +243,24 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val VIVICHOKE_PLACE = this.create("block.vivichoke.place")
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_BREAK = this.create("block.hearty_grain_bale.break")
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_PLACE = this.create("block.hearty_grain_bale.place")
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_HIT = this.create("block.hearty_grain_bale.hit")
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_STEP = this.create("block.hearty_grain_bale.step")
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_BREAK = this.create("block.hearty_grains.break")
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_PLACE = this.create("block.hearty_grains.place")
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_BREAK_WATER = this.create("block.hearty_grains.break_water")
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_PLACE_WATER = this.create("block.hearty_grains.place_water")
|
||||
|
||||
@JvmField
|
||||
val MINT_BREAK = this.create("block.mint.break")
|
||||
@JvmField
|
||||
@@ -324,8 +355,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val IMPACT_WATER = this.create("impact.water")
|
||||
|
||||
|
||||
|
||||
// Note to self or whoever's peeking, block sounds have to be at 1.1f pitch
|
||||
// For some reason, the game pitches them down and compensating for it here returns them to normal
|
||||
@JvmField
|
||||
val TUMBLESTONE_SOUNDS = SoundType(1f, 1.1f,
|
||||
TUMBLESTONE_BREAK,
|
||||
@@ -343,6 +374,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
TUMBLESTONE_HIT,
|
||||
TUMBLESTONE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val EVOLUTION_STONE_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
EVOLUTION_STONE_BLOCK_BREAK,
|
||||
@@ -352,6 +384,23 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
EVOLUTION_STONE_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TATAMI_BLOCK_SOUNDS = SoundType(1f, 1.1f,
|
||||
TATAMI_BLOCK_BREAK,
|
||||
TATAMI_BLOCK_STEP,
|
||||
TATAMI_BLOCK_PLACE,
|
||||
TATAMI_BLOCK_HIT,
|
||||
TATAMI_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val TATAMI_MAT_SOUNDS = SoundType(1f, 1.1f,
|
||||
TATAMI_MAT_BREAK,
|
||||
TATAMI_BLOCK_STEP,
|
||||
TATAMI_MAT_PLACE,
|
||||
TATAMI_BLOCK_HIT,
|
||||
TATAMI_BLOCK_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BERRY_BUSH_SOUNDS = SoundType(0.8f, 1.1f,
|
||||
@@ -398,6 +447,33 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
SoundEvents.GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAIN_BALE_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
HEARTY_GRAIN_BALE_BREAK,
|
||||
HEARTY_GRAIN_BALE_STEP,
|
||||
HEARTY_GRAIN_BALE_PLACE,
|
||||
HEARTY_GRAIN_BALE_HIT,
|
||||
HEARTY_GRAIN_BALE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
HEARTY_GRAINS_BREAK,
|
||||
HEARTY_GRAIN_BALE_STEP,
|
||||
HEARTY_GRAINS_PLACE,
|
||||
HEARTY_GRAIN_BALE_HIT,
|
||||
HEARTY_GRAIN_BALE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val HEARTY_GRAINS_WATER_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
HEARTY_GRAINS_BREAK_WATER,
|
||||
SoundEvents.GRASS_STEP,
|
||||
HEARTY_GRAINS_PLACE_WATER,
|
||||
SoundEvents.GRASS_HIT,
|
||||
SoundEvents.GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MINT_SOUNDS = SoundType(0.6f, 1.1f,
|
||||
MINT_BREAK,
|
||||
@@ -437,7 +513,6 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
val DISPLAY_CASE_PLACE = this.create("block.display_case.place")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_STEP = this.create("block.display_case.step")
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE_SOUNDS = SoundType(1f, 1f,
|
||||
DISPLAY_CASE_BREAK,
|
||||
@@ -448,7 +523,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PLACE_CAMPFIRE = this.create("block.campfire_pot.place_campfire")
|
||||
val CAMPFIRE_POT_SET = this.create("block.campfire_pot.set")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_RETRIEVE = this.create("block.campfire_pot.retrieve")
|
||||
@JvmField
|
||||
@@ -456,12 +531,14 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_CLOSE = this.create("block.campfire_pot.close")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_USE = this.create("block.campfire_pot.use")
|
||||
val CAMPFIRE_POT_TAKE_ITEM = this.create("block.campfire_pot.take_item")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_ACTIVE = this.create("block.campfire_pot.active")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_AMBIENT = this.create("block.campfire_pot.ambient")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_COOK = this.create("block.campfire_pot.cook")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_CRAFT = this.create("block.campfire_pot.craft")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_PLACE = this.create("block.campfire_pot.place")
|
||||
@JvmField
|
||||
val CAMPFIRE_POT_BREAK = this.create("block.campfire_pot.break")
|
||||
|
||||
@@ -15,9 +15,12 @@ import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import java.io.File
|
||||
import kotlin.collections.set
|
||||
|
||||
/**
|
||||
* Registry for all known Abilities
|
||||
@@ -31,15 +34,31 @@ object Abilities : DataRegistry {
|
||||
val DUMMY = AbilityTemplate(name = "dummy")
|
||||
|
||||
private val abilityMap = mutableMapOf<String, AbilityTemplate>()
|
||||
internal val abilityScripts = mutableMapOf<String, String>() // abilityId to JavaScript
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
PotentialAbility.types.clear()
|
||||
PotentialAbility.types.add(CommonAbilityType)
|
||||
PotentialAbility.types.add(HiddenAbilityType)
|
||||
this.abilityMap.clear()
|
||||
val abilitiesJson = ShowdownService.service.getAbilityIds()
|
||||
this.abilityScripts.clear()
|
||||
|
||||
ShowdownService.service.resetRegistryData("ability")
|
||||
manager.listResources("abilities") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
val js = reader.readText()
|
||||
abilityScripts[resolvedIdentifier.path] = js
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowdownService.service.sendRegistryData(abilityScripts, "ability")
|
||||
|
||||
val abilitiesJson = ShowdownService.service.getRegistryData("ability")
|
||||
for (i in 0 until abilitiesJson.size()) {
|
||||
val id = abilitiesJson[i].asString
|
||||
val jsonAbility = abilitiesJson[i].asJsonObject
|
||||
val id = jsonAbility.get("id").asString
|
||||
val ability = AbilityTemplate(id)
|
||||
this.register(ability)
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ class Berry(
|
||||
val fruitModelIdentifier: ResourceLocation,
|
||||
val fruitTexture: ResourceLocation,
|
||||
val stageOnePositioning: GrowthPoint,
|
||||
val pokeSnackPositionings: Array<GrowthPoint>,
|
||||
val weight: Float,
|
||||
val boneMealChance: Float
|
||||
) {
|
||||
@@ -293,6 +294,14 @@ class Berry(
|
||||
buffer.writeDouble(stageOnePositioning.rotation.x)
|
||||
buffer.writeDouble(stageOnePositioning.rotation.y)
|
||||
buffer.writeDouble(stageOnePositioning.rotation.z)
|
||||
buffer.writeCollection(this.pokeSnackPositionings.toList()) { writer, value ->
|
||||
writer.writeDouble(value.position.x)
|
||||
writer.writeDouble(value.position.y)
|
||||
writer.writeDouble(value.position.z)
|
||||
writer.writeDouble(value.rotation.x)
|
||||
writer.writeDouble(value.rotation.y)
|
||||
writer.writeDouble(value.rotation.z)
|
||||
}
|
||||
buffer.writeFloat(boneMealChance)
|
||||
}
|
||||
|
||||
@@ -360,9 +369,12 @@ class Berry(
|
||||
val flowerTexture = buffer.readIdentifier()
|
||||
val fruitModelIdentifier = buffer.readIdentifier()
|
||||
val fruitTexture = buffer.readIdentifier()
|
||||
val stageOneYPos = GrowthPoint(Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()), Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()))
|
||||
val stageOnePos = GrowthPoint(Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()), Vec3(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()))
|
||||
val pokeSnackPos = buffer.readList { reader ->
|
||||
GrowthPoint(Vec3(reader.readDouble(), reader.readDouble(), reader.readDouble()), Vec3(reader.readDouble(), reader.readDouble(), reader.readDouble()))
|
||||
}.toTypedArray()
|
||||
val boneMealChance = buffer.readFloat()
|
||||
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, colour, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, stageOneYPos, 0F, boneMealChance)
|
||||
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, colour, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, stageOnePos, pokeSnackPos, 0F, boneMealChance)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.bedrockk.molang.runtime.MoLangEnvironment
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.struct.MoStruct
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
@@ -157,6 +158,17 @@ object MoLangFunctions {
|
||||
val message = params.get<MoValue>(0).asString()
|
||||
Cobblemon.LOGGER.info(message)
|
||||
},
|
||||
"delete_variable" to java.util.function.Function { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
struct.map.remove(variable)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"delete_variables" to java.util.function.Function { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
struct.map.clear()
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"set_query" to java.util.function.Function { params ->
|
||||
val variable = params.getString(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
@@ -1664,6 +1676,8 @@ object MoLangFunctions {
|
||||
map.put("get_alt_pose") { StringValue(pokemonEntity.getAltPose()) }
|
||||
map.put("is_gliding") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("gliding"))) }
|
||||
map.put("is_sprinting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("sprinting"))) }
|
||||
map.put("is_drifting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("drifting"))) }
|
||||
map.put("is_powered_drifting") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("powered_drifting"))) }
|
||||
map.put("in_air") { DoubleValue(pokemonEntity.isUsingAltPose(cobblemonResource("in_air"))) }
|
||||
map.put("is_wild") { DoubleValue(pokemonEntity.ownerUUID == null) }
|
||||
map.put("is_in_party") { DoubleValue(pokemonEntity.pokemon.storeCoordinates.get()?.store is PartyStore) }
|
||||
|
||||
@@ -21,9 +21,12 @@ import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
import java.io.File
|
||||
import kotlin.collections.set
|
||||
|
||||
/**
|
||||
* Registry for all known Moves
|
||||
@@ -36,11 +39,26 @@ object Moves : DataRegistry {
|
||||
|
||||
private val allMoves = mutableMapOf<String, MoveTemplate>()
|
||||
private val idMapping = mutableMapOf<Int, MoveTemplate>()
|
||||
internal val moveScripts = mutableMapOf<String, String>() // moveId to JavaScript
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.allMoves.clear()
|
||||
this.idMapping.clear()
|
||||
val movesJson = ShowdownService.service.getMoves()
|
||||
this.moveScripts.clear()
|
||||
|
||||
ShowdownService.service.resetRegistryData("move")
|
||||
manager.listResources("moves") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
val js = reader.readText()
|
||||
moveScripts[resolvedIdentifier.path] = js
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowdownService.service.sendRegistryData(moveScripts, "move")
|
||||
|
||||
val movesJson = ShowdownService.service.getRegistryData("move")
|
||||
for (i in 0 until movesJson.size()) {
|
||||
val jsMove = movesJson[i].asJsonObject
|
||||
val id = jsMove.get("id").asString
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStatDefinition
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -76,6 +77,7 @@ import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
import net.minecraft.world.phys.AABB
|
||||
|
||||
object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
@@ -123,6 +125,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
@@ -157,7 +160,8 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
}
|
||||
this.species.forEach(Species::resolveEvolutionMoves)
|
||||
Cobblemon.showdownThread.queue {
|
||||
it.registerSpecies()
|
||||
it.resetRegistryData("species")
|
||||
it.sendRegistryData(allShowdownSpecies(), "species")
|
||||
it.indicateSpeciesInitialized()
|
||||
// Reload this with the mod
|
||||
CobblemonHeldItemManager.load()
|
||||
@@ -312,4 +316,18 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
return "${species.resourceIdentifier.namespace}:${species.name}"
|
||||
}
|
||||
|
||||
internal fun allShowdownSpecies(): Map<String, String> {
|
||||
val result = mutableMapOf<String, String>()
|
||||
this.species.forEach {species ->
|
||||
val baseSpecies = ShowdownSpecies(species, null)
|
||||
result[baseSpecies.name] = this.gson.toJson(baseSpecies)
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
val formSpecies = ShowdownSpecies(species, form)
|
||||
result[formSpecies.name] = this.gson.toJson(formSpecies)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -8,8 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.aspect
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
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
|
||||
import net.minecraft.core.particles.BlockParticleOption
|
||||
import net.minecraft.core.particles.ParticleOptions
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -18,7 +21,12 @@ import net.minecraft.resources.ResourceLocation
|
||||
* Used by [PokemonClientDelegate.spawnAspectParticle] to spawn associated particles for aspects
|
||||
*/
|
||||
val aspectParticleMap: Map<String, ParticleData> = mapOf(
|
||||
SaccharineLogSlatheredInfluence.HONEY_DRENCHED_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_HONEY, 0.075, 1)
|
||||
SaccharineLogSlatheredInfluence.HONEY_DRENCHED_ASPECT to ParticleData.MinecraftParticle(ParticleTypes.FALLING_HONEY, 0.075, 1),
|
||||
LurePokeSnackDetector.POKE_SNACK_CRUMBED_ASPECT to ParticleData.MinecraftParticle(
|
||||
BlockParticleOption(
|
||||
ParticleTypes.BLOCK,
|
||||
CobblemonBlocks.POKE_SNACK.defaultBlockState()
|
||||
), 0.05, 3),
|
||||
)
|
||||
|
||||
sealed class ParticleData {
|
||||
|
||||
@@ -18,11 +18,4 @@ import net.minecraft.world.entity.PlayerRideableJumping
|
||||
*
|
||||
* @since 1.7.0
|
||||
*/
|
||||
interface Rideable : PlayerRideableJumping {
|
||||
|
||||
/**
|
||||
* Denotes the manager responsible for handling any instance of riding taking place on an entity
|
||||
*/
|
||||
val riding: RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>?
|
||||
|
||||
}
|
||||
interface Rideable : PlayerRideableJumping
|
||||
@@ -18,58 +18,41 @@ import com.cobblemon.mod.common.util.adapters.RidingBehaviourSettingsAdapter
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
|
||||
class RidingProperties(
|
||||
val stats: MutableMap<RidingStat, RidingStatDefinition> = mutableMapOf(),
|
||||
val seats: List<Seat> = listOf(),
|
||||
val conditions: List<Expression> = listOf(),
|
||||
val behaviour: RidingBehaviourSettings? = null
|
||||
val behaviours: Map<RidingStyle, RidingBehaviourSettings>? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun decode(buffer: RegistryFriendlyByteBuf): RidingProperties {
|
||||
val stats: MutableMap<RidingStat, RidingStatDefinition> = buffer.readMap(
|
||||
{ buffer.readEnum(RidingStat::class.java) },
|
||||
{ RidingStatDefinition.decode(buffer) }
|
||||
).toMutableMap()
|
||||
val seats: List<Seat> = buffer.readList { _ -> Seat.decode(buffer) }
|
||||
val conditions = buffer.readList { buffer.readString().asExpression() }
|
||||
val behaviour = buffer.readNullable { _ ->
|
||||
val key = buffer.readResourceLocation()
|
||||
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance() ?: error("Unknown controller key: $key")
|
||||
settings.decode(buffer)
|
||||
return@readNullable settings
|
||||
val behaviours = buffer.readNullable { _ ->
|
||||
buffer.readMap(
|
||||
{ buffer.readEnumConstant(RidingStyle::class.java) },
|
||||
{
|
||||
val key = buffer.readResourceLocation()
|
||||
val settings = RidingBehaviourSettingsAdapter.types[key]?.getConstructor()?.newInstance()
|
||||
?: error("Unknown controller key: $key")
|
||||
settings.decode(buffer)
|
||||
settings
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return RidingProperties(stats = stats, seats = seats, conditions = conditions, behaviour = behaviour)
|
||||
return RidingProperties(seats = seats, conditions = conditions, behaviours = behaviours)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun updateStatRange(stat: RidingStat, style: RidingStyle, min: Int, max: Int) {
|
||||
if (!Cobblemon.config.enableDebugKeys) return
|
||||
if (stats[stat] == null) {
|
||||
stats[stat] = RidingStatDefinition()
|
||||
}
|
||||
stats[stat]!!.ranges[style] = min..max
|
||||
}
|
||||
|
||||
fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeMap(
|
||||
stats,
|
||||
{ _, stat -> buffer.writeEnum(stat) },
|
||||
{ _, stat -> stat.encode(buffer) }
|
||||
)
|
||||
buffer.writeCollection(seats) { _, seat -> seat.encode(buffer) }
|
||||
buffer.writeCollection(conditions) { _, condition -> buffer.writeString(condition.getString()) }
|
||||
buffer.writeNullable(behaviour) { _, behaviour ->
|
||||
behaviour.encode(buffer)
|
||||
buffer.writeNullable(behaviours) { _, v ->
|
||||
buffer.writeMap(
|
||||
v,
|
||||
{ _, style -> buffer.writeEnumConstant(style) },
|
||||
{ _, behaviour -> behaviour.encode(buffer) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun calculate(stat: RidingStat, style: RidingStyle, boosts: Int): Float {
|
||||
val definitions = stats[stat] ?: return 0F
|
||||
return definitions.calculate(style, boosts)
|
||||
}
|
||||
|
||||
fun hasStat(stat: RidingStat, style: RidingStyle): Boolean {
|
||||
return stats[stat]?.ranges?.containsKey(style) == true
|
||||
}
|
||||
}
|
||||
+15
@@ -101,4 +101,19 @@ interface RidingBehaviour<Settings : RidingBehaviourSettings, State : RidingBeha
|
||||
* Calculate the damage a horizontal collision will do to a ridden Pokémon. This is only relevant to fast flying behaviours, fall damage is separate!
|
||||
*/
|
||||
fun damageOnCollision(settings: Settings, state: State, vehicle: PokemonEntity, impactVec: Vec3): Boolean = false
|
||||
|
||||
|
||||
/**
|
||||
* Internal helpers to help behaviour specific calculations
|
||||
*/
|
||||
companion object {
|
||||
/**
|
||||
* Scales a given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
internal fun scaleToRange(x: Double, min: Double, max: Double): Double {
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
@@ -23,4 +25,13 @@ import net.minecraft.world.phys.Vec3
|
||||
*/
|
||||
interface RidingBehaviourSettings: Encodable, Decodable {
|
||||
val key: ResourceLocation
|
||||
val stats: MutableMap<RidingStat, IntRange>
|
||||
|
||||
fun calculate(stat: RidingStat, boosts: Int): Float {
|
||||
val range = stats[stat] ?: return 0F
|
||||
val boostQuotient = boosts / 255F
|
||||
return range.first + (range.last - range.first) * boostQuotient
|
||||
}
|
||||
|
||||
fun hasStat(stat: RidingStat) = stats.containsKey(stat)
|
||||
}
|
||||
|
||||
+6
-1
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.DolphinBehaviour
|
||||
@@ -22,21 +23,25 @@ object RidingBehaviours {
|
||||
|
||||
init {
|
||||
register(BirdBehaviour.KEY, BirdBehaviour())
|
||||
register(BirdNoRollBehaviour.KEY, BirdNoRollBehaviour())
|
||||
register(DolphinBehaviour.KEY, DolphinBehaviour())
|
||||
register(HorseBehaviour.KEY, HorseBehaviour())
|
||||
register(BoatBehaviour.KEY, BoatBehaviour())
|
||||
register(GliderBehaviour.KEY, GliderBehaviour())
|
||||
register(HelicopterBehaviour.KEY, HelicopterBehaviour())
|
||||
register(JetBehaviour.KEY, JetBehaviour())
|
||||
register(JetNoRollBehaviour.KEY, JetNoRollBehaviour())
|
||||
register(BurstBehaviour.KEY, BurstBehaviour())
|
||||
register(VehicleBehaviour.KEY, VehicleBehaviour())
|
||||
register(MinekartBehaviour.KEY, MinekartBehaviour())
|
||||
register(HoverBehaviour.KEY, HoverBehaviour())
|
||||
register(RocketBehaviour.KEY, RocketBehaviour())
|
||||
register(CompositeBehaviour.KEY, CompositeBehaviour())
|
||||
}
|
||||
|
||||
fun register(key: ResourceLocation, behaviour: RidingBehaviour<out RidingBehaviourSettings, out RidingBehaviourState>) {
|
||||
if (behaviours.contains(key)) error("Behaviour already registered to key $key")
|
||||
behaviours[key] = RidingController(behaviour) as RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>
|
||||
behaviours[key] = behaviour as RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>
|
||||
}
|
||||
|
||||
fun get(key: ResourceLocation): RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState> {
|
||||
|
||||
+89
-176
@@ -8,206 +8,119 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour
|
||||
|
||||
import com.cobblemon.mod.common.DoubleJump
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.tags.FluidTags
|
||||
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 kotlin.collections.get
|
||||
|
||||
/**
|
||||
* Small wrapper around a RidingBehaviour to provide sane defaults in the event that the behaviour is not active.
|
||||
*
|
||||
* @author landonjw
|
||||
*/
|
||||
class RidingController<Settings : RidingBehaviourSettings, State : RidingBehaviourState>(val behaviour: RidingBehaviour<Settings, State>) : RidingBehaviour<Settings, State> {
|
||||
class RidingController(
|
||||
val entity: PokemonEntity,
|
||||
val behaviours: Map<RidingStyle, RidingBehaviourSettings>) {
|
||||
|
||||
override val key = behaviour.key
|
||||
var lastTransitionAge: Int = 0
|
||||
private set
|
||||
|
||||
override fun getRidingStyle(settings: Settings, state: State): RidingStyle {
|
||||
return behaviour.getRidingStyle(settings, state)
|
||||
var context: ActiveRidingContext? = null
|
||||
private set
|
||||
|
||||
fun changeBehaviour(behaviour: ResourceLocation) {
|
||||
if (entity.form.riding.behaviours == null) return
|
||||
val style = entity.form.riding.behaviours!!.filter { it.value.key == behaviour }.keys.firstOrNull() ?: return
|
||||
val behaviourSettings = entity.pokemon.riding.behaviours?.get(style) ?: return
|
||||
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
|
||||
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
|
||||
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, style)
|
||||
lastTransitionAge = entity.ticksLived
|
||||
}
|
||||
|
||||
override fun isActive(settings: Settings, state: State, vehicle: PokemonEntity) =
|
||||
behaviour.isActive(settings, state, vehicle)
|
||||
fun tick() {
|
||||
if (entity.ticksLived - lastTransitionAge < 10) return
|
||||
val newTransition = checkForNewTransition(entity.controllingPassenger)
|
||||
val behaviourSettings = entity.pokemon.riding.behaviours?.get(newTransition)
|
||||
if (newTransition != context?.style) {
|
||||
if (newTransition != null && behaviourSettings != null) {
|
||||
val newState = RidingBehaviours.get(behaviourSettings.key).createDefaultState(behaviourSettings)
|
||||
context?.let { newState.stamina.set(it.state.stamina.get(), forced = true) }
|
||||
context = ActiveRidingContext(behaviourSettings.key, behaviourSettings, newState, newTransition)
|
||||
lastTransitionAge = entity.ticksLived
|
||||
} else {
|
||||
context = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun tick(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player, input: Vec3) =
|
||||
behaviour.tick(settings, state, vehicle, driver, input)
|
||||
|
||||
override fun pose(settings: Settings, state: State, vehicle: PokemonEntity) =
|
||||
behaviour.pose(settings, state, vehicle)
|
||||
|
||||
override fun speed(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 0.0F
|
||||
return behaviour.speed(settings, state, vehicle, driver)
|
||||
if (context == null && !this.entity.level().isClientSide) {
|
||||
entity.ejectPassengers()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
if (!isActive(settings, state, vehicle)) return
|
||||
return behaviour.updatePassengerRotation(settings, state, vehicle, driver)
|
||||
fun getBehaviour(): RidingBehaviour<RidingBehaviourSettings, RidingBehaviourState>? {
|
||||
if (context == null) return null
|
||||
return RidingBehaviours.get(context!!.behaviour)
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity) {
|
||||
if (!isActive(settings, state, vehicle)) return
|
||||
return behaviour.clampPassengerRotation(settings, state, vehicle, driver)
|
||||
private fun checkForNewTransition(driver: LivingEntity?): RidingStyle? {
|
||||
when (context?.style) {
|
||||
RidingStyle.AIR -> {
|
||||
if (canTransitionToLand()) return RidingStyle.LAND
|
||||
if (canTransitionToLiquid()) return RidingStyle.LIQUID
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
RidingStyle.LIQUID -> {
|
||||
if (canTransitionToAir(driver)) return RidingStyle.AIR
|
||||
if (canTransitionToLand()) return RidingStyle.LAND
|
||||
return RidingStyle.LIQUID
|
||||
}
|
||||
|
||||
RidingStyle.LAND -> {
|
||||
if (canTransitionToAir(driver)) return RidingStyle.AIR
|
||||
if (canTransitionToLiquid()) return RidingStyle.LIQUID
|
||||
return RidingStyle.LAND
|
||||
}
|
||||
|
||||
null -> {
|
||||
if (canTransitionToAir(driver)) return RidingStyle.AIR
|
||||
if (canTransitionToLiquid()) return RidingStyle.LIQUID
|
||||
if (canTransitionToLand()) return RidingStyle.LAND
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotation(settings: Settings, state: State, vehicle: PokemonEntity, driver: LivingEntity): Vec2 {
|
||||
if (!isActive(settings, state, vehicle)) return driver.rotationVector
|
||||
return behaviour.rotation(settings, state, vehicle, driver)
|
||||
private fun canTransitionToLand(): Boolean {
|
||||
if (entity.isInLiquid || entity.isUnderWater) return false
|
||||
return entity.onGround()
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.velocity(settings, state, vehicle, driver, input)
|
||||
private fun canTransitionToLiquid(): Boolean {
|
||||
return entity.isInLiquid || entity.isUnderWater
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.angRollVel(settings, state, vehicle, driver, deltaTime)
|
||||
private fun canTransitionToAir(driver: LivingEntity?): Boolean {
|
||||
if (driver != null) {
|
||||
if (driver !is DoubleJump) return false
|
||||
// check if style is water and player is submerged
|
||||
val isEyeInLiquid = entity.isEyeInFluid(FluidTags.WATER) || entity.isEyeInFluid(FluidTags.LAVA)
|
||||
if (context?.style == RidingStyle.LIQUID && isEyeInLiquid) return false
|
||||
return (driver as DoubleJump).isDoubleJumping
|
||||
}
|
||||
else {
|
||||
return !entity.onGround() && !(entity.isInLiquid || entity.isUnderWater)
|
||||
}
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.rotationOnMouseXY(
|
||||
settings,
|
||||
state,
|
||||
vehicle,
|
||||
driver,
|
||||
mouseY,
|
||||
mouseX,
|
||||
mouseYSmoother,
|
||||
mouseXSmoother,
|
||||
sensitivity,
|
||||
deltaTime
|
||||
)
|
||||
}
|
||||
|
||||
override fun canJump(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.canJump(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRotateRiderHead(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRotatePokemonHead(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.dismountOnShift(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.turnOffOnGround(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return false
|
||||
return behaviour.shouldRoll(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun inertia(settings: Settings, state: State, vehicle: PokemonEntity): Double {
|
||||
if (!isActive(settings, state, vehicle)) return 0.5
|
||||
return behaviour.inertia(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): ResourceLocation {
|
||||
if (!isActive(settings, state, vehicle)) return cobblemonResource("no_pose")
|
||||
return behaviour.useRidingAltPose(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: Settings, state: State, vehicle: PokemonEntity): Boolean {
|
||||
if (!isActive(settings, state, vehicle)) return true
|
||||
return behaviour.useAngVelSmoothing(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 1.0F
|
||||
return behaviour.rideFovMultiplier(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun gravity(settings: Settings, state: State, vehicle: PokemonEntity, regularGravity: Double): Double {
|
||||
if (!isActive(settings, state, vehicle)) return regularGravity
|
||||
return behaviour.gravity(settings, state, vehicle, regularGravity)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
if (!isActive(settings, state, vehicle)) return Vec3.ZERO
|
||||
return behaviour.jumpForce(settings, state, vehicle, driver, jumpStrength)
|
||||
}
|
||||
|
||||
override fun setRideBar(settings: Settings, state: State, vehicle: PokemonEntity, driver: Player): Float {
|
||||
if (!isActive(settings, state, vehicle)) return 0.0F
|
||||
return behaviour.setRideBar(settings, state, vehicle, driver)
|
||||
}
|
||||
|
||||
override fun maxUpStep(settings: Settings, state: State, vehicle: PokemonEntity): Float? {
|
||||
if (!isActive(settings, state, vehicle)) return null
|
||||
return behaviour.maxUpStep(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList {
|
||||
if (!isActive(settings, state, vehicle)) return RideSoundSettingsList()
|
||||
return behaviour.getRideSounds(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: Settings): State {
|
||||
return behaviour.createDefaultState(settings)
|
||||
}
|
||||
|
||||
override fun damageOnCollision(
|
||||
settings: Settings,
|
||||
state: State,
|
||||
vehicle: PokemonEntity,
|
||||
impactVec: Vec3
|
||||
): Boolean {
|
||||
return behaviour.damageOnCollision(settings, state, vehicle, impactVec)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class ActiveRidingContext(
|
||||
val behaviour: ResourceLocation,
|
||||
val settings: RidingBehaviourSettings,
|
||||
val state: RidingBehaviourState,
|
||||
val style: RidingStyle
|
||||
)
|
||||
+9
-28
@@ -17,6 +17,7 @@ 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.*
|
||||
@@ -105,14 +106,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
upForce -= 0.3
|
||||
}
|
||||
|
||||
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
|
||||
//Only limit altitude if altitude is not infinite
|
||||
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
|
||||
//Provide a hard limit on altitude
|
||||
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
|
||||
}
|
||||
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
|
||||
return velocity
|
||||
@@ -127,12 +120,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
|
||||
var glideSpeedChange = 0.0
|
||||
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
|
||||
@@ -255,7 +243,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
|
||||
val rotMin = 15.0
|
||||
val rotLimit = max(handling * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
val rotLimit = max(handling * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
@@ -311,7 +299,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
val rotMin = 15.0
|
||||
var rollForce = xInput
|
||||
val rotLimit = max(90 * sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
val rotLimit = max(90 * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
//Limit roll by non linearly decreasing inputs towards
|
||||
// a rotation limit based on the current distance from
|
||||
@@ -327,7 +315,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
}
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * ( 1.0 - sqrt(normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed)))
|
||||
val yawForce = xInput * ( 1.0 - sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)))
|
||||
|
||||
//Yaw locally a bit when up or down so that its more intuitive to make it out of a dive or a straight vertical
|
||||
//climb
|
||||
@@ -355,16 +343,6 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
return Vec3(0.0, 0.0, rollForce)
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the given value between a min and a max.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents x is at or below min
|
||||
* and 1.0 represents x is at or above it.
|
||||
*/
|
||||
private fun normalizeVal(x: Double, min: Double, max: Double): Double {
|
||||
require(max > min) { "max must be greater than min" }
|
||||
return ((x - min) / (max - min)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BirdSettings,
|
||||
state: BirdState,
|
||||
@@ -412,7 +390,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = normalizeVal(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
@@ -467,7 +445,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
state: BirdState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
@@ -483,6 +461,7 @@ class BirdBehaviour : RidingBehaviour<BirdSettings, BirdState> {
|
||||
|
||||
class BirdSettings : RidingBehaviourSettings {
|
||||
override val key = BirdBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
@@ -504,6 +483,7 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
@@ -516,6 +496,7 @@ class BirdSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
|
||||
class BirdNoRollBehaviour : RidingBehaviour<BirdNoRollSettings, BirdNoRollState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/birdnoroll")
|
||||
}
|
||||
|
||||
override val key: ResourceLocation = KEY
|
||||
|
||||
override fun getRidingStyle(settings: BirdNoRollSettings, state: BirdNoRollState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<BirdNoRollSettings, BirdNoRollState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.2 })
|
||||
|
||||
override fun isActive(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun pose(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var yVel = 0.0
|
||||
var leftForce = 0.0
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
//Perform ride velocity update
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
upForce += -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
forwardForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
|
||||
upForce += cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
forwardForce += sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().y
|
||||
}
|
||||
|
||||
//Bring the ride out of the sky when stamina is depleted.
|
||||
if (state.stamina.get() <= 0.0) {
|
||||
upForce -= 0.3
|
||||
}
|
||||
|
||||
val velocity = Vec3(state.rideVelocity.get().x , upForce, forwardForce)
|
||||
return velocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
fun calculateRideSpaceVel(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity, driver: Player) {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
|
||||
var glideSpeedChange = 0.0
|
||||
|
||||
val currSpeed = state.rideVelocity.get().length()
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z < (-topSpeed / 3.0) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
if (controller != null) {
|
||||
//Base glide speed change on current pitch of the ride.
|
||||
glideSpeedChange = sin(Math.toRadians(controller.pitch.toDouble()))
|
||||
glideSpeedChange = glideSpeedChange * 0.25
|
||||
|
||||
//TODO: Possibly create a deadzone around parallel where glide doesn't affect speed?
|
||||
if (glideSpeedChange <= 0.0) {
|
||||
//Ensures that a propelling force is still able to be applied when
|
||||
//climbing in height
|
||||
if (driver.zza <= 0) {
|
||||
//speed decrease should be 2x speed increase?
|
||||
//state.currSpeed = max(state.currSpeed + (0.0166) * glideSpeedChange, 0.0 )
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
lerp( newVelocity.z, 0.0,glideSpeedChange * -0.0166 * 2 )
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// only add to the speed if it hasn't exceeded the current
|
||||
//glide angles maximum amount of speed that it can give.
|
||||
//state.currSpeed = min(state.currSpeed + ((0.0166 * 2) * glideSpeedChange), maxGlideSpeed)
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
min(newVelocity.z + ((0.0166 * 2) * glideSpeedChange), glideTopSpeed)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//Vertical movement based on driver input.
|
||||
val vertTopSpeed = topSpeed / 2.0
|
||||
val vertInput = when {
|
||||
driver.jumping && state.rideVelocity.get().length() < 0.3 -> 1.0
|
||||
driver.isShiftKeyDown && state.rideVelocity.get().length() < 0.3 -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
|
||||
if (vertInput != 0.0 && state.stamina.get() > 0.0) {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * vertInput)).coerceIn(-vertTopSpeed, vertTopSpeed),
|
||||
newVelocity.z)
|
||||
activeInput = true
|
||||
}
|
||||
else {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
lerp(newVelocity.y, 0.0, vertTopSpeed / 20.0),
|
||||
newVelocity.z)
|
||||
}
|
||||
|
||||
//Check if the ride should be gliding
|
||||
if (driver.isLocalPlayer) {
|
||||
if (activeInput && state.stamina.get() > 0.0) {
|
||||
state.gliding.set(false)
|
||||
} else {
|
||||
state.gliding.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
//air resistance
|
||||
if( abs(newVelocity.z) > 0) {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.0015 * newVelocity.z.sign, newVelocity.z))
|
||||
}
|
||||
state.rideVelocity.set(newVelocity)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
//limit rolling based on handling and current speed.
|
||||
//modulated by speed so that when flapping idle in air you are ont wobbling around to look around
|
||||
//TODO: Tie handling into yaw rate
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//This rotation limit will be used to know if the ride has exceeded it and its needs correcting.
|
||||
val rotMin = 15.0
|
||||
val rotLimit = max(handling * sqrt(RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed)), rotMin)
|
||||
|
||||
var yawDeltaDeg = deltaTime * handling * sin(Math.toRadians(controller.roll.toDouble())) //sin(Math.toRadians(controller.roll.toDouble())) * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
val trueYawDelt = yawDeltaDeg * abs(cos(Math.toRadians(controller.pitch.toDouble())))
|
||||
//val pitchedYawDelt = yawDeltaDeg - trueYawDelt
|
||||
|
||||
controller.applyGlobalYaw(trueYawDelt.toFloat())
|
||||
|
||||
//Calculate correcting roll force.
|
||||
val correctionRate = 5.0
|
||||
if(abs(controller.roll) > rotLimit && state.rideVelocity.get().length() < 0.2) {
|
||||
state.currRollCorrectionForce.set(
|
||||
state.currRollCorrectionForce.get() + ((abs(controller.roll) - rotLimit) / 180 - rotLimit) * controller.roll.sign * correctionRate * deltaTime * 0.01
|
||||
)
|
||||
} else {
|
||||
state.currRollCorrectionForce.set(
|
||||
//reduce rollCorrection gradually
|
||||
lerp(state.currRollCorrectionForce.get(), 0.0, deltaTime * 0.98)
|
||||
)
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, 0.0, state.currRollCorrectionForce.get())
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
//TODO: hook up handling in parity with normal bird
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4.0
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
|
||||
|
||||
//Give the ability to yaw with x mouse input when at low speeds.
|
||||
val yawForce = xInput * ( 1.0 - min(sqrt(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())
|
||||
|
||||
var pitchRot = yInput
|
||||
|
||||
// Pitch up globally
|
||||
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
|
||||
pitchRot = 0.0
|
||||
mouseYSmoother.reset()
|
||||
} else {
|
||||
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val glideTopSpeed = vehicle.runtime.resolveDouble(settings.glidespeedExpr)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedGlideSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, glideTopSpeed)
|
||||
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedGlideSpeed.pow(2).toFloat() * 0.2f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
if (state.gliding.get()) {
|
||||
return cobblemonResource("gliding")
|
||||
}
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: BirdNoRollSettings, state: BirdNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: BirdNoRollSettings,
|
||||
state: BirdNoRollState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: BirdNoRollSettings) = BirdNoRollState()
|
||||
}
|
||||
|
||||
class BirdNoRollSettings : RidingBehaviourSettings {
|
||||
override val key = BirdNoRollBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
//max y level for the ride
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
|
||||
|
||||
var glidespeedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 2.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(glidespeedExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
glidespeedExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class BirdNoRollState : RidingBehaviourState() {
|
||||
var gliding = ridingState(false, Side.CLIENT)
|
||||
var lastGlide = ridingState(-100L, Side.CLIENT)
|
||||
var currRollCorrectionForce = ridingState(0.0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeFloat(stamina.get())
|
||||
buffer.writeBoolean(gliding.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
gliding.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
gliding.set(false, forced = true)
|
||||
lastGlide.set(-100L, forced = true)
|
||||
currRollCorrectionForce.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BirdNoRollState(rideVelocity=${rideVelocity.get()}, stamina=${stamina.get()}, gliding=${gliding.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = BirdNoRollState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.gliding.set(this.gliding.get(), forced = true)
|
||||
it.lastGlide.set(this.lastGlide.get(), forced = true)
|
||||
it.currRollCorrectionForce.set(this.currRollCorrectionForce.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is BirdNoRollState) return false
|
||||
if (previous.gliding.get() != gliding.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -75,8 +75,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
val xVector = if (vehicle.runtime.resolveBoolean(settings.canStrafe)) driver.xxa.toDouble() else 0.0
|
||||
val yVector = -vehicle.runtime.resolveDouble(settings.glideSpeed)
|
||||
val zVector = driver.zza.toDouble()
|
||||
val statSpeed = vehicle.rideProp.calculate(RidingStat.SPEED, RidingStyle.AIR, 0 )
|
||||
|
||||
val speedStat = settings.calculate(RidingStat.SPEED, 0)
|
||||
return Vec3(xVector, yVector, zVector)
|
||||
}
|
||||
|
||||
@@ -197,6 +196,7 @@ class GliderBehaviour : RidingBehaviour<GliderSettings, RidingBehaviourState> {
|
||||
|
||||
class GliderSettings : RidingBehaviourSettings {
|
||||
override val key = GliderBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var glideSpeed: Expression = "0.1".asExpression()
|
||||
private set
|
||||
@@ -211,6 +211,7 @@ class GliderSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(glideSpeed)
|
||||
buffer.writeExpression(speed)
|
||||
@@ -218,6 +219,7 @@ class GliderSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
glideSpeed = buffer.readExpression()
|
||||
speed = buffer.readExpression()
|
||||
|
||||
+4
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
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.*
|
||||
@@ -287,6 +288,7 @@ class HelicopterBehaviour : RidingBehaviour<HelicopterSettings, RidingBehaviourS
|
||||
|
||||
class HelicopterSettings : RidingBehaviourSettings {
|
||||
override val key = HelicopterBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "1.0".asExpression()
|
||||
private set
|
||||
@@ -304,6 +306,7 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(horizontalAcceleration)
|
||||
@@ -312,6 +315,7 @@ class HelicopterSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
horizontalAcceleration = buffer.readExpression()
|
||||
|
||||
+6
-11
@@ -14,6 +14,7 @@ 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.*
|
||||
@@ -103,7 +104,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
val rotAmount = 10.0f
|
||||
|
||||
//Take the inverse so that you turn more at higher speeds
|
||||
val normSpeed = 1.0f - 0.5f*normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
val normSpeed = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
|
||||
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
|
||||
@@ -163,7 +164,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*normalizeVal(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*RidingBehaviour.scaleToRange(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
val turnRate = (handling.toFloat() / 20.0f)
|
||||
|
||||
@@ -307,15 +308,6 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: HoverSettings,
|
||||
state: HoverState,
|
||||
@@ -472,6 +464,7 @@ class HoverBehaviour : RidingBehaviour<HoverSettings, HoverState> {
|
||||
|
||||
class HoverSettings : RidingBehaviourSettings {
|
||||
override val key = HoverBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var rideSound: ResourceLocation = "ride.loop.saucer".asIdentifierDefaultingNamespace()
|
||||
var volumeExpr: Expression = "math.pow(math.min(q.ride_velocity() / 0.8, 1.0),2)".asExpression()
|
||||
@@ -503,6 +496,7 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
@@ -512,6 +506,7 @@ class HoverSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
|
||||
+10
-37
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -79,7 +80,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
// //Calculate stamina loss due to speed
|
||||
// //At max speed it will tick down 0.1 a second so the stamina will last ten seconds
|
||||
// //There has got to be a better way to express this equation. It interpolates between 0.5 and 1.0
|
||||
// var staminaRate = (normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed))
|
||||
// var staminaRate = (RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed))
|
||||
//
|
||||
// //interpolate between 0.25 and 1.0 so that you always have at least a min of 0.25 stam loss
|
||||
// staminaRate = 0.25 + (0.75 * staminaRate.pow(3))
|
||||
@@ -94,16 +95,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
//TODO: Move these functions to a riding util class.
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeSpeed(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
@@ -137,14 +128,6 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
//If stamina has run out then initiate forced glide down.
|
||||
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
|
||||
|
||||
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
|
||||
//Only limit altitude if altitude is not infinite
|
||||
if (!vehicle.runtime.resolveBoolean(settings.infiniteAltitude)) {
|
||||
//Provide a hard limit on altitude
|
||||
upForce = if (vehicle.y >= altitudeLimit && upForce > 0) 0.0 else upForce
|
||||
}
|
||||
|
||||
val velocity = Vec3(0.0, upForce, forwardForce)
|
||||
|
||||
return velocity
|
||||
@@ -161,19 +144,13 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val altitudeLimit = vehicle.runtime.resolveDouble(settings.jumpExpr)
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
val speed = state.rideVelocity.get().length()
|
||||
|
||||
//Give no altitude limit if at max jump stat.
|
||||
val pushingHeightLimit = if (vehicle.runtime.resolveBoolean(settings.infiniteStamina)) false
|
||||
else (vehicle.y >= altitudeLimit && vehicle.xRot <= 0)
|
||||
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f && !pushingHeightLimit) {
|
||||
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max(-(normalizeSpeed(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
val accelMod = max(-(RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
@@ -181,7 +158,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
|
||||
)
|
||||
)
|
||||
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f || pushingHeightLimit)) {
|
||||
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
@@ -191,7 +168,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
)
|
||||
} else if (driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max((normalizeSpeed(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
val deccelMod = max((RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(
|
||||
@@ -337,7 +314,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedSpeed = normalizeSpeed(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
val normalizedSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
|
||||
//TODO: Determine if this should be based on max possible speed instead of top speed.
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
@@ -412,6 +389,7 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
|
||||
class JetSettings : RidingBehaviourSettings {
|
||||
override val key = JetBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
@@ -426,9 +404,6 @@ class JetSettings : RidingBehaviourSettings {
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var infiniteAltitude: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
|
||||
private set
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
@@ -445,12 +420,12 @@ class JetSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
buffer.writeExpression(handlingYawExpr)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(infiniteAltitude)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
@@ -459,12 +434,12 @@ class JetSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
handlingYawExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
infiniteAltitude = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
@@ -500,8 +475,6 @@ class JetState : RidingBehaviourState() {
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
// it.currMouseXForce.set(currMouseXForce.get(), forced = true)
|
||||
// it.currMouseYForce.set(currMouseYForce.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
|
||||
+471
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.*
|
||||
|
||||
class JetNoRollBehaviour : RidingBehaviour<JetNoRollSettings, JetNoRollState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/jetnoroll")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: JetNoRollSettings, state: JetNoRollState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<JetNoRollSettings, JetNoRollState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ -> state.rideVelocity.get().z > 0.1 })
|
||||
|
||||
override fun isActive(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity, driver: Player): Float {
|
||||
//retrieve stats
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val staminaStat = vehicle.runtime.resolveDouble(settings.staminaExpr)
|
||||
|
||||
//retrieve minSpeed
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//TODO: Reintroduce stamina drain once stats start to be polished and tweaked
|
||||
state.stamina.set(1.0f)
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
return Vec2(driver.xRot * 0.5f, driver.yRot)
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
var upForce = 0.0
|
||||
var forwardForce = 0.0
|
||||
|
||||
val controller = (driver as? OrientationControllable)?.orientationController
|
||||
|
||||
//Calculate ride space velocity
|
||||
calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
|
||||
//Translate ride space velocity to world space velocity.
|
||||
if (controller != null) {
|
||||
upForce = -1.0 * sin(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
forwardForce = cos(Math.toRadians(controller.pitch.toDouble())) * state.rideVelocity.get().z
|
||||
}
|
||||
|
||||
//If stamina has run out then initiate forced glide down.
|
||||
upForce = if (state.stamina.get() > 0.0) upForce else -0.7
|
||||
|
||||
val velocity = Vec3(0.0, upForce, forwardForce)
|
||||
|
||||
return velocity
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the change in the ride space vector due to player input and ride state
|
||||
*/
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
val speed = state.rideVelocity.get().length()
|
||||
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && speed < topSpeed && state.stamina.get() > 0.0f) {
|
||||
//modify acceleration to be slower when at closer speeds to top speed
|
||||
val accelMod = max(-(RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed)) + 1, 0.0)
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
min(state.rideVelocity.get().z + (accel * accelMod), topSpeed)
|
||||
)
|
||||
)
|
||||
} else if (driver.zza >= 0.0 && (state.stamina.get() == 0.0f)) {
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
max(state.rideVelocity.get().z - ((accel) / 4), minSpeed)
|
||||
)
|
||||
)
|
||||
} else if (driver.zza < 0.0 && speed > minSpeed) {
|
||||
//modify deccel to be slower when at closer speeds to minimum speed
|
||||
val deccelMod = max((RidingBehaviour.scaleToRange(speed, minSpeed, topSpeed) - 1).pow(2) * 4, 0.1)
|
||||
|
||||
//Decelerate currently always a constant half of max acceleration.
|
||||
state.rideVelocity.set(
|
||||
Vec3(
|
||||
state.rideVelocity.get().x,
|
||||
state.rideVelocity.get().y,
|
||||
max(state.rideVelocity.get().z - ((accel * deccelMod) / 2), minSpeed)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
// //Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
// val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
//
|
||||
// //Get handling in degrees per second
|
||||
// val yawRotRate = vehicle.runtime.resolveDouble(settings.handlingYawExpr)
|
||||
//
|
||||
// //Base the change off of deltatime.
|
||||
// var handlingYaw = yawRotRate * (cappedDeltaTime)
|
||||
//
|
||||
// //apply stamina debuff if applicable
|
||||
// handlingYaw *= if (state.stamina.get() > 0.0) 1.0 else 0.5
|
||||
//
|
||||
// //A+D to yaw
|
||||
// val yawForce = driver.xxa * handlingYaw * -1
|
||||
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
//TODO: figure out a cleaner solution to this issue of large jumps when skipping frames or lagging
|
||||
//Cap at a rate of 5fps so frame skips dont lead to huge jumps
|
||||
val cappedDeltaTime = min(deltaTime, 0.2)
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
// Accumulate the mouse input
|
||||
state.currMouseXForce.set((state.currMouseXForce.get() + (0.0015 * mouseX * invertRoll)).coerceIn(-1.0, 1.0))
|
||||
state.currMouseYForce.set((state.currMouseYForce.get() + (0.0015 * mouseY * invertPitch)).coerceIn(-1.0, 1.0))
|
||||
//Get handling in degrees per second
|
||||
var handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
//convert it to delta time
|
||||
handling *= (cappedDeltaTime)
|
||||
|
||||
//apply stamina debuff if applicable
|
||||
handling *= if (state.stamina.get() > 0.0) 1.0 else 0.5
|
||||
|
||||
var pitchRot = handling * state.currMouseYForce.get()
|
||||
|
||||
// Yaw
|
||||
val yawRot = handling * 0.5 * state.currMouseXForce.get()
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
controller.applyGlobalYaw(yawRot.toFloat())
|
||||
|
||||
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
|
||||
pitchRot = 0.0
|
||||
state.currMouseYForce.set(0.0)
|
||||
mouseYSmoother.reset()
|
||||
} else {
|
||||
controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
|
||||
}
|
||||
|
||||
val mouseRotation = Vec3(0.0, pitchRot, 0.0)
|
||||
|
||||
// Have accumulated input begin decay when no input detected
|
||||
if (abs(mouseX) == 0.0) {
|
||||
// Have decay on roll be much stronger.
|
||||
state.currMouseXForce.set(lerp(state.currMouseXForce.get(), 0.0, 0.02))
|
||||
}
|
||||
if (mouseY == 0.0) {
|
||||
state.currMouseYForce.set(lerp(state.currMouseYForce.get(), 0.0, 0.005))
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val minSpeed = vehicle.runtime.resolveDouble(settings.minSpeed)
|
||||
|
||||
//Must I ensure that topspeed is greater than minimum?
|
||||
val normalizedSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), minSpeed, topSpeed)
|
||||
|
||||
//TODO: Determine if this should be based on max possible speed instead of top speed.
|
||||
//Only ever want the fov change to be a max of 0.2 and for it to have non linear scaling.
|
||||
return 1.0f + normalizedSpeed.pow(2).toFloat() * 0.2f
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(settings: JetNoRollSettings, state: JetNoRollState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: JetNoRollSettings) = JetNoRollState()
|
||||
|
||||
override fun damageOnCollision(
|
||||
settings: JetNoRollSettings,
|
||||
state: JetNoRollState,
|
||||
vehicle: PokemonEntity,
|
||||
impactVec: Vec3
|
||||
): Boolean {
|
||||
val impactSpeed = impactVec.horizontalDistance().toFloat() * 10f
|
||||
return vehicle.causeFallDamage(impactSpeed, 1f, vehicle.damageSources().flyIntoWall())
|
||||
}
|
||||
}
|
||||
|
||||
class JetNoRollSettings : RidingBehaviourSettings {
|
||||
override val key = JetNoRollBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var gravity: Expression = "0".asExpression()
|
||||
private set
|
||||
|
||||
var minSpeed: Expression = "1.2".asExpression()
|
||||
private set
|
||||
|
||||
var handlingYawExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 50.0, 25.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Make configurable by json
|
||||
var infiniteStamina: Expression = "false".asExpression()
|
||||
private set
|
||||
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 300.0, 128.0)".asExpression()
|
||||
private set
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.8, 1.0)".asExpression()
|
||||
private set
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.0)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 60.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(gravity)
|
||||
buffer.writeExpression(minSpeed)
|
||||
buffer.writeExpression(handlingYawExpr)
|
||||
buffer.writeExpression(infiniteStamina)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
gravity = buffer.readExpression()
|
||||
minSpeed = buffer.readExpression()
|
||||
handlingYawExpr = buffer.readExpression()
|
||||
infiniteStamina = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class JetNoRollState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseXForce = ridingState(0.0, Side.CLIENT)
|
||||
var currMouseYForce = ridingState(0.0, Side.CLIENT)
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeDouble(currSpeed.get())
|
||||
buffer.writeFloat(stamina.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
currSpeed.set(buffer.readDouble(), forced = true)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
currSpeed.set(0.0, forced = true)
|
||||
currMouseXForce.set(0.0, forced = true)
|
||||
currMouseYForce.set(0.0, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = JetNoRollState().also {
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
// it.currMouseXForce.set(currMouseXForce.get(), forced = true)
|
||||
// it.currMouseYForce.set(currMouseYForce.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is JetNoRollState) return false
|
||||
if (previous.currSpeed != currSpeed) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+561
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.air
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import kotlin.math.*
|
||||
|
||||
class RocketBehaviour : RidingBehaviour<RocketSettings, RocketState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("air/rocket")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: RocketSettings, state: RocketState): RidingStyle {
|
||||
return RidingStyle.AIR
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<RocketSettings, RocketState>(PoseType.HOVER)
|
||||
.with(PoseOption(PoseType.FLY) { _, state, _ ->
|
||||
return@PoseOption state.boosting.get()
|
||||
})
|
||||
|
||||
override fun isActive(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !blockState.isAir && blockState.fluidState.isEmpty
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): PoseType {
|
||||
return this.poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
// Use this as a "tick" function and check to see if the driver is "boosting"
|
||||
if(vehicle.level().isClientSide) {
|
||||
|
||||
//If the forward key is not held then it cannot be boosting
|
||||
if(Minecraft.getInstance().options.keyUp.isDown()) {
|
||||
val boostKeyPressed = Minecraft.getInstance().options.keySprint.isDown()
|
||||
|
||||
//If on the previous tick the boost key was held then don't change if the ride is boosting
|
||||
if(state.boostIsToggleable.get() && boostKeyPressed) {
|
||||
//flip the boosting state if boost key is pressed
|
||||
state.boosting.set(!state.boosting.get())
|
||||
}
|
||||
|
||||
//If the boost key is not held then next tick boosting is toggleable
|
||||
state.boostIsToggleable.set(!boostKeyPressed)
|
||||
} else {
|
||||
//Turn off boost and reset boost params
|
||||
state.boostIsToggleable.set(true)
|
||||
state.boosting.set(false)
|
||||
state.canSpeedBurst.set(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
val f = Mth.wrapDegrees(driver.yRot - vehicle.yRot)
|
||||
val lookYawLimit = 90.0f
|
||||
val g = Mth.clamp(f, -lookYawLimit, lookYawLimit)
|
||||
|
||||
val ticksBeforeCorrection = 40
|
||||
|
||||
if(state.prevCamYRot.get() == driver.yRot){
|
||||
state.ticksNoInput.set(state.ticksNoInput.get() + 1)
|
||||
} else {
|
||||
state.ticksNoInput.set(0)
|
||||
}
|
||||
val correctionForce = if(state.ticksNoInput.get() >= ticksBeforeCorrection && f != 0.0f && state.boosting.get()) 1f * min(sqrt(lookYawLimit / (abs(f))), 10.0f) else 0.0f
|
||||
driver.yRotO += g - f
|
||||
driver.yRot = driver.yRot + g - f //- ( clamp(f.sign * correctionForce, -abs(f), abs(f)) )
|
||||
//driver.yRot = driver.yRot + g - f - ( min )
|
||||
state.prevCamYRot.set(driver.yRot)
|
||||
driver.setYHeadRot(driver.yRot)
|
||||
}
|
||||
|
||||
|
||||
override fun rotation(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
// Grab turningAcceleration and divide 20 twice to get
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) / 20.0f) / 20.0f * 2
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
|
||||
val maxTurnMomentum = 80.0f / 20.0f
|
||||
|
||||
// Base boost stats off of normal turning stats
|
||||
val boostMaxTurnMomentum = maxTurnMomentum * 0.1f
|
||||
val boostTurnInput = turnInput * 0.15f
|
||||
|
||||
if(state.boosting.get()) {
|
||||
if(driver.xxa != 0.0f && abs(newMomentum + turnInput) < (boostMaxTurnMomentum)) { //If max momentum will not be exceeded then modulate
|
||||
newMomentum += boostTurnInput
|
||||
} else {
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.05)
|
||||
}
|
||||
} else {
|
||||
if(driver.xxa == 0.0f) { //If no turning input then lerp to 0
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.05)
|
||||
} else if(abs(newMomentum + turnInput) < maxTurnMomentum) { //If max momentum will not be exceeded then modulate
|
||||
newMomentum += turnInput
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
state.turnMomentum.set(newMomentum.toFloat())
|
||||
driver.yRot += newMomentum.toFloat()
|
||||
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun velocity(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val retVel = calculateRideSpaceVel(settings, state, vehicle, driver)
|
||||
state.rideVelocity.set(retVel)
|
||||
return retVel
|
||||
}
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 2
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.5 * 2
|
||||
|
||||
var newVelocity = vehicle.deltaMovement
|
||||
|
||||
// Align the direction of movement with the world coordinate space so as not to modify it through turning
|
||||
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
|
||||
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.boosting.get()) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
vehicle.deltaMovement.horizontalDistance() > topSpeed && (driver.zza.sign == newVelocity.z.sign.toFloat()) -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
} else if ((state.stamina.get() > 0.0 && state.boosting.get())) {
|
||||
val boostSpeed = topSpeed * 5
|
||||
val boostAccel = if(newVelocity.length() < boostSpeed) accel * 3 else 0.0
|
||||
val forwardInput = 1.0f
|
||||
var burst = 0.0f
|
||||
|
||||
if(state.canSpeedBurst.get()) {
|
||||
burst = 0.25f
|
||||
state.canSpeedBurst.set(false)
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
newVelocity.z + (boostAccel * forwardInput.toDouble()) + burst)
|
||||
}
|
||||
|
||||
//Vertical movement based on driver input.
|
||||
val maxVertSpeed = 0.5f
|
||||
var vertInput = 0.0
|
||||
|
||||
//If boosting then don't lose altitude
|
||||
//otherwise propel up or fall
|
||||
if (state.boosting.get() && !driver.jumping && vehicle.deltaMovement.y > (-maxVertSpeed * 0.25)) {
|
||||
vertInput = -0.7
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y + accel * vertInput,
|
||||
newVelocity.z)
|
||||
} else if(state.boosting.get() && vehicle.deltaMovement.y < maxVertSpeed) {
|
||||
vertInput = when {
|
||||
driver.jumping -> 2.0
|
||||
else -> 0.0
|
||||
}
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * 0.5 * vertInput)),
|
||||
newVelocity.z)
|
||||
} else {
|
||||
if (driver.jumping && !(vehicle.deltaMovement.y > maxVertSpeed && (newVelocity.y.sign.toFloat() > 0.0))) {
|
||||
vertInput = 1.0
|
||||
|
||||
// Reset falldistance if upward motion is detected
|
||||
vehicle.resetFallDistance()
|
||||
} else {
|
||||
vertInput = -2.5
|
||||
}
|
||||
|
||||
if (state.stamina.get() > 0.0) {
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
(newVelocity.y + (accel * vertInput)),
|
||||
newVelocity.z)
|
||||
}
|
||||
}
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
//Smooth out mouse input.
|
||||
val smoothingSpeed = 4
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseX * 0.1, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseY * 0.1, deltaTime * smoothingSpeed);
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3(0.0, yInput, xInput)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
//Retrieve stamina from state and use it to set the "stamina bar"
|
||||
return (state.stamina.get() / 1.0f)
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
if (state.boosting.get()) {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
//val exceededSpeed = max(vehicle.deltaMovement.length() - topSpeed, 0.0)
|
||||
|
||||
//TODO: Remove this magic number and get a better comparison for boost top speed
|
||||
val normalizedBoostSpeed = RidingBehaviour.scaleToRange(state.rideVelocity.get().length(), topSpeed, topSpeed * 3)
|
||||
return 1.0f + normalizedBoostSpeed.pow(2).toFloat() * 0.2f
|
||||
} else {
|
||||
return 1.0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: RocketSettings,
|
||||
state: RocketState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: RocketSettings) = RocketState()
|
||||
}
|
||||
|
||||
class RocketSettings : RidingBehaviourSettings {
|
||||
override val key = RocketBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 0.65, 0.3) * 0.25".asExpression()
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 3.0))) * 0.5".asExpression()
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 10.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
// Controls the acceleration of the turning speed since the max turning speed is locked at 60 degrees/second
|
||||
// So this is a range of 60 degrees/second per second to 20. Meaning at max stats when holding a direction it
|
||||
// takes a full second to be turning at max speed.
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 60.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
buffer.writeExpression(staminaExpr)
|
||||
buffer.writeExpression(jumpExpr)
|
||||
buffer.writeExpression(handlingExpr)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
staminaExpr = buffer.readExpression()
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RocketState : RidingBehaviourState() {
|
||||
var boosting = ridingState(false, Side.BOTH)
|
||||
var boostIsToggleable = ridingState(false, Side.BOTH)
|
||||
var canSpeedBurst = ridingState(false, Side.BOTH)
|
||||
var prevCamYRot: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
|
||||
var ticksNoInput: SidedRidingState<Int> = ridingState(0, Side.CLIENT)
|
||||
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
boosting.set(false, forced = true)
|
||||
boostIsToggleable.set(false, forced = true)
|
||||
canSpeedBurst.set(true, forced = true)
|
||||
prevCamYRot.set(0.0f, forced = true)
|
||||
ticksNoInput.set(0, forced = true)
|
||||
turnMomentum.set(0.0f, forced = true)
|
||||
}
|
||||
|
||||
override fun copy() = RocketState().also {
|
||||
it.rideVelocity.set(this.rideVelocity.get(), forced = true)
|
||||
it.stamina.set(this.stamina.get(), forced = true)
|
||||
it.boosting.set(this.boosting.get(), forced = true)
|
||||
it.boostIsToggleable.set(this.boosting.get(), forced = true)
|
||||
it.canSpeedBurst.set(this.canSpeedBurst.get(), forced = true)
|
||||
it.prevCamYRot.set(this.prevCamYRot.get(), forced = true)
|
||||
it.ticksNoInput.set(this.ticksNoInput.get(), forced = true)
|
||||
it.turnMomentum.set(this.turnMomentum.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(boosting.get())
|
||||
buffer.writeBoolean(boostIsToggleable.get())
|
||||
buffer.writeBoolean(canSpeedBurst.get())
|
||||
buffer.writeFloat(prevCamYRot.get())
|
||||
buffer.writeInt(ticksNoInput.get())
|
||||
buffer.writeFloat(turnMomentum.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
boosting.set(buffer.readBoolean(), forced = true)
|
||||
boostIsToggleable.set(buffer.readBoolean(), forced = true)
|
||||
canSpeedBurst.set(buffer.readBoolean(), forced = true)
|
||||
prevCamYRot.set(buffer.readFloat(), forced = true)
|
||||
ticksNoInput.set(buffer.readInt(), forced = true)
|
||||
turnMomentum.set(buffer.readFloat(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is RocketState) return false
|
||||
if (previous.boosting.get() != boosting.get()) return true
|
||||
if (previous.boostIsToggleable.get() != boostIsToggleable.get()) return true
|
||||
if (previous.canSpeedBurst.get() != canSpeedBurst.get()) return true
|
||||
if (previous.prevCamYRot.get() != prevCamYRot.get()) return true
|
||||
if (previous.ticksNoInput.get() != ticksNoInput.get()) return true
|
||||
if (previous.turnMomentum.get() != turnMomentum.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+6
@@ -12,10 +12,13 @@ import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.strategies.CompositeRidingStrategies
|
||||
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.adapters.RidingBehaviourSettingsAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -380,8 +383,10 @@ open class CompositeSettings : RidingBehaviourSettings {
|
||||
private set
|
||||
|
||||
override val key: ResourceLocation = CompositeBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeRidingStats(stats)
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeResourceLocation(transitionStrategy)
|
||||
defaultBehaviour.encode(buffer)
|
||||
@@ -389,6 +394,7 @@ open class CompositeSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
transitionStrategy = buffer.readResourceLocation()
|
||||
val defaultBehaviourKey = buffer.readResourceLocation()
|
||||
defaultBehaviour = RidingBehaviourSettingsAdapter.types[defaultBehaviourKey]?.getConstructor()?.newInstance()
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
|
||||
): Boolean {
|
||||
if (state.activeBehaviour.get() == defaultSettings.key) return false
|
||||
if (!vehicle.onGround()) return false
|
||||
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
|
||||
if (state.lastTransition.get() + 5 >= vehicle.level().gameTime) return false
|
||||
val defaultBehaviour = RidingBehaviours.get(defaultSettings.key)
|
||||
return defaultBehaviour.isActive(defaultSettings, state.defaultBehaviourState, vehicle)
|
||||
}
|
||||
@@ -58,7 +58,7 @@ object JumpStrategy : CompositeRidingStrategy<CompositeSettings> {
|
||||
): Boolean {
|
||||
if (state.activeBehaviour.get() == alternativeSettings.key) return false
|
||||
if (!driver.jumping) return false
|
||||
if (state.lastTransition.get() + 20 >= vehicle.level().gameTime) return false
|
||||
if (state.lastTransition.get() + 5 >= vehicle.level().gameTime) return false
|
||||
val alternativeBehaviour = RidingBehaviours.get(alternativeSettings.key)
|
||||
return alternativeBehaviour.isActive(alternativeSettings, state.alternateBehaviourState, vehicle)
|
||||
}
|
||||
|
||||
+40
-58
@@ -14,6 +14,7 @@ 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.*
|
||||
@@ -83,7 +84,15 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
|
||||
// Use this as a "tick" function and calculate sprinting and inAir state here
|
||||
state.sprinting.set(driver.isSprinting)
|
||||
inAirCheck(state, vehicle)
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
// Check both vertical movement and if there are blocks below.
|
||||
val posBelow = vehicle.blockPosition().below()
|
||||
val blockStateBelow = vehicle.level().getBlockState(posBelow)
|
||||
@@ -92,14 +101,10 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
// val level = vehicle.level()
|
||||
// val toesBox = vehicle.boundingBox.move(0.0, -0.1, 0.0)
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(inAir)
|
||||
|
||||
return state.rideVelocity.get().length().toFloat()
|
||||
}
|
||||
|
||||
override fun updatePassengerRotation(
|
||||
@@ -107,26 +112,7 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
state: HorseState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
) {
|
||||
|
||||
//Take the inverse so that it cancels out how
|
||||
//much the entity rotates.
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
//Normalize the current rotation diff
|
||||
val rotMod = Mth.wrapDegrees(driver.yRot - vehicle.yRot) / maxYawDiff
|
||||
|
||||
val rotAmount = 10.0f
|
||||
|
||||
//Take the inverse so that you turn more at higher speeds
|
||||
val normSpeed = 1.0f - 0.5f*normalizeVal(state.rideVelocity.get().length(), 0.0, topSpeed).toFloat()
|
||||
|
||||
//driver.yRot += (entity.riding.deltaRotation.y - turnAmount)
|
||||
//driver.setYHeadRot(driver.yHeadRot + (entity.riding.deltaRotation.y) - turnAmount)
|
||||
}
|
||||
) { return }
|
||||
|
||||
override fun clampPassengerRotation(
|
||||
settings: HorseSettings,
|
||||
@@ -149,9 +135,9 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
var turnAmount = calcRotAmount(settings, state, vehicle, driver)
|
||||
|
||||
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount )
|
||||
return Vec2(vehicle.xRot, vehicle.yRot + turnAmount * 0.5f )
|
||||
|
||||
}
|
||||
|
||||
@@ -169,9 +155,9 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
): Float {
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
|
||||
//In degrees per second
|
||||
val walkHandling = 140.0 * 2
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * 2
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr) * 3
|
||||
val walkHandlingBoost = 5
|
||||
val walkHandling = handling * walkHandlingBoost
|
||||
|
||||
val maxYawDiff = 90.0f
|
||||
|
||||
@@ -185,13 +171,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
val rotDiffMod = (sqrt(abs(rotDiffNorm)) * rotDiffNorm.sign)
|
||||
|
||||
//Take the inverse so that you turn less at higher speeds
|
||||
val normSpeed = 1.0f // = 1.0f - 0.5f*normalizeVal(state.rideVelocityocity.length(), 0.0, topSpeed).toFloat()
|
||||
val normSpeed = 1.0f
|
||||
|
||||
// TurnRate should always be quick if not sprinting
|
||||
val turnRate = if(state.sprinting.get()) (handling.toFloat() / 20.0f) else (walkHandling.toFloat() / 20.0f)
|
||||
val invRelSpeed = (RidingBehaviour.scaleToRange(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 1.0f) * -1.0f
|
||||
val turnRate = ((handling.toFloat() / 20.0f) * max(walkHandlingBoost * invRelSpeed, 1.0)).toFloat()
|
||||
|
||||
//Ensure you only ever rotate as much difference as there is between the angles.
|
||||
val turnSpeed = turnRate * rotDiffMod * normSpeed
|
||||
val turnSpeed = turnRate * rotDiffMod * normSpeed
|
||||
val rotAmount = turnSpeed.coerceIn(-abs(rotDiff), abs(rotDiff))
|
||||
|
||||
return rotAmount
|
||||
@@ -215,19 +202,17 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
val canSprint = vehicle.runtime.resolveBoolean(settings.canSprint)
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 1.3
|
||||
val walkSpeed = getWalkSpeed(vehicle)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val topSpeed = if(state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr)
|
||||
val topSpeed = if(canSprint && state.sprinting.get()) rideTopSpeed else walkSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 2
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z)
|
||||
|
||||
var newVelocity = Vec3(state.rideVelocity.get().x, state.rideVelocity.get().y, state.rideVelocity.get().z).normalize().scale(vehicle.deltaMovement.length())
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0) {
|
||||
@@ -246,11 +231,11 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
//Gravity logic
|
||||
// Gravity logic
|
||||
if (vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
} else {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.8
|
||||
val terminalVel = 2.0
|
||||
|
||||
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
|
||||
@@ -262,27 +247,19 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
newVelocity = newVelocity.subtract(0.0, 0.0, min(0.03 * newVelocity.z.sign, newVelocity.z))
|
||||
}
|
||||
|
||||
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
|
||||
//Jump the thang!
|
||||
if (driver.jumping && vehicle.onGround()) {
|
||||
if (driver.jumping && vehicle.onGround() && canJump) {
|
||||
val jumpForce = 1.0
|
||||
val horz = state.rideVelocity.get().horizontalDistance()
|
||||
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
|
||||
val velMag = newVelocity.length()
|
||||
|
||||
//Ensure this doesn't add unwanted forward velocity
|
||||
//val mag = if(newVelocity.length() < rideTopSpeed) newVelocity.length() else rideTopSpeed
|
||||
//newVelocity = newVelocity.normalize().scale(mag)
|
||||
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
|
||||
}
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
//Zero out lateral velocity possibly picked up from a controller transition
|
||||
newVelocity = Vec3(0.0, newVelocity.y, newVelocity.z)
|
||||
|
||||
/*
|
||||
* Normalizes the current speed between minSpeed and maxSpeed.
|
||||
* The result is clamped between 0.0 and 1.0, where 0.0 represents minSpeed and 1.0 represents maxSpeed.
|
||||
*/
|
||||
private fun normalizeVal(currSpeed: Double, minSpeed: Double, maxSpeed: Double): Double {
|
||||
require(maxSpeed > minSpeed) { "maxSpeed must be greater than minSpeed" }
|
||||
return ((currSpeed - minSpeed) / (maxSpeed - minSpeed)).coerceIn(0.0, 1.0)
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
private fun getWalkSpeed( vehicle: PokemonEntity ): Double {
|
||||
@@ -453,10 +430,14 @@ class HorseBehaviour : RidingBehaviour<HorseSettings, HorseState> {
|
||||
|
||||
class HorseSettings : RidingBehaviourSettings {
|
||||
override val key = HorseBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var canSprint = "true".asExpression()
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
private set
|
||||
|
||||
@@ -480,6 +461,7 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(speedExpr)
|
||||
buffer.writeExpression(accelerationExpr)
|
||||
@@ -489,6 +471,7 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
speedExpr = buffer.readExpression()
|
||||
accelerationExpr = buffer.readExpression()
|
||||
@@ -496,7 +479,6 @@ class HorseSettings : RidingBehaviourSettings {
|
||||
jumpExpr = buffer.readExpression()
|
||||
handlingExpr = buffer.readExpression()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HorseState : RidingBehaviourState() {
|
||||
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import org.joml.Matrix3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
|
||||
class MinekartBehaviour : RidingBehaviour<MinekartSettings, MinekartState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/minekart")
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
|
||||
override fun getRidingStyle(settings: MinekartSettings, state: MinekartState): RidingStyle {
|
||||
return RidingStyle.LAND
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<MinekartSettings, MinekartState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
|
||||
})
|
||||
|
||||
// val poseProvider = PoseProvider<MinekartSettings, MinekartState>(PoseType.STAND)
|
||||
// .with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
//Need to check other fluids
|
||||
if (vehicle.isInWater || vehicle.isUnderWater) {
|
||||
return@any false
|
||||
}
|
||||
//This might not actually work, depending on what the yPos actually is. yPos of the middle of the entity? the feet?
|
||||
if (it.y.toDouble() == (vehicle.position().y)) {
|
||||
val blockState = vehicle.level().getBlockState(it.below())
|
||||
return@any !(!blockState.isAir && blockState.fluidState.isEmpty)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun pose(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): PoseType {
|
||||
return poseProvider.select(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun speed(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
|
||||
val boostMax = vehicle.runtime.resolveFloat(settings.boostLimit)
|
||||
//TODO: should this be by how much turn has accumulated or simply time turning... or both?
|
||||
val boostPerSecond = 20.0f
|
||||
val boostLoss = 40.0f
|
||||
|
||||
if (driver.jumping && driver.zza > 0.0f) {
|
||||
if (!state.drifting.get()) {
|
||||
if (driver.xxa != 0.0f) {
|
||||
state.clockwiseDrift.set(driver.xxa.sign > 0.0)
|
||||
state.turnMomentum.set(0.0f)
|
||||
state.drifting.set(true)
|
||||
state.boost.set(0.0f)
|
||||
} else {
|
||||
//TODO: Add jumping logic
|
||||
}
|
||||
} else {
|
||||
state.boost.set((state.boost.get() + boostPerSecond / 20.0f).coerceIn(0.0f, boostMax))
|
||||
}
|
||||
} else {
|
||||
if(driver.zza <= 0.0 && state.boost.get() != 0.0f){
|
||||
state.boost.set(0.0f)
|
||||
} else if( state.boost.get() > 0.0f ) {
|
||||
state.boost.set(max(state.boost.get() - boostLoss, 0.0f))
|
||||
}
|
||||
state.drifting.set(false)
|
||||
}
|
||||
|
||||
inAirCheck(state, vehicle)
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
// Check both vertical movement and if there are blocks below.
|
||||
val posBelow = vehicle.blockPosition().below()
|
||||
val blockStateBelow = vehicle.level().getBlockState(posBelow)
|
||||
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
|
||||
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(inAir)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
//TODO: maybe tie this to max turnspeeds rather than the turn acceleration
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
val driftInput = turnInput * 0.25f
|
||||
|
||||
val lowSpeedTurnBoost = (RidingBehaviour.scaleToRange(vehicle.deltaMovement.horizontalDistance(), 0.0, topSpeed ).pow(0.5) - 2.0f) * -1.0f
|
||||
val forcedDriftTurnMomentum = (80.0f / 20.0f) * lowSpeedTurnBoost
|
||||
val maxTurnMomentum = (60.0f / 20.0f) * lowSpeedTurnBoost
|
||||
val maxDriftMomentum = forcedDriftTurnMomentum * 0.6f
|
||||
|
||||
if (!state.drifting.get()){
|
||||
// perform non drift turning
|
||||
if(abs(newMomentum) <= maxTurnMomentum && turnInput != 0.0 ) {
|
||||
newMomentum += turnInput
|
||||
newMomentum = min(max(newMomentum, maxTurnMomentum * -1.0), maxTurnMomentum.toDouble())
|
||||
} else {
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.15)
|
||||
}
|
||||
|
||||
state.turnMomentum.set(newMomentum.toFloat())
|
||||
driver.yRot += newMomentum.toFloat()
|
||||
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
|
||||
} else {
|
||||
// perform drift turning
|
||||
if(abs(newMomentum) <= maxDriftMomentum && driftInput != 0.0 ) {
|
||||
newMomentum += driftInput
|
||||
newMomentum = min(max(newMomentum, maxDriftMomentum * -1.0), maxDriftMomentum.toDouble())
|
||||
} else {
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.15)
|
||||
}
|
||||
|
||||
val signedDriftTurn = if(state.clockwiseDrift.get()) forcedDriftTurnMomentum * -1.0f else forcedDriftTurnMomentum
|
||||
state.turnMomentum.set(newMomentum.toFloat())
|
||||
driver.yRot += newMomentum.toFloat() + signedDriftTurn.toFloat()
|
||||
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat() + signedDriftTurn.toFloat())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
return state.rideVelocity.get()
|
||||
}
|
||||
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
|
||||
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.7 else rideTopSpeed
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
|
||||
val driftAngle = 20.0f
|
||||
|
||||
//Align the direction of this vector with the vehicles current heading.
|
||||
var newVelocity = vehicle.deltaMovement
|
||||
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
|
||||
|
||||
// rotate the angle of force if drifting
|
||||
if (state.drifting.get()) {
|
||||
val signedDriftAngle = if(state.clockwiseDrift.get()) driftAngle else driftAngle * -1.0f
|
||||
newVelocity = newVelocity.yRot(signedDriftAngle.toRadians())
|
||||
}
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.inAir.get()) {
|
||||
//make sure it can't exceed top speed
|
||||
var forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z <= 0.0 -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
if (!state.drifting.get() && state.boost.get() != 0.0f) {
|
||||
forwardInput = 2.0;
|
||||
}
|
||||
|
||||
// If drifting we need more acceleration due to the constant turn
|
||||
val driftAccelBoost = if (state.drifting.get()) 1.5 else 1.0
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble() * driftAccelBoost)))
|
||||
}
|
||||
|
||||
// Gravity logic
|
||||
if (vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
} else {
|
||||
//TODO: Should we just go back to standard minecraft gravity or do the lerp modifications prevent that?
|
||||
//I think minecrafts gravity logic is also too harsh and isn't gamefied enough for mounts maybe? Need
|
||||
//to do some testing and get other's opinions
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.25 * 3.0
|
||||
val terminalVel = 2.0
|
||||
|
||||
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
|
||||
}
|
||||
|
||||
//ground Friction
|
||||
var friction = 0.003 * 2.0
|
||||
if (state.drifting.get()) {friction *= 0.5}
|
||||
if (!state.inAir.get()) {
|
||||
newVelocity = newVelocity.subtract(
|
||||
min(friction , abs(newVelocity.x)) * newVelocity.x.sign,
|
||||
0.0,
|
||||
min(friction , abs(newVelocity.z)) * newVelocity.z.sign
|
||||
)
|
||||
|
||||
// TEST FOR LATERAL SLIP OR WHATEVER
|
||||
//TODO: calc lateral slip and friction better!
|
||||
if (!state.drifting.get()) {
|
||||
newVelocity = newVelocity.subtract(
|
||||
min(friction * 10, abs(newVelocity.x)) * newVelocity.x.sign,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
|
||||
// val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
// //Jump the thang!
|
||||
// if (driver.jumping && vehicle.onGround() && canJump) {
|
||||
// val jumpForce = 0.5
|
||||
//
|
||||
// newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
|
||||
//
|
||||
// }
|
||||
|
||||
// unrotate the angle of force if drifting
|
||||
if (state.drifting.get()) {
|
||||
val inverseSignedDriftAngle = if(state.clockwiseDrift.get()) driftAngle * -1.0f else driftAngle
|
||||
newVelocity = newVelocity.yRot(inverseSignedDriftAngle.toRadians())
|
||||
}
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun rotationOnMouseXY(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
mouseY: Double,
|
||||
mouseX: Double,
|
||||
mouseYSmoother: SmoothDouble,
|
||||
mouseXSmoother: SmoothDouble,
|
||||
sensitivity: Double,
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Boolean {
|
||||
return vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
}
|
||||
|
||||
override fun setRideBar(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
return 0.0f
|
||||
}
|
||||
|
||||
override fun jumpForce(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
var fov = 1.0f
|
||||
if(!state.drifting.get() && state.boost.get() != 0.0f) {
|
||||
//TODO: is this too abrupt? Possibly caluclate differently?
|
||||
fov = 1.2f
|
||||
}
|
||||
return fov
|
||||
}
|
||||
|
||||
override fun useAngVelSmoothing(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun useRidingAltPose(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
when {
|
||||
state.drifting.get() -> return cobblemonResource("drifting")
|
||||
}
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: MinekartSettings, state: MinekartState, vehicle: PokemonEntity): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun turnOffOnGround(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun dismountOnShift(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePokemonHead(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotateRiderHead(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
settings: MinekartSettings,
|
||||
state: MinekartState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: MinekartSettings) = MinekartState()
|
||||
}
|
||||
|
||||
class MinekartSettings : RidingBehaviourSettings {
|
||||
override val key = MinekartBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var boostLimit = "100.0f".asExpression()
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(boostLimit)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
boostLimit = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
class MinekartState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.BOTH)
|
||||
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
|
||||
var drifting = ridingState(false, Side.CLIENT)
|
||||
var clockwiseDrift = ridingState(false, Side.CLIENT)
|
||||
val boost: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
|
||||
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
currSpeed.set(0.0, forced = true)
|
||||
deltaRotation.set(Vec2.ZERO, forced = true)
|
||||
drifting.set(false, forced = true)
|
||||
clockwiseDrift.set(false, forced = true)
|
||||
boost.set(0.0f, forced = true)
|
||||
turnMomentum.set(0.0f, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(drifting.get())
|
||||
buffer.writeBoolean(clockwiseDrift.get())
|
||||
buffer.writeFloat(boost.get())
|
||||
buffer.writeFloat(turnMomentum.get())
|
||||
buffer.writeBoolean(inAir.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
drifting.set(buffer.readBoolean(), forced = true)
|
||||
clockwiseDrift.set(buffer.readBoolean(), forced = true)
|
||||
boost.set(buffer.readFloat(), forced = true)
|
||||
turnMomentum.set(buffer.readFloat(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "MinekartState(currSpeed=${currSpeed.get()}, deltaRotation=${deltaRotation.get()})"
|
||||
}
|
||||
|
||||
override fun copy() = MinekartState().also {
|
||||
it.rideVelocity.set(rideVelocity.get(), forced = true)
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.deltaRotation.set(deltaRotation.get(), forced = true)
|
||||
it.drifting.set(drifting.get(), forced = true)
|
||||
it.clockwiseDrift.set(clockwiseDrift.get(), forced = true)
|
||||
it.boost.set(boost.get(), forced = true)
|
||||
it.turnMomentum.set(turnMomentum.get(), forced = true)
|
||||
it.inAir.set(inAir.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is MinekartState) return false
|
||||
if (previous.drifting.get() != drifting.get()) return true
|
||||
if (previous.clockwiseDrift.get() != clockwiseDrift.get()) return true
|
||||
if (previous.boost.get() != boost.get()) return true
|
||||
if (previous.turnMomentum.get() != turnMomentum.get()) return true
|
||||
if (previous.inAir.get() != inAir.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
+239
-66
@@ -8,7 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.land
|
||||
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangMath.lerp
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
import com.cobblemon.mod.common.api.riding.RidingStyle
|
||||
@@ -20,6 +21,9 @@ import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
@@ -28,29 +32,15 @@ import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.absoluteValue
|
||||
import org.joml.Matrix3f
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
|
||||
class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
companion object {
|
||||
val KEY = cobblemonResource("land/vehicle")
|
||||
|
||||
val MAX_TOP_SPEED = 1.0 // 20 bl/s
|
||||
val MIN_TOP_SPEED = 0.35 // 7 bl/s
|
||||
//val MIN_SPEED = 0.25 // 5 bl/s
|
||||
|
||||
//Accel will lie between 1.0 second and 5.0 seconds
|
||||
val MAX_ACCEL = (MAX_TOP_SPEED) / (20*3) //3.0 second to max speed
|
||||
val MIN_ACCEL = (MAX_TOP_SPEED) / (20*8) // 8 seconds to max speed
|
||||
|
||||
//Can rotate 90 degrees
|
||||
val MAX_HANDLING = 90.0
|
||||
val MIN_HANDLING = 30.0
|
||||
|
||||
|
||||
val MAX_YAW_HANDLING = 16.0
|
||||
val MIN_YAW_HANDLING = 8.0
|
||||
}
|
||||
|
||||
override val key = KEY
|
||||
@@ -60,7 +50,12 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
|
||||
.with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
.with(PoseOption(PoseType.WALK) { _, state, _ ->
|
||||
return@PoseOption abs(state.rideVelocity.get().horizontalDistance()) > 0.0
|
||||
})
|
||||
|
||||
// val poseProvider = PoseProvider<VehicleSettings, VehicleState>(PoseType.STAND)
|
||||
// .with(PoseOption(PoseType.WALK) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
|
||||
override fun isActive(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
@@ -87,18 +82,38 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Float {
|
||||
val topSpeed = vehicle.getRideStat(RidingStat.SPEED, RidingStyle.AIR, MIN_TOP_SPEED, MAX_TOP_SPEED)
|
||||
val accel = vehicle.getRideStat(RidingStat.ACCELERATION, RidingStyle.AIR, MIN_ACCEL, MAX_ACCEL)
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza > 0.0 && state.currSpeed.get() < topSpeed) {
|
||||
state.currSpeed.set(min(state.currSpeed.get() + accel , topSpeed))
|
||||
} else if (driver.zza < 0.0 && state.currSpeed.get() > 0.0) {
|
||||
//Decelerate is now always a constant half of max acceleration.
|
||||
state.currSpeed.set(max(state.currSpeed.get() - (MAX_ACCEL / 2), 0.0))
|
||||
if (driver.isShiftKeyDown) {
|
||||
state.drifting.set(true)
|
||||
if(driver.zza != 0.0f) {
|
||||
state.poweredDrifting.set(true)
|
||||
} else {
|
||||
state.poweredDrifting.set(false)
|
||||
}
|
||||
} else {
|
||||
state.drifting.set(false)
|
||||
state.poweredDrifting.set(false)
|
||||
}
|
||||
|
||||
return state.currSpeed.get().toFloat()
|
||||
inAirCheck(state, vehicle)
|
||||
return driver.deltaMovement.length().toFloat()
|
||||
}
|
||||
|
||||
fun inAirCheck(
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
) {
|
||||
// Check both vertical movement and if there are blocks below.
|
||||
val posBelow = vehicle.blockPosition().below()
|
||||
val blockStateBelow = vehicle.level().getBlockState(posBelow)
|
||||
val isAirOrLiquid = blockStateBelow.isAir || !blockStateBelow.fluidState.isEmpty
|
||||
|
||||
val canSupportEntity = blockStateBelow.isFaceSturdy(vehicle.level(), posBelow, Direction.UP)
|
||||
val standingOnSolid = canSupportEntity && !isAirOrLiquid
|
||||
|
||||
// inAir if not on the ground
|
||||
val inAir = !(vehicle.deltaMovement.y == 0.0 || standingOnSolid)
|
||||
state.inAir.set(inAir)
|
||||
}
|
||||
|
||||
override fun rotation(
|
||||
@@ -107,10 +122,38 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: LivingEntity
|
||||
): Vec2 {
|
||||
val rotationDegrees = driver.xxa * vehicle.runtime.resolveFloat(settings.rotationSpeed)
|
||||
val rotation = Vec2(driver.xRot, vehicle.yRot - rotationDegrees)
|
||||
state.deltaRotation.set(Vec2(rotation.x - driver.rotationVector.x, rotation.y - vehicle.rotationVector.y))
|
||||
return rotation
|
||||
|
||||
var newMomentum = state.turnMomentum.get().toDouble()
|
||||
|
||||
// Grab turningAcceleration and divide 20 twice to get
|
||||
val turningAcceleration = (vehicle.runtime.resolveDouble(settings.handlingExpr) * 1.5 / 20.0f) / 20.0f * 4.0
|
||||
val turnInput = (driver.xxa *-1.0f) * turningAcceleration
|
||||
|
||||
val maxTurnMomentum = 80.0f / 20.0f
|
||||
|
||||
// Base boost stats off of normal turning stats
|
||||
val driftMaxTurnMomentum = maxTurnMomentum * 3.0f
|
||||
var driftTurnInput = turnInput * 0.4f
|
||||
|
||||
if(state.drifting.get() || state.inAir.get()) {
|
||||
if( newMomentum < driftMaxTurnMomentum) {
|
||||
driftTurnInput = driftTurnInput * (vehicle.deltaMovement.horizontalDistance() / vehicle.runtime.resolveDouble(settings.speedExpr))
|
||||
newMomentum += driftTurnInput
|
||||
}
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.03)
|
||||
if( abs(newMomentum) < 0.05 ) {newMomentum = 0.0}
|
||||
} else {
|
||||
if(abs(newMomentum) <= maxTurnMomentum && turnInput != 0.0 ) {
|
||||
newMomentum += turnInput
|
||||
newMomentum.coerceIn(-maxTurnMomentum.toDouble(), maxTurnMomentum.toDouble())
|
||||
} else {
|
||||
newMomentum = lerp(newMomentum, 0.0, 0.15)
|
||||
}
|
||||
}
|
||||
|
||||
state.turnMomentum.set(newMomentum.toFloat())
|
||||
driver.yRot += newMomentum.toFloat()
|
||||
return Vec2(driver.xRot, vehicle.yRot + newMomentum.toFloat())
|
||||
}
|
||||
|
||||
override fun velocity(
|
||||
@@ -120,18 +163,103 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
driver: Player,
|
||||
input: Vec3
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
val driveFactor = runtime.resolveFloat(settings.driveFactor)
|
||||
var g = driver.zza * driveFactor
|
||||
if (g <= 0.0f) {
|
||||
g *= runtime.resolveFloat(settings.reverseDriveFactor)
|
||||
}
|
||||
if (driver.xxa != 0F && g.absoluteValue < runtime.resolveFloat(settings.minimumSpeedToTurn)) {
|
||||
driver.xxa = 0F
|
||||
}
|
||||
val velocity = Vec3(0.0 /* Maybe do drifting here later */, 0.0, g.toDouble() * state.currSpeed.get())
|
||||
state.rideVelocity.set(calculateRideSpaceVel(settings, state, vehicle, driver))
|
||||
return state.rideVelocity.get()
|
||||
}
|
||||
|
||||
return velocity
|
||||
|
||||
private fun calculateRideSpaceVel(
|
||||
settings: VehicleSettings,
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): Vec3 {
|
||||
|
||||
// Check to see if the ride should be walking or sprinting
|
||||
//val walkSpeed = getWalkSpeed(vehicle)
|
||||
val rideTopSpeed = vehicle.runtime.resolveDouble(settings.speedExpr) * 0.6 * 2.0
|
||||
val topSpeed = if (state.drifting.get()) rideTopSpeed * 0.5 else rideTopSpeed
|
||||
|
||||
val accel = vehicle.runtime.resolveDouble(settings.accelerationExpr) * 0.3 * 3 * 2.0
|
||||
|
||||
//Flag for determining if player is actively inputting
|
||||
var activeInput = false
|
||||
|
||||
var newVelocity = Vec3.ZERO
|
||||
if (!state.drifting.get()) {
|
||||
newVelocity = vehicle.deltaMovement
|
||||
|
||||
newVelocity = newVelocity.yRot(vehicle.yRot.toRadians())
|
||||
|
||||
} else {
|
||||
state
|
||||
newVelocity = vehicle.deltaMovement
|
||||
|
||||
// Align the direction of movement with the world coordinate space so as not to modify it through turning
|
||||
val yawAligned = Matrix3f().rotateY(vehicle.yRot.toRadians())
|
||||
newVelocity = (newVelocity.toVector3f().mul(yawAligned)).toVec3d()
|
||||
}
|
||||
|
||||
//speed up and slow down based on input
|
||||
if (driver.zza != 0.0f && state.stamina.get() > 0.0 && !state.inAir.get()) {
|
||||
//make sure it can't exceed top speed
|
||||
val forwardInput = when {
|
||||
driver.zza > 0 && newVelocity.z > topSpeed -> 0.0
|
||||
driver.zza < 0 && newVelocity.z <= 0.0 -> 0.0
|
||||
else -> driver.zza.sign
|
||||
}
|
||||
|
||||
newVelocity = Vec3(
|
||||
newVelocity.x,
|
||||
newVelocity.y,
|
||||
(newVelocity.z + (accel * forwardInput.toDouble())))
|
||||
|
||||
activeInput = true
|
||||
}
|
||||
|
||||
// Gravity logic
|
||||
if (vehicle.onGround()) {
|
||||
newVelocity = Vec3(newVelocity.x, 0.0, newVelocity.z)
|
||||
} else {
|
||||
val gravity = (9.8 / ( 20.0)) * 0.2 * 0.25 * 3.0
|
||||
val terminalVel = 2.0
|
||||
|
||||
val fallingForce = gravity - ( newVelocity.z.sign *gravity *(abs(newVelocity.z) / 2.0))
|
||||
newVelocity = Vec3(newVelocity.x, max(newVelocity.y - fallingForce, -terminalVel), newVelocity.z)
|
||||
}
|
||||
|
||||
//ground Friction
|
||||
var friction = 0.003 * 2.0
|
||||
if (state.drifting.get()) {friction *= 0.1}
|
||||
if (!state.inAir.get()) {
|
||||
newVelocity = newVelocity.subtract(
|
||||
min(friction , abs(newVelocity.x)) * newVelocity.x.sign,
|
||||
0.0,
|
||||
min(friction , abs(newVelocity.z)) * newVelocity.z.sign
|
||||
)
|
||||
|
||||
// TEST FOR LATERAL SLIP OR WHATEVER
|
||||
//TODO: calc lateral slip and friction better!
|
||||
if (!state.drifting.get()) {
|
||||
newVelocity = newVelocity.subtract(
|
||||
min(friction * 10, abs(newVelocity.x)) * newVelocity.x.sign,
|
||||
0.0,
|
||||
0.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Change this so its tied to a jumping stat and representative of the amount of jumps
|
||||
val canJump = vehicle.runtime.resolveBoolean(settings.canJump)
|
||||
//Jump the thang!
|
||||
if (driver.jumping && vehicle.onGround() && canJump) {
|
||||
val jumpForce = 0.5
|
||||
|
||||
newVelocity = newVelocity.add(0.0, jumpForce, 0.0)
|
||||
|
||||
}
|
||||
|
||||
return newVelocity
|
||||
}
|
||||
|
||||
override fun angRollVel(
|
||||
@@ -189,10 +317,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
driver: Player,
|
||||
jumpStrength: Int
|
||||
): Vec3 {
|
||||
val runtime = vehicle.runtime
|
||||
runtime.environment.query.addFunction("jump_strength") { DoubleValue(jumpStrength.toDouble()) }
|
||||
val jumpVector = settings.jumpVector.map { runtime.resolveFloat(it) }
|
||||
return Vec3(jumpVector[0].toDouble(), jumpVector[1].toDouble(), jumpVector[2].toDouble())
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun gravity(
|
||||
@@ -201,7 +326,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
vehicle: PokemonEntity,
|
||||
regularGravity: Double
|
||||
): Double {
|
||||
return regularGravity
|
||||
return 0.0
|
||||
}
|
||||
|
||||
override fun rideFovMultiplier(
|
||||
@@ -227,11 +352,15 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
vehicle: PokemonEntity,
|
||||
driver: Player
|
||||
): ResourceLocation {
|
||||
when {
|
||||
state.poweredDrifting.get() -> return cobblemonResource("powered_drifting")
|
||||
state.drifting.get() -> return cobblemonResource("drifting")
|
||||
}
|
||||
return cobblemonResource("no_pose")
|
||||
}
|
||||
|
||||
override fun inertia(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Double {
|
||||
return 0.5
|
||||
return 1.0
|
||||
}
|
||||
|
||||
override fun shouldRoll(settings: VehicleSettings, state: VehicleState, vehicle: PokemonEntity): Boolean {
|
||||
@@ -267,7 +396,7 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
state: VehicleState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getRideSounds(
|
||||
@@ -283,13 +412,11 @@ class VehicleBehaviour : RidingBehaviour<VehicleSettings, VehicleState> {
|
||||
|
||||
class VehicleSettings : RidingBehaviourSettings {
|
||||
override val key = VehicleBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
|
||||
var jumpVector = listOf("0".asExpression(), "0.3".asExpression(), "0".asExpression())
|
||||
private set
|
||||
|
||||
var speed = "0.3".asExpression()
|
||||
private set
|
||||
|
||||
@@ -299,44 +426,52 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
var reverseDriveFactor = "0.25".asExpression()
|
||||
private set
|
||||
|
||||
var minimumSpeedToTurn = "0.1".asExpression()
|
||||
private set
|
||||
|
||||
var rotationSpeed = "45/20".asExpression()
|
||||
private set
|
||||
|
||||
var lookYawLimit = "101".asExpression()
|
||||
private set
|
||||
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'LAND', 1.0, 0.3)".asExpression()
|
||||
private set
|
||||
|
||||
// Max accel is a whole 1.0 in 1 second. The conversion in the function below is to convert seconds to ticks
|
||||
var accelerationExpr: Expression =
|
||||
"q.get_ride_stats('ACCELERATION', 'LAND', (1.0 / (20.0 * 1.5)), (1.0 / (20.0 * 5.0)))".asExpression()
|
||||
private set
|
||||
|
||||
// Between 30 seconds and 10 seconds at the lowest when at full speed.
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'LAND', 30.0, 10.0)".asExpression()
|
||||
private set
|
||||
|
||||
//Between a one block jump and a ten block jump
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'LAND', 10.0, 1.0)".asExpression()
|
||||
private set
|
||||
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'LAND', 140.0, 20.0)".asExpression()
|
||||
private set
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(jumpVector[0])
|
||||
buffer.writeExpression(jumpVector[1])
|
||||
buffer.writeExpression(jumpVector[2])
|
||||
buffer.writeExpression(speed)
|
||||
buffer.writeExpression(driveFactor)
|
||||
buffer.writeExpression(reverseDriveFactor)
|
||||
buffer.writeExpression(minimumSpeedToTurn)
|
||||
buffer.writeExpression(rotationSpeed)
|
||||
buffer.writeExpression(lookYawLimit)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
jumpVector = listOf(
|
||||
buffer.readExpression(),
|
||||
buffer.readExpression(),
|
||||
buffer.readExpression()
|
||||
)
|
||||
speed = buffer.readExpression()
|
||||
driveFactor = buffer.readExpression()
|
||||
reverseDriveFactor = buffer.readExpression()
|
||||
minimumSpeedToTurn = buffer.readExpression()
|
||||
rotationSpeed = buffer.readExpression()
|
||||
lookYawLimit = buffer.readExpression()
|
||||
}
|
||||
@@ -345,11 +480,36 @@ class VehicleSettings : RidingBehaviourSettings {
|
||||
class VehicleState : RidingBehaviourState() {
|
||||
var currSpeed = ridingState(0.0, Side.BOTH)
|
||||
var deltaRotation = ridingState(Vec2.ZERO, Side.BOTH)
|
||||
var drifting = ridingState(false, Side.CLIENT)
|
||||
var poweredDrifting = ridingState(false, Side.CLIENT)
|
||||
val turnMomentum: SidedRidingState<Float> = ridingState(0.0f, Side.CLIENT)
|
||||
var inAir = ridingState(false, Side.CLIENT)
|
||||
|
||||
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
currSpeed.set(0.0, forced = true)
|
||||
deltaRotation.set(Vec2.ZERO, forced = true)
|
||||
drifting.set(false, forced = true)
|
||||
poweredDrifting.set(false, forced = true)
|
||||
turnMomentum.set(0.0f, forced = true)
|
||||
inAir.set(false, forced = true)
|
||||
}
|
||||
|
||||
override fun encode(buffer: FriendlyByteBuf) {
|
||||
super.encode(buffer)
|
||||
buffer.writeBoolean(drifting.get())
|
||||
buffer.writeBoolean(poweredDrifting.get())
|
||||
buffer.writeFloat(turnMomentum.get())
|
||||
buffer.writeBoolean(inAir.get())
|
||||
}
|
||||
|
||||
override fun decode(buffer: FriendlyByteBuf) {
|
||||
super.decode(buffer)
|
||||
drifting.set(buffer.readBoolean(), forced = true)
|
||||
poweredDrifting.set(buffer.readBoolean(), forced = true)
|
||||
turnMomentum.set(buffer.readFloat(), forced = true)
|
||||
inAir.set(buffer.readBoolean(), forced = true)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -361,5 +521,18 @@ class VehicleState : RidingBehaviourState() {
|
||||
it.stamina.set(stamina.get(), forced = true)
|
||||
it.currSpeed.set(currSpeed.get(), forced = true)
|
||||
it.deltaRotation.set(deltaRotation.get(), forced = true)
|
||||
it.drifting.set(drifting.get(), forced = true)
|
||||
it.poweredDrifting.set(poweredDrifting.get(), forced = true)
|
||||
it.turnMomentum.set(turnMomentum.get(), forced = true)
|
||||
it.inAir.set(inAir.get(), forced = true)
|
||||
}
|
||||
|
||||
override fun shouldSync(previous: RidingBehaviourState): Boolean {
|
||||
if (previous !is VehicleState) return false
|
||||
if (previous.drifting.get() != drifting.get()) return true
|
||||
if (previous.poweredDrifting.get() != poweredDrifting.get()) return true
|
||||
if (previous.turnMomentum.get() != turnMomentum.get()) return true
|
||||
if (previous.inAir.get() != inAir.get()) return true
|
||||
return super.shouldSync(previous)
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -354,6 +354,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
|
||||
class BoatSettings : RidingBehaviourSettings {
|
||||
override val key = BoatBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
var terminalVelocity = "-2.0".asExpression()
|
||||
@@ -364,11 +365,13 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(terminalVelocity)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
terminalVelocity = buffer.readExpression()
|
||||
}
|
||||
|
||||
+6
@@ -15,10 +15,13 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.blockPositionsAsListRounded
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.readRidingStats
|
||||
import com.cobblemon.mod.common.util.writeRidingStats
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
@@ -236,6 +239,7 @@ class BurstBehaviour : RidingBehaviour<BurstSettings, BurstState> {
|
||||
|
||||
class BurstSettings : RidingBehaviourSettings {
|
||||
override val key = BurstBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var dashSpeed = 1F
|
||||
private set
|
||||
@@ -244,11 +248,13 @@ class BurstSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeFloat(dashSpeed)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
dashSpeed = buffer.readFloat()
|
||||
}
|
||||
|
||||
+55
-3
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.riding.behaviour.types.liquid
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.OrientationControllable
|
||||
@@ -16,9 +17,11 @@ import com.cobblemon.mod.common.api.riding.behaviour.*
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseOption
|
||||
import com.cobblemon.mod.common.api.riding.posing.PoseProvider
|
||||
import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.SmoothDouble
|
||||
@@ -29,7 +32,9 @@ import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
companion object {
|
||||
@@ -43,7 +48,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
}
|
||||
|
||||
val poseProvider = PoseProvider<DolphinSettings, DolphinState>(PoseType.FLOAT)
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.entityData.get(PokemonEntity.MOVING) })
|
||||
.with(PoseOption(PoseType.SWIM) { _, _, entity -> entity.deltaMovement.length() > 0.1 })
|
||||
|
||||
override fun isActive(settings: DolphinSettings, state: DolphinState, vehicle: PokemonEntity): Boolean {
|
||||
return Shapes.create(vehicle.boundingBox).blockPositionsAsListRounded().any {
|
||||
@@ -161,11 +166,49 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
deltaTime: Double
|
||||
): Vec3 {
|
||||
if (driver !is OrientationControllable) return Vec3.ZERO
|
||||
val controller = (driver as OrientationControllable).orientationController
|
||||
|
||||
//Might need to add the smoothing here for default.
|
||||
//TODO:hook up handling
|
||||
val handling = vehicle.runtime.resolveDouble(settings.handlingExpr)
|
||||
val topSpeed = vehicle.runtime.resolveDouble(settings.speedExpr)
|
||||
|
||||
//Interpret mouse input
|
||||
val smoothingSpeed = 4.0
|
||||
val invertRoll = if (Cobblemon.config.invertRoll) -1 else 1
|
||||
val invertPitch = if (Cobblemon.config.invertPitch) -1 else 1
|
||||
return Vec3(0.0, mouseY * invertPitch, mouseX * invertRoll)
|
||||
val mouseXc = (mouseX).coerceIn(-60.0, 60.0)
|
||||
val mouseYc = (mouseY).coerceIn(-60.0, 60.0)
|
||||
val xInput = mouseXSmoother.getNewDeltaValue(mouseXc * 0.1 * invertRoll, deltaTime * smoothingSpeed);
|
||||
val yInput = mouseYSmoother.getNewDeltaValue(mouseYc * 0.1 * invertPitch, deltaTime * smoothingSpeed);
|
||||
val rollRot = xInput
|
||||
val pitchRot = yInput
|
||||
|
||||
// Roll
|
||||
// if (abs(controller.pitch + rollRot) >= 89.5 ) {
|
||||
// mouseXSmoother.reset()
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
|
||||
controller.rotateRoll(rollRot.toFloat())
|
||||
|
||||
// Yaw
|
||||
// TODO:move all magic numbers to handling expression
|
||||
val yawSpeed = handling * deltaTime * 0.25
|
||||
val yawForce = sin(controller.roll.toRadians()) * yawSpeed * ( 1.0 - min(sqrt(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())
|
||||
|
||||
// Pitch
|
||||
if (abs(controller.pitch + pitchRot) >= 89.5 ) {
|
||||
mouseYSmoother.reset()
|
||||
} else {
|
||||
//controller.applyGlobalPitch(pitchRot.toFloat() * -1.0f)
|
||||
controller.rotatePitch(pitchRot.toFloat())
|
||||
}
|
||||
|
||||
//yaw, pitch, roll
|
||||
return Vec3.ZERO
|
||||
}
|
||||
|
||||
override fun canJump(
|
||||
@@ -276,6 +319,7 @@ class DolphinBehaviour : RidingBehaviour<DolphinSettings, DolphinState> {
|
||||
|
||||
class DolphinSettings : RidingBehaviourSettings {
|
||||
override val key = DolphinBehaviour.KEY
|
||||
override val stats = mutableMapOf<RidingStat, IntRange>()
|
||||
|
||||
var canJump = "true".asExpression()
|
||||
private set
|
||||
@@ -295,10 +339,17 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
var strafeFactor = "0.2".asExpression()
|
||||
private set
|
||||
|
||||
var jumpExpr: Expression = "q.get_ride_stats('JUMP', 'AIR', 200.0, 128.0)".asExpression()
|
||||
var handlingExpr: Expression = "q.get_ride_stats('SKILL', 'AIR', 135.0, 45.0)".asExpression()
|
||||
var speedExpr: Expression = "q.get_ride_stats('SPEED', 'AIR', 1.0, 0.35)".asExpression()
|
||||
var accelerationExpr: Expression = "q.get_ride_stats('ACCELERATION', 'AIR', (1.0 / (20.0 * 3.0)), (1.0 / (20.0 * 8.0)))".asExpression()
|
||||
var staminaExpr: Expression = "q.get_ride_stats('STAMINA', 'AIR', 120.0, 20.0)".asExpression()
|
||||
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
buffer.writeRidingStats(stats)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(canJump)
|
||||
buffer.writeExpression(jumpVector[0])
|
||||
@@ -311,6 +362,7 @@ class DolphinSettings : RidingBehaviourSettings {
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
stats.putAll(buffer.readRidingStats())
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
canJump = buffer.readExpression()
|
||||
jumpVector = listOf(
|
||||
|
||||
@@ -50,7 +50,7 @@ class RideLoopSound(val ride: PokemonEntity, val soundSettings: RideSoundSetting
|
||||
this.looping = true
|
||||
this.delay = 0
|
||||
this.volume = 0.1f
|
||||
this.attenuation = SoundInstance.Attenuation.NONE // This class performs its own attenuation
|
||||
this.attenuation = SoundInstance.Attenuation.LINEAR // This class performs its own attenuation
|
||||
if (this.isPassenger) this.relative = true
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -63,9 +63,9 @@ class RideSoundManager(
|
||||
}
|
||||
|
||||
fun getActiveRideKey(): ResourceLocation {
|
||||
val key = ride.ridingBehaviourSettings?.key ?: cobblemonResource("no_key")
|
||||
val key = ride.ridingController?.context?.settings?.key ?: cobblemonResource("no_key")
|
||||
if (key == CompositeBehaviour.KEY) {
|
||||
return (ride.ridingState as? CompositeState)?.activeBehaviour?.get() ?: cobblemonResource("no_key")
|
||||
return (ride.ridingController?.context?.state as? CompositeState)?.activeBehaviour?.get() ?: cobblemonResource("no_key")
|
||||
} else {
|
||||
return key
|
||||
}
|
||||
|
||||
+10
@@ -16,6 +16,7 @@ import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Matrix3f
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.sign
|
||||
|
||||
/**
|
||||
* Class to handle the updating and containment of ride data used in animation.
|
||||
@@ -33,6 +34,7 @@ class RidingAnimationData(val ride: PokemonEntity)
|
||||
var rotSpring: Vec3Spring = Vec3Spring()
|
||||
var rotDeltaSpring: Vec3Spring = Vec3Spring()
|
||||
var localVelocitySpring: Vec3Spring = Vec3Spring()
|
||||
var driverInputSpring: Vec3Spring = Vec3Spring()
|
||||
|
||||
fun update() {
|
||||
val activeRide = ride.hasControllingPassenger()
|
||||
@@ -136,6 +138,14 @@ class RidingAnimationData(val ride: PokemonEntity)
|
||||
/******************************************************
|
||||
* Update springs
|
||||
*****************************************************/
|
||||
val driver = ride.controllingPassenger
|
||||
val vertInput = when {
|
||||
driver != null && driver.jumping-> 1.0
|
||||
driver != null && driver.isShiftKeyDown -> -1.0
|
||||
else -> 0.0
|
||||
}
|
||||
val input = if(driver != null) Vec3(driver.xxa.toDouble() * -1, vertInput, driver.zza.toDouble()) else Vec3.ZERO
|
||||
driverInputSpring.update(input, stiffness * 2, damping)
|
||||
rotDeltaSpring.update(angDelta, stiffness, damping)
|
||||
velocitySpring.update(currentVelocity, stiffness, damping)
|
||||
rotSpring.update(currRot, stiffness, damping)
|
||||
|
||||
+6
-4
@@ -44,10 +44,12 @@ class FishingSpawningCondition: SpawningCondition<FishingSpawnablePosition>() {
|
||||
|
||||
if (minLureLevel != null) { // check for the lureLevel of the rod
|
||||
val pokerodStack = spawnablePosition.rodStack
|
||||
val lureLevel = EnchantmentHelper.getItemEnchantmentLevel(
|
||||
spawnablePosition.enchantmentRegistry.getHolder(Enchantments.LURE).get(),
|
||||
pokerodStack
|
||||
)
|
||||
val lureLevel = spawnablePosition.enchantmentRegistry.getHolder(Enchantments.LURE).map {
|
||||
EnchantmentHelper.getItemEnchantmentLevel(
|
||||
it,
|
||||
pokerodStack
|
||||
)
|
||||
}.orElse(0)
|
||||
if (lureLevel < minLureLevel!!) {
|
||||
return false
|
||||
} else if (maxLureLevel != null && lureLevel > maxLureLevel!!) {
|
||||
|
||||
+4
-4
@@ -29,13 +29,13 @@ import net.minecraft.world.entity.Entity
|
||||
* @author Hiroku, Plastered_Crab
|
||||
* @since March 18th, 2025
|
||||
*/
|
||||
class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (time: Int) -> Unit = {}) : SpawningInfluence {
|
||||
open class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (time: Int, entity: PokemonEntity?) -> Unit = { _, _ -> }) : SpawningInfluence {
|
||||
|
||||
var usedTimes = 0
|
||||
|
||||
private fun markUsed() {
|
||||
private fun markUsed(entity: PokemonEntity? = null) {
|
||||
usedTimes++
|
||||
onUsed(usedTimes)
|
||||
onUsed(usedTimes, entity)
|
||||
}
|
||||
|
||||
override fun affectSpawn(action: SpawnAction<*>, entity: Entity) {
|
||||
@@ -43,7 +43,7 @@ class SpawnBaitInfluence(val effects: List<SpawnBait.Effect>, val onUsed: (time:
|
||||
val merged = SpawnBaitUtils.mergeEffects(effects)
|
||||
merged.forEach { effect ->
|
||||
if (Math.random() <= effect.chance) {
|
||||
markUsed()
|
||||
markUsed(entity)
|
||||
Effects.getEffectFunction(effect.type)?.invoke(entity, effect)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.IncenseSweetDetector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.LureCakeDetector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.LurePokeSnackDetector
|
||||
import com.cobblemon.mod.common.api.spawning.prospecting.SaccharineLogSlatheredDetector
|
||||
|
||||
/**
|
||||
@@ -31,7 +31,7 @@ interface SpawningInfluenceDetector {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
val detectors = mutableSetOf<SpawningInfluenceDetector>(
|
||||
LureCakeDetector,
|
||||
LurePokeSnackDetector,
|
||||
SaccharineLogSlatheredDetector,
|
||||
IncenseSweetDetector
|
||||
)
|
||||
|
||||
+13
-12
@@ -37,18 +37,19 @@ object IncenseSweetDetector : SpawningInfluenceDetector {
|
||||
val searchRange = RANGE + ceil(sqrt(((input.length pow 2) + (input.width pow 2)).toDouble())).toInt()
|
||||
val centerPos = input.getCenter().toBlockPos()
|
||||
|
||||
val sweetIncensePositions = world.poiManager.findAll(
|
||||
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.INCENSE_SWEET_KEY) },
|
||||
{ true },
|
||||
centerPos,
|
||||
searchRange,
|
||||
PoiManager.Occupancy.ANY
|
||||
).toList()
|
||||
|
||||
for (pos in sweetIncensePositions) {
|
||||
val influence = SpatialSpawningZoneInfluence(pos, radius = RANGE.toFloat(), IncenseSweetInfluence(pos))
|
||||
listOfInfluences.add(influence)
|
||||
}
|
||||
// TODO after 1.7
|
||||
// val sweetIncensePositions = world.poiManager.findAll(
|
||||
// { holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.INCENSE_SWEET_KEY) },
|
||||
// { true },
|
||||
// centerPos,
|
||||
// searchRange,
|
||||
// PoiManager.Occupancy.ANY
|
||||
// ).toList()
|
||||
//
|
||||
// for (pos in sweetIncensePositions) {
|
||||
// val influence = SpatialSpawningZoneInfluence(pos, radius = RANGE.toFloat(), IncenseSweetInfluence(pos))
|
||||
// listOfInfluences.add(influence)
|
||||
// }
|
||||
|
||||
return listOfInfluences
|
||||
}
|
||||
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.prospecting
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpatialSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.UnconditionalSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.detector.SpawningInfluenceDetector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
import com.cobblemon.mod.common.block.LureCakeBlock
|
||||
import com.cobblemon.mod.common.block.entity.LureCakeBlockEntity
|
||||
import com.cobblemon.mod.common.util.math.pow
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
object LureCakeDetector : SpawningInfluenceDetector {
|
||||
@JvmField
|
||||
val RANGE: Int = 48
|
||||
|
||||
override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): MutableList<SpawningZoneInfluence> {
|
||||
val world = input.world
|
||||
val listOfInfluences = mutableListOf<SpawningZoneInfluence>()
|
||||
|
||||
val searchRange = RANGE + ceil(sqrt(((input.length pow 2) + (input.width pow 2)).toDouble())).toInt()
|
||||
val lureCakePositions = world.poiManager.findAll(
|
||||
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.LURE_CAKE_KEY) },
|
||||
{ true },
|
||||
input.getCenter().toBlockPos(),
|
||||
searchRange,
|
||||
PoiManager.Occupancy.ANY
|
||||
).toList()
|
||||
|
||||
var highestLureTier = 0
|
||||
|
||||
for (lureCakePos in lureCakePositions) {
|
||||
val blockState = world.getBlockState(lureCakePos)
|
||||
if (blockState.block !is LureCakeBlock) continue
|
||||
|
||||
val blockEntity = world.getBlockEntity(lureCakePos) as? LureCakeBlockEntity ?: continue
|
||||
val baitEffects = blockEntity.getBaitEffectsFromLureCake()
|
||||
|
||||
highestLureTier = maxOf(highestLureTier, baitEffects.filter { it.type == SpawnBait.Effects.RARITY_BUCKET }.maxOfOrNull { it.value }?.toInt() ?: 0)
|
||||
|
||||
listOfInfluences.add(
|
||||
SpatialSpawningZoneInfluence(
|
||||
pos = lureCakePos,
|
||||
radius = RANGE.toFloat(),
|
||||
influence = SpawnBaitInfluence(
|
||||
effects = baitEffects,
|
||||
onUsed = { time ->
|
||||
if (time == 1) {
|
||||
tryBiteCake(world, lureCakePos)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (highestLureTier > 0) {
|
||||
listOfInfluences.add(UnconditionalSpawningZoneInfluence(influence = BucketNormalizingInfluence(tier = highestLureTier)))
|
||||
}
|
||||
|
||||
return listOfInfluences
|
||||
}
|
||||
|
||||
private fun tryBiteCake(world: ServerLevel, lureCakePos: BlockPos) {
|
||||
if (world.isLoaded(lureCakePos)) {
|
||||
val blockEntity = world.getBlockEntity(lureCakePos) as? LureCakeBlockEntity ?: return
|
||||
blockEntity.bites++
|
||||
|
||||
if (blockEntity.bites >= blockEntity.maxBites) {
|
||||
world.removeBlock(lureCakePos, false)
|
||||
} else {
|
||||
blockEntity.setChanged()
|
||||
world.sendBlockUpdated(lureCakePos, blockEntity.blockState, blockEntity.blockState, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun detectFromBlock(
|
||||
world: ServerLevel,
|
||||
pos: BlockPos,
|
||||
blockState: BlockState
|
||||
) = emptyList<SpawningZoneInfluence>()
|
||||
}
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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.spawning.prospecting
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonPoiTypes
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpatialSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.UnconditionalSpawningZoneInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.detector.SpawningInfluenceDetector
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.SpawningZoneInput
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import com.cobblemon.mod.common.util.math.pow
|
||||
import com.cobblemon.mod.common.util.toBlockPos
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiManager
|
||||
import net.minecraft.world.entity.ai.village.poi.PoiType
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.abs
|
||||
|
||||
object LurePokeSnackDetector : SpawningInfluenceDetector {
|
||||
|
||||
@JvmField
|
||||
val RANGE: Int = 48
|
||||
|
||||
@JvmField
|
||||
val POKE_SNACK_CRUMBED_ASPECT = "poke_snack_crumbed"
|
||||
|
||||
override fun detectFromInput(spawner: Spawner, input: SpawningZoneInput): MutableList<SpawningZoneInfluence> {
|
||||
val world = input.world
|
||||
val listOfInfluences = mutableListOf<SpawningZoneInfluence>()
|
||||
|
||||
val searchRange = RANGE + ceil(sqrt(((input.length pow 2) + (input.width pow 2)).toDouble())).toInt()
|
||||
val lurePokeSnackPositions = world.poiManager.findAll(
|
||||
{ holder: Holder<PoiType> -> holder.`is`(CobblemonPoiTypes.POKE_SNACK_KEY) },
|
||||
{ true },
|
||||
input.getCenter().toBlockPos(),
|
||||
searchRange,
|
||||
PoiManager.Occupancy.ANY
|
||||
).toList()
|
||||
|
||||
var highestLureTier = 0
|
||||
|
||||
for (lurePokeSnackPos in lurePokeSnackPositions) {
|
||||
val blockState = world.getBlockState(lurePokeSnackPos)
|
||||
if (blockState.block !is PokeSnackBlock) continue
|
||||
|
||||
val blockEntity = world.getBlockEntity(lurePokeSnackPos) as? PokeSnackBlockEntity ?: continue
|
||||
val baitEffects = blockEntity.getBaitEffects()
|
||||
|
||||
highestLureTier = maxOf(highestLureTier, baitEffects.filter { it.type == SpawnBait.Effects.RARITY_BUCKET }.maxOfOrNull { it.value }?.toInt() ?: 0)
|
||||
|
||||
listOfInfluences.add(
|
||||
SpatialSpawningZoneInfluence(
|
||||
pos = lurePokeSnackPos,
|
||||
radius = RANGE.toFloat(),
|
||||
influence = SpawnBaitInfluence(
|
||||
effects = baitEffects,
|
||||
onUsed = { usedAmount, entity ->
|
||||
entity?.let {
|
||||
// Prevent triggering twice on same entity
|
||||
if (usedAmount % 2 == 0) {
|
||||
tryBite(world, lurePokeSnackPos)
|
||||
moveAndApplyAspect(world, lurePokeSnackPos, entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (highestLureTier > 0) {
|
||||
listOfInfluences.add(UnconditionalSpawningZoneInfluence(influence = BucketNormalizingInfluence(tier = highestLureTier)))
|
||||
}
|
||||
|
||||
return listOfInfluences
|
||||
}
|
||||
|
||||
private fun tryBite(level: ServerLevel, blockPos: BlockPos) {
|
||||
if (level.isLoaded(blockPos)) {
|
||||
val blockState = level.getBlockState(blockPos)
|
||||
val block = blockState.block as? PokeSnackBlock ?: return
|
||||
|
||||
block.eat(level, blockPos, blockState, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveAndApplyAspect(level: ServerLevel, blockPos: BlockPos, entity: PokemonEntity?) {
|
||||
entity?.let {
|
||||
entity.pokemon.forcedAspects += POKE_SNACK_CRUMBED_ASPECT
|
||||
attemptSafeMove(level, blockPos, entity)
|
||||
}
|
||||
}
|
||||
|
||||
fun attemptSafeMove2(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity, ) {
|
||||
val inWater = entity.isInWater
|
||||
|
||||
// Random offset in a cardinal direction (north, south, east, west)
|
||||
val randomDirection = listOf(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST).random()
|
||||
val initialOffsetPos = startPos.relative(randomDirection)
|
||||
|
||||
// Variables to track the best (closest) safe position
|
||||
var safePos: BlockPos? = null
|
||||
var bestDistanceSq = Double.MAX_VALUE
|
||||
|
||||
val maxRadius = 10
|
||||
|
||||
// Search within a sphere around the initial offset position
|
||||
for (dx in -maxRadius..maxRadius) {
|
||||
for (dy in -maxRadius..maxRadius) {
|
||||
for (dz in -maxRadius..maxRadius) {
|
||||
val candidatePos = initialOffsetPos.offset(dx, dy, dz)
|
||||
val distanceSq = startPos.distSqr(candidatePos)
|
||||
|
||||
// Check if within sphere radius
|
||||
if (dx*dx + dy*dy + dz*dz <= maxRadius*maxRadius) {
|
||||
// Check collision at candidate position
|
||||
val currentBox = entity.boundingBox.move(
|
||||
candidatePos.x - entity.x,
|
||||
candidatePos.y - entity.y,
|
||||
candidatePos.z - entity.z
|
||||
)
|
||||
|
||||
if (level.noCollision(entity, currentBox)) {
|
||||
val blockBelowPos = candidatePos.below()
|
||||
val blockState = level.getBlockState(blockBelowPos)
|
||||
|
||||
val isWater = blockState.block == Blocks.WATER
|
||||
if (inWater) {
|
||||
if (isWater) {
|
||||
if (distanceSq < bestDistanceSq) {
|
||||
bestDistanceSq = distanceSq
|
||||
safePos = candidatePos
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!blockState.isAir) {
|
||||
if (distanceSq < bestDistanceSq) {
|
||||
bestDistanceSq = distanceSq
|
||||
safePos = candidatePos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (safePos != null) {
|
||||
entity.moveTo(safePos.toVec3d())
|
||||
PokeSnackBlockParticlesPacket(startPos, safePos).sendToPlayersAround(
|
||||
startPos.x.toDouble(),
|
||||
startPos.y.toDouble(),
|
||||
startPos.z.toDouble(),
|
||||
64.0,
|
||||
level.dimension()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun attemptSafeMove(level: ServerLevel, startPos: BlockPos, entity: PokemonEntity) {
|
||||
val inWater = entity.isInWater
|
||||
val maxDistance = 10 // maximum search radius
|
||||
val maxPositions = 10 // maximum number of positions at the closest distance
|
||||
|
||||
var foundPositions: List<BlockPos> = emptyList()
|
||||
|
||||
// Search layer by layer, starting from the closest
|
||||
for (distance in 1..maxDistance) {
|
||||
val positionsAtThisDistance = mutableListOf<BlockPos>()
|
||||
|
||||
for (dx in -distance..distance) {
|
||||
for (dy in -distance..distance) {
|
||||
for (dz in -distance..distance) {
|
||||
// Consider only positions on the surface of the cube at current distance
|
||||
if (listOf(dx, dy, dz).count { abs(it) != 0 } == distance) {
|
||||
val candidatePos = startPos.offset(dx, dy, dz)
|
||||
if (!candidatePos.equals(startPos.relative(Direction.UP))) {
|
||||
// Check collision
|
||||
val currentBox = entity.boundingBox.move(
|
||||
candidatePos.x - entity.x,
|
||||
candidatePos.y - entity.y,
|
||||
candidatePos.z - entity.z
|
||||
)
|
||||
|
||||
if (level.noCollision(entity, currentBox)) {
|
||||
val blockBelowPos = candidatePos.below()
|
||||
val blockState = level.getBlockState(blockBelowPos)
|
||||
|
||||
val isWater = blockState.block == Blocks.WATER
|
||||
if (inWater) {
|
||||
if (isWater) {
|
||||
positionsAtThisDistance.add(candidatePos)
|
||||
}
|
||||
} else {
|
||||
if (!blockState.isAir) {
|
||||
positionsAtThisDistance.add(candidatePos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (positionsAtThisDistance.isNotEmpty()) {
|
||||
// Limit to 10 nearest positions (all at this distance)
|
||||
foundPositions = if (positionsAtThisDistance.size > maxPositions) {
|
||||
positionsAtThisDistance.shuffled().take(maxPositions)
|
||||
} else {
|
||||
positionsAtThisDistance
|
||||
}
|
||||
break // Found the nearest layer with safe positions, stop expanding
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPositions.isNotEmpty()) {
|
||||
val chosenPos = foundPositions.random()
|
||||
val centerX = chosenPos.x + 0.5
|
||||
val centerZ = chosenPos.z + 0.5
|
||||
entity.moveTo(Vec3(centerX, chosenPos.y.toDouble(), centerZ))
|
||||
|
||||
PokeSnackBlockParticlesPacket(startPos, chosenPos).sendToPlayersAround(
|
||||
startPos.x.toDouble(),
|
||||
startPos.y.toDouble(),
|
||||
startPos.z.toDouble(),
|
||||
64.0,
|
||||
level.dimension()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun detectFromBlock(level: ServerLevel, pos: BlockPos, blockState: BlockState) = emptyList<SpawningZoneInfluence>()
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.Collections
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
@@ -259,9 +258,15 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
BattlePokemon.playerOwned(it)
|
||||
}.also { if (healPokemon) it.effectedPokemon.heal() }
|
||||
}.toMutableList()
|
||||
if (leadingPokemon != null) {
|
||||
Collections.rotate(result, result.size - this.indexOfFirst { it.uuid == leadingPokemon })
|
||||
|
||||
// reposition lead to front of the party
|
||||
if (leadingPokemon != null && result.first().uuid != leadingPokemon) {
|
||||
result.find { it.uuid == leadingPokemon }?.let { lead ->
|
||||
result.remove(lead)
|
||||
result.add(0, lead)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
fun clearParty() {
|
||||
|
||||
@@ -38,6 +38,8 @@ class Search(
|
||||
filter = filter.substring(1)
|
||||
}
|
||||
|
||||
filter = filter.lowercase(Locale.ROOT)
|
||||
|
||||
val pokemonFilter: PokemonFilter = when (filter) {
|
||||
"holding", "helditem", "held_item" -> PokemonFilter { pokemon -> !pokemon.heldItem().isEmpty }
|
||||
"fainted" -> PokemonFilter { pokemon -> pokemon.isFainted() }
|
||||
@@ -45,14 +47,16 @@ class Search(
|
||||
"mythical" -> PokemonFilter { pokemon -> pokemon.isMythical() }
|
||||
"ultrabeast", "ultra_beast" -> PokemonFilter { pokemon -> pokemon.isUltraBeast() }
|
||||
else -> {
|
||||
val nameFilter = filter.lowercase(Locale.ROOT)
|
||||
PokemonFilter { pokemon ->
|
||||
// search by partial name
|
||||
if (pokemon.species.resourceIdentifier.path.contains(nameFilter)) {
|
||||
return@PokemonFilter true
|
||||
if (filter.isEmpty()) {
|
||||
true
|
||||
} else {
|
||||
val species = pokemon.species.translatedName.string.lowercase(Locale.ROOT)
|
||||
val name = pokemon.getDisplayName().string.lowercase(Locale.ROOT)
|
||||
val props = PokemonProperties.parse(filter)
|
||||
|
||||
species.contains(filter) || name.contains(filter) || (props.matches(pokemon) && props.asString().isNotEmpty())
|
||||
}
|
||||
// search by exactly match in different props
|
||||
return@PokemonFilter PokemonProperties.parse(filter).matches(pokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.PrioritizedList
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
@@ -40,17 +39,13 @@ object BagItems : DataRegistry {
|
||||
val bagItems = PrioritizedList<BagItemLike>()
|
||||
internal val bagItemsScripts = mutableMapOf<String, String>() // itemId to JavaScript
|
||||
|
||||
init {
|
||||
observable.subscribe {
|
||||
Cobblemon.showdownThread.queue(ShowdownService::registerBagItems)
|
||||
}
|
||||
}
|
||||
|
||||
fun getConvertibleForStack(stack: ItemStack): BagItemLike? {
|
||||
return bagItems.firstOrNull { it.getBagItem(stack) != null }
|
||||
}
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
this.bagItemsScripts.clear()
|
||||
ShowdownService.service.resetRegistryData("bagItem")
|
||||
manager.listResources("bag_items") { it.path.endsWith(".js") }.forEach { (identifier, resource) ->
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
@@ -60,6 +55,8 @@ object BagItems : DataRegistry {
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowdownService.service.sendRegistryData(bagItemsScripts, "bagItem")
|
||||
|
||||
this.observable.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,8 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
|
||||
// No PP or disabled or something
|
||||
return false
|
||||
}
|
||||
val availableTargets = (gimmickMove?.target ?: move.target).targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
val availableTargets = (if (gimmickID != null && validGimmickMove) gimmickMove.target else move.target)
|
||||
.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
|
||||
|
||||
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
|
||||
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
|
||||
|
||||
@@ -16,6 +16,9 @@ import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
* @author Segfault Guy
|
||||
* @since April 10th, 2023
|
||||
*/
|
||||
@Deprecated(message =
|
||||
"This was designed before Doubles and Multi battle formats were introduce and has not been maintained since." +
|
||||
"Accurate context for formats other than Singles is not guaranteed. This will be replaced in the near future.")
|
||||
class ContextManager {
|
||||
|
||||
/** [BattleContext.Type] buckets containing [BattleContexts] of that type. */
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.battles.runner
|
||||
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.battles.runner.graal.GraalShowdownService
|
||||
import com.cobblemon.mod.common.battles.runner.socket.SocketShowdownService
|
||||
import com.google.gson.JsonArray
|
||||
import java.util.UUID
|
||||
|
||||
@@ -27,14 +28,13 @@ interface ShowdownService {
|
||||
fun closeConnection()
|
||||
fun startBattle(battle: PokemonBattle, messages: Array<String>)
|
||||
fun send(battleId: UUID, messages: Array<String>)
|
||||
fun getAbilityIds(): JsonArray
|
||||
fun getMoves(): JsonArray
|
||||
fun getItemIds(): JsonArray
|
||||
fun registerSpecies()
|
||||
fun registerBagItems()
|
||||
fun getRegistryData(type: String): JsonArray
|
||||
fun sendRegistryData(data: Map<String, String>, type: String)
|
||||
fun sendRegistryEntry(data: String, type: String)
|
||||
fun resetRegistryData(type: String)
|
||||
fun resetAllRegistries()
|
||||
fun indicateSpeciesInitialized() {}
|
||||
|
||||
|
||||
companion object {
|
||||
val service: ShowdownService by lazy { GraalShowdownService() }
|
||||
}
|
||||
|
||||
+27
-39
@@ -10,8 +10,6 @@ package com.cobblemon.mod.common.battles.runner.graal
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.google.gson.Gson
|
||||
@@ -33,6 +31,7 @@ import org.graalvm.polyglot.HostAccess.Export
|
||||
import org.graalvm.polyglot.PolyglotAccess
|
||||
import org.graalvm.polyglot.Value
|
||||
import org.graalvm.polyglot.io.FileSystem
|
||||
import kotlin.text.replace
|
||||
|
||||
/**
|
||||
* Mediator service for communicating between the Cobblemon Minecraft mod and Cobblemon showdown service via
|
||||
@@ -134,49 +133,38 @@ class GraalShowdownService : ShowdownService {
|
||||
sendToShowdown(battleId, messages)
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
val getCobbledAbilityIdsFn = context.getBindings("js").getMember("getCobbledAbilityIds")
|
||||
val arrayResult = getCobbledAbilityIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
override fun getRegistryData(type: String): JsonArray {
|
||||
val func = context.getBindings("js").getMember("getData")
|
||||
val result = func.execute(type).asString()
|
||||
return gson.fromJson(result, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getMoves(): JsonArray {
|
||||
val getCobbledMovesFn = context.getBindings("js").getMember("getCobbledMoves")
|
||||
val arrayResult = getCobbledMovesFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getItemIds(): JsonArray {
|
||||
val getCobbledItemIdsFn = context.getBindings("js").getMember("getCobbledItemIds")
|
||||
val arrayResult = getCobbledItemIdsFn.execute().asString()
|
||||
return gson.fromJson(arrayResult, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
val receiveSpeciesDataFn = this.context.getBindings("js").getMember("receiveSpeciesData")
|
||||
val jsArray = this.context.eval("js", "new Array();")
|
||||
var index = 0L
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, null)))
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
jsArray.setArrayElement(index++, this.gson.toJson(PokemonSpecies.ShowdownSpecies(species, form)))
|
||||
}
|
||||
}
|
||||
override fun sendRegistryData(data: Map<String, String>, type: String) {
|
||||
val payload = data.entries.joinToString(prefix = "{", postfix = "}") { (k, v) ->
|
||||
val newV = v.replace(Regex("[\r\n]+"), " ")
|
||||
"\"$k\": $newV"
|
||||
}
|
||||
receiveSpeciesDataFn.execute(jsArray)
|
||||
val func = this.context.getBindings("js").getMember("receiveData")
|
||||
func.execute(payload, type)
|
||||
}
|
||||
|
||||
override fun sendRegistryEntry(data: String, type: String) {
|
||||
val payload = data.replace(Regex("[\r\n]+"), " ")
|
||||
val func = this.context.getBindings("js").getMember("receiveData")
|
||||
func.execute(payload, type)
|
||||
}
|
||||
|
||||
override fun resetRegistryData(type: String) {
|
||||
this.context.getBindings("js").getMember("resetData").execute(type)
|
||||
}
|
||||
|
||||
override fun resetAllRegistries() {
|
||||
this.context.getBindings("js").getMember("resetAll").execute()
|
||||
}
|
||||
|
||||
override fun indicateSpeciesInitialized() {
|
||||
val afterCobbledSpeciesInitFn = this.context.getBindings("js").getMember("afterCobbledSpeciesInit")
|
||||
afterCobbledSpeciesInitFn.execute()
|
||||
}
|
||||
|
||||
override fun registerBagItems() {
|
||||
val receiveBagItemDataFn = this.context.getBindings("js").getMember("receiveBagItemData")
|
||||
for ((itemId, js) in BagItems.bagItemsScripts) {
|
||||
receiveBagItemDataFn.execute(itemId, js.replace("\n", " "))
|
||||
}
|
||||
val func = this.context.getBindings("js").getMember("afterSpeciesInit")
|
||||
func.execute()
|
||||
}
|
||||
|
||||
private fun sendToShowdown(battleId: UUID, messages: Array<String>) {
|
||||
|
||||
+37
-48
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.battles.runner.socket
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.runner.ShowdownService
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
@@ -102,48 +101,44 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
ShowdownInterpreter.interpretMessage(battleId, message)
|
||||
}
|
||||
|
||||
override fun getAbilityIds(): JsonArray {
|
||||
writer.write(">getCobbledAbilityIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
override fun sendRegistryData(data: Map<String, String>, type: String) {
|
||||
data.forEach { (_, value) -> sendRegistryEntry(value, type) }
|
||||
// The code for sending bulk data is commented out below, because:
|
||||
// 1) while debugging it's useful to have individual entries, and socket is only used for debug atm
|
||||
// 2) the species JSONs are way too big to be handled as one line on the JS side (maybe both sides?)
|
||||
// this can eventually be remedied if socket is intended to be used for any actual servers/etc.
|
||||
// alternatively, you could get around this limitation by just having ONLY PokemonSpecies send individually,
|
||||
// but it's likely that with enough custom data for moves/abilities/etc. you'd run into the same issue
|
||||
|
||||
override fun getMoves(): JsonArray {
|
||||
writer.write(">getCobbledMoves\n")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun getItemIds(): JsonArray {
|
||||
writer.write(">getCobbledItemIds")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
private fun sendSpeciesData(species: Species, form: FormData?) {
|
||||
writer.write(">receiveSpeciesData ${gson.toJson(PokemonSpecies.ShowdownSpecies(species, form))}\n")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
private fun sendBagItem(itemId: String, js: String) {
|
||||
writer.write(">receiveBagItemData $itemId $js")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send bag item to Showdown: $itemId") }
|
||||
}
|
||||
|
||||
override fun registerSpecies() {
|
||||
writer.write(">resetSpeciesData\n")
|
||||
acknowledge()
|
||||
PokemonSpecies.species.forEach { species ->
|
||||
sendSpeciesData(species, null)
|
||||
species.forms.forEach { form ->
|
||||
if (form != species.standardForm) {
|
||||
sendSpeciesData(species, form)
|
||||
}
|
||||
}
|
||||
/*val payload = data.entries.joinToString(prefix = "{", postfix = "}") { (k, v) ->
|
||||
val newV = v.replace(Regex("[\r\n]+"), " ")
|
||||
"\"$k\": $newV"
|
||||
}
|
||||
writer.write(">receiveData $type payload")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send $type data to Showdown: $data") }*/
|
||||
}
|
||||
|
||||
override fun sendRegistryEntry(data: String, type: String) {
|
||||
val payload = data.replace(Regex("[\r\n]+"), " ")
|
||||
writer.write(">receiveEntry $type $payload")
|
||||
acknowledge { Cobblemon.LOGGER.error("Failed to send $type data to Showdown: $payload") }
|
||||
}
|
||||
|
||||
override fun getRegistryData(type: String): JsonArray {
|
||||
writer.write(">getData $type")
|
||||
writer.flush()
|
||||
val response = readMessage()
|
||||
return gson.fromJson(response, JsonArray::class.java)
|
||||
}
|
||||
|
||||
override fun resetRegistryData(type: String) {
|
||||
writer.write(">resetData $type")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
override fun resetAllRegistries() {
|
||||
writer.write(">resetAll")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
fun acknowledge(ifFails: () -> Unit = {}) {
|
||||
@@ -155,14 +150,8 @@ class SocketShowdownService(val host: String = "localhost", val port: Int = 1846
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerBagItems() {
|
||||
for ((itemId, js) in BagItems.bagItemsScripts) {
|
||||
sendBagItem(itemId, js.replace("\n", " "))
|
||||
}
|
||||
}
|
||||
|
||||
override fun indicateSpeciesInitialized() {
|
||||
writer.write(">afterCobbledSpeciesInit")
|
||||
writer.write(">afterSpeciesInit")
|
||||
acknowledge()
|
||||
}
|
||||
|
||||
|
||||
@@ -226,115 +226,99 @@ class ApricornBlock(settings: Properties, val apricorn: Apricorn) : HorizontalDi
|
||||
const val MIN_AGE = 0
|
||||
|
||||
// North
|
||||
// Stage 0
|
||||
private val NORTH_STAGE_0 = Shapes.box(0.40625, 0.5, 0.0, 0.59375, 0.6875, 0.1875)
|
||||
private val NORTH_CAP_STAGE_0 = Shapes.box(0.4375, 0.65625, 0.03125, 0.5625, 0.71875, 0.15625)
|
||||
|
||||
// Stage 1
|
||||
private val NORTH_TOP_STAGE_1 = Block.box(7.0, 11.0, 0.5, 9.0, 11.5, 2.5)
|
||||
private val NORTH_BODY_STAGE_1 = Block.box(6.5, 9.0, 0.0, 9.5, 11.0, 3.0)
|
||||
private val NORTH_BOTTOM_STAGE_1 = Block.box(7.0, 8.5, 0.5, 9.0, 9.0, 2.5)
|
||||
private val NORTH_STAGE_1 = Shapes.box(0.375, 0.40625, 0.0, 0.625, 0.65625, 0.25)
|
||||
private val NORTH_CAP_STAGE_1 = Shapes.box(0.40625, 0.625, 0.03125, 0.59375, 0.6875, 0.21875)
|
||||
|
||||
// Stage 2
|
||||
private val NORTH_TOP_STAGE_2 = Block.box(6.5, 10.5, 0.5, 9.5, 11.0, 3.5)
|
||||
private val NORTH_BODY_STAGE_2 = Block.box(6.0, 7.5, 0.0, 10.0, 10.5, 4.0)
|
||||
private val NORTH_BOTTOM_STAGE_2 = Block.box(6.5, 7.0, 0.5, 9.5, 7.5, 3.5)
|
||||
private val NORTH_STAGE_2 = Shapes.box(0.34375, 0.296875, 0.0, 0.65625, 0.609375, 0.3125)
|
||||
private val NORTH_CAP_STAGE_2 = Shapes.box(0.375, 0.59375, 0.03125, 0.625, 0.65625, 0.28125)
|
||||
|
||||
// Stage 3
|
||||
private val NORTH_TOP_STAGE_3 = Block.box(6.0, 9.75, 0.5, 10.0, 10.5, 4.5)
|
||||
private val NORTH_BODY_STAGE_3 = Block.box(5.5, 5.75, 0.0, 10.5, 9.75, 5.0)
|
||||
private val NORTH_BOTTOM_STAGE_3 = Block.box(6.0, 5.0, 0.5, 10.0, 5.75, 4.5)
|
||||
|
||||
// Full fruit
|
||||
private val NORTH_TOP_FRUIT = Block.box(6.0, 9.0, 1.0, 10.0, 10.0, 5.0)
|
||||
private val NORTH_BODY_FRUIT = Block.box(5.0, 4.0, 0.0, 11.0, 9.0, 6.0)
|
||||
private val NORTH_BOTTOM_FRUIT = Block.box(5.5, 3.0, 0.5, 10.5, 4.0, 5.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val NORTH_STAGE_3 = Shapes.box(0.3125, 0.1875, 0.0, 0.6875, 0.5625, 0.375)
|
||||
private val NORTH_CAP_STAGE_3 = Shapes.box(0.375, 0.5625, 0.0625, 0.625, 0.625, 0.3125)
|
||||
|
||||
private val NORTH_AABB = arrayOf(
|
||||
Shapes.or(NORTH_BODY_STAGE_1, NORTH_TOP_STAGE_1, NORTH_BOTTOM_STAGE_1),
|
||||
Shapes.or(NORTH_BODY_STAGE_2, NORTH_TOP_STAGE_2, NORTH_BOTTOM_STAGE_2),
|
||||
Shapes.or(NORTH_BODY_STAGE_3, NORTH_TOP_STAGE_3, NORTH_BOTTOM_STAGE_3),
|
||||
Shapes.or(NORTH_BODY_FRUIT, NORTH_TOP_FRUIT, NORTH_BOTTOM_FRUIT)
|
||||
Shapes.or(NORTH_STAGE_0, NORTH_CAP_STAGE_0),
|
||||
Shapes.or(NORTH_STAGE_1, NORTH_CAP_STAGE_1),
|
||||
Shapes.or(NORTH_STAGE_2, NORTH_CAP_STAGE_2),
|
||||
Shapes.or(NORTH_STAGE_3, NORTH_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
// South
|
||||
// Stage 0
|
||||
private val SOUTH_STAGE_0 = Shapes.box(0.40625, 0.5, 0.8125, 0.59375, 0.6875, 1.0)
|
||||
private val SOUTH_CAP_STAGE_0 = Shapes.box(0.4375, 0.65625, 0.84375, 0.5625, 0.71875, 0.96875)
|
||||
|
||||
// Stage 1
|
||||
private val SOUTH_TOP_STAGE_1 = Block.box(7.0, 11.0, 13.5, 9.0, 11.5, 15.5)
|
||||
private val SOUTH_BODY_STAGE_1 = Block.box(6.5, 9.0, 13.0, 9.5, 11.0, 16.0)
|
||||
private val SOUTH_BOTTOM_STAGE_1 = Block.box(7.0, 8.5, 13.5, 9.0, 9.0, 15.5)
|
||||
private val SOUTH_STAGE_1 = Shapes.box(0.375, 0.40625, 0.75, 0.625, 0.65625, 1.0)
|
||||
private val SOUTH_CAP_STAGE_1 = Shapes.box(0.40625, 0.625, 0.78125, 0.59375, 0.6875, 0.96875)
|
||||
|
||||
// Stage 2
|
||||
private val SOUTH_TOP_STAGE_2 = Block.box(6.5, 10.5, 12.5, 9.5, 11.0, 15.5)
|
||||
private val SOUTH_BODY_STAGE_2 = Block.box(6.0, 7.5, 12.0, 10.0, 10.5, 16.0)
|
||||
private val SOUTH_BOTTOM_STAGE_2 = Block.box(6.5, 7.0, 12.5, 9.5, 7.5, 15.5)
|
||||
private val SOUTH_STAGE_2 = Shapes.box(0.34375, 0.296875, 0.6875, 0.65625, 0.609375, 1.0)
|
||||
private val SOUTH_CAP_STAGE_2 = Shapes.box(0.375, 0.59375, 0.71875, 0.625, 0.65625, 0.96875)
|
||||
|
||||
// Stage 3
|
||||
private val SOUTH_TOP_STAGE_3 = Block.box(6.0, 9.75, 11.5, 10.0, 10.5, 15.5)
|
||||
private val SOUTH_BODY_STAGE_3 = Block.box(5.5, 5.75, 11.0, 10.5, 9.75, 16.0)
|
||||
private val SOUTH_BOTTOM_STAGE_3 = Block.box(6.0, 5.0, 11.5, 10.0, 5.75, 15.5)
|
||||
|
||||
// Full fruit
|
||||
private val SOUTH_TOP_FRUIT = Block.box(6.0, 9.0, 11.0, 10.0, 10.0, 15.0)
|
||||
private val SOUTH_BODY_FRUIT = Block.box(5.0, 4.0, 10.0, 11.0, 9.0, 16.0)
|
||||
private val SOUTH_BOTTOM_FRUIT = Block.box(5.5, 3.0, 10.5, 10.5, 4.0, 15.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val SOUTH_STAGE_3 = Shapes.box(0.3125, 0.1875, 0.625, 0.6875, 0.5625, 1.0)
|
||||
private val SOUTH_CAP_STAGE_3 = Shapes.box(0.375, 0.5625, 0.6875, 0.625, 0.625, 0.9375)
|
||||
|
||||
private val SOUTH_AABB = arrayOf(
|
||||
Shapes.or(SOUTH_BODY_STAGE_1, SOUTH_TOP_STAGE_1, SOUTH_BOTTOM_STAGE_1),
|
||||
Shapes.or(SOUTH_BODY_STAGE_2, SOUTH_TOP_STAGE_2, SOUTH_BOTTOM_STAGE_2),
|
||||
Shapes.or(SOUTH_BODY_STAGE_3, SOUTH_TOP_STAGE_3, SOUTH_BOTTOM_STAGE_3),
|
||||
Shapes.or(SOUTH_BODY_FRUIT, SOUTH_TOP_FRUIT, SOUTH_BOTTOM_FRUIT)
|
||||
Shapes.or(SOUTH_STAGE_0, SOUTH_CAP_STAGE_0),
|
||||
Shapes.or(SOUTH_STAGE_1, SOUTH_CAP_STAGE_1),
|
||||
Shapes.or(SOUTH_STAGE_2, SOUTH_CAP_STAGE_2),
|
||||
Shapes.or(SOUTH_STAGE_3, SOUTH_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
// East
|
||||
// Stage 0
|
||||
private val EAST_STAGE_0 = Shapes.box(0.8125, 0.5, 0.40625, 1.0, 0.6875, 0.59375)
|
||||
private val EAST_CAP_STAGE_0 = Shapes.box(0.84375, 0.65625, 0.4375, 0.96875, 0.71875, 0.5625)
|
||||
|
||||
// Stage 1
|
||||
private val EAST_TOP_STAGE_1 = Block.box(13.5, 11.0, 7.0, 15.5, 11.5, 9.0)
|
||||
private val EAST_BODY_STAGE_1 = Block.box(13.0, 9.0, 6.5, 16.0, 11.0, 9.5)
|
||||
private val EAST_BOTTOM_STAGE_1 = Block.box(13.5, 8.5, 7.0, 15.5, 9.0, 9.0)
|
||||
private val EAST_STAGE_1 = Shapes.box(0.75, 0.40625, 0.375, 1.0, 0.65625, 0.625)
|
||||
private val EAST_CAP_STAGE_1 = Shapes.box(0.78125, 0.625, 0.40625, 0.96875, 0.6875, 0.59375)
|
||||
|
||||
// Stage 2
|
||||
private val EAST_TOP_STAGE_2 = Block.box(12.5, 10.5, 6.5, 15.5, 11.0, 9.5)
|
||||
private val EAST_BODY_STAGE_2 = Block.box(12.0, 7.5, 6.0, 16.0, 10.5, 10.0)
|
||||
private val EAST_BOTTOM_STAGE_2 = Block.box(12.5, 7.0, 6.5, 15.5, 7.5, 9.5)
|
||||
private val EAST_STAGE_2 = Shapes.box(0.6875, 0.296875, 0.34375, 1.0, 0.609375, 0.65625)
|
||||
private val EAST_CAP_STAGE_2 = Shapes.box(0.71875, 0.59375, 0.375, 0.96875, 0.65625, 0.625)
|
||||
|
||||
// Stage 3
|
||||
private val EAST_TOP_STAGE_3 = Block.box(11.5, 9.75, 6.0, 15.5, 10.5, 10.0)
|
||||
private val EAST_BODY_STAGE_3 = Block.box(11.0, 5.75, 5.5, 16.0, 9.75, 10.5)
|
||||
private val EAST_BOTTOM_STAGE_3 = Block.box(11.5, 5.0, 6.0, 15.5, 5.75, 10.0)
|
||||
|
||||
// Full fruit
|
||||
private val EAST_TOP_FRUIT = Block.box(11.0, 9.0, 6.0, 15.0, 10.0, 10.0)
|
||||
private val EAST_BODY_FRUIT = Block.box(10.0, 4.0, 5.0, 16.0, 9.0, 11.0)
|
||||
private val EAST_BOTTOM_FRUIT = Block.box(10.5, 3.0, 5.5, 15.5, 4.0, 10.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val EAST_STAGE_3 = Shapes.box(0.625, 0.1875, 0.3125, 1.0, 0.5625, 0.6875)
|
||||
private val EAST_CAP_STAGE_3 = Shapes.box(0.6875, 0.5625, 0.375, 0.9375, 0.625, 0.625)
|
||||
|
||||
private val EAST_AABB = arrayOf(
|
||||
Shapes.or(EAST_BODY_STAGE_1, EAST_TOP_STAGE_1, EAST_BOTTOM_STAGE_1),
|
||||
Shapes.or(EAST_BODY_STAGE_2, EAST_TOP_STAGE_2, EAST_BOTTOM_STAGE_2),
|
||||
Shapes.or(EAST_BODY_STAGE_3, EAST_TOP_STAGE_3, EAST_BOTTOM_STAGE_3),
|
||||
Shapes.or(EAST_BODY_FRUIT, EAST_TOP_FRUIT, EAST_BOTTOM_FRUIT)
|
||||
Shapes.or(EAST_STAGE_0, EAST_CAP_STAGE_0),
|
||||
Shapes.or(EAST_STAGE_1, EAST_CAP_STAGE_1),
|
||||
Shapes.or(EAST_STAGE_2, EAST_CAP_STAGE_2),
|
||||
Shapes.or(EAST_STAGE_3, EAST_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
// West
|
||||
// Stage 0
|
||||
private val WEST_STAGE_0 = Shapes.box(0.0, 0.5, 0.40625, 0.1875, 0.6875, 0.59375)
|
||||
private val WEST_CAP_STAGE_0 = Shapes.box(0.03125, 0.65625, 0.4375, 0.15625, 0.71875, 0.5625)
|
||||
|
||||
// Stage 1
|
||||
private val WEST_TOP_STAGE_1 = Block.box(0.5, 11.0, 7.0, 2.5, 11.5, 9.0)
|
||||
private val WEST_BODY_STAGE_1 = Block.box(0.0, 9.0, 6.5, 3.0, 11.0, 9.5)
|
||||
private val WEST_BOTTOM_STAGE_1 = Block.box(0.5, 8.5, 7.0, 2.5, 9.0, 9.0)
|
||||
private val WEST_STAGE_1 = Shapes.box(0.0, 0.40625, 0.375, 0.25, 0.65625, 0.625)
|
||||
private val WEST_CAP_STAGE_1 = Shapes.box(0.03125, 0.625, 0.40625, 0.21875, 0.6875, 0.59375)
|
||||
|
||||
// Stage 2
|
||||
private val WEST_TOP_STAGE_2 = Block.box(0.5, 10.5, 6.5, 3.5, 11.0, 9.5)
|
||||
private val WEST_BODY_STAGE_2 = Block.box(0.0, 7.5, 6.0, 4.0, 10.5, 10.0)
|
||||
private val WEST_BOTTOM_STAGE_2 = Block.box(0.5, 7.0, 6.5, 3.5, 7.5, 9.5)
|
||||
private val WEST_STAGE_2 = Shapes.box(0.0, 0.296875, 0.34375, 0.3125, 0.609375, 0.65625)
|
||||
private val WEST_CAP_STAGE_2 = Shapes.box(0.03125, 0.59375, 0.375, 0.28125, 0.65625, 0.625)
|
||||
|
||||
// Stage 3
|
||||
private val WEST_TOP_STAGE_3 = Block.box(0.5, 9.75, 6.0, 4.5, 10.5, 10.0)
|
||||
private val WEST_BODY_STAGE_3 = Block.box(0.0, 5.75, 5.5, 5.0, 9.75, 10.5)
|
||||
private val WEST_BOTTOM_STAGE_3 = Block.box(0.5, 5.0, 6.0, 4.5, 5.75, 10.0)
|
||||
|
||||
// Full fruit
|
||||
private val WEST_TOP_FRUIT = Block.box(1.0, 9.0, 6.0, 5.0, 10.0, 10.0)
|
||||
private val WEST_BODY_FRUIT = Block.box(0.0, 4.0, 5.0, 6.0, 9.0, 11.0)
|
||||
private val WEST_BOTTOM_FRUIT = Block.box(0.5, 3.0, 5.5, 5.5, 4.0, 10.5)
|
||||
// Stage 3 - Full Fruit
|
||||
private val WEST_STAGE_3 = Shapes.box(0.0, 0.1875, 0.3125, 0.375, 0.5625, 0.6875)
|
||||
private val WEST_CAP_STAGE_3 = Shapes.box(0.0625, 0.5625, 0.375, 0.3125, 0.625, 0.625)
|
||||
|
||||
private val WEST_AABB = arrayOf(
|
||||
Shapes.or(WEST_BODY_STAGE_1, WEST_TOP_STAGE_1, WEST_BOTTOM_STAGE_1),
|
||||
Shapes.or(WEST_BODY_STAGE_2, WEST_TOP_STAGE_2, WEST_BOTTOM_STAGE_2),
|
||||
Shapes.or(WEST_BODY_STAGE_3, WEST_TOP_STAGE_3, WEST_BOTTOM_STAGE_3),
|
||||
Shapes.or(WEST_BODY_FRUIT, WEST_TOP_FRUIT, WEST_BOTTOM_FRUIT)
|
||||
Shapes.or(WEST_STAGE_0, WEST_CAP_STAGE_0),
|
||||
Shapes.or(WEST_STAGE_1, WEST_CAP_STAGE_1),
|
||||
Shapes.or(WEST_STAGE_2, WEST_CAP_STAGE_2),
|
||||
Shapes.or(WEST_STAGE_3, WEST_CAP_STAGE_3)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ class BugwortBlock(settings: Properties) : CropBlock(settings), BonemealableBloc
|
||||
builder.add(IS_WILD)
|
||||
}
|
||||
|
||||
override fun getBaseSeedId(): ItemLike = CobblemonItems.BUGWORT
|
||||
// TODO after 1.7
|
||||
// override fun getBaseSeedId(): ItemLike = CobblemonItems.BUGWORT
|
||||
|
||||
override fun getShape(state: BlockState, world: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape = AGE_TO_SHAPE[this.getAge(state)]
|
||||
|
||||
@@ -93,26 +94,27 @@ class BugwortBlock(settings: Properties) : CropBlock(settings), BonemealableBloc
|
||||
return floor.`is`(BlockTags.DIRT) || floor.`is`(Blocks.FARMLAND)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
world: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hit: BlockHitResult
|
||||
): InteractionResult {
|
||||
if (state != null && world != null && pos != null) {
|
||||
// if bugwort is at max age then revert age to 0 and drop items
|
||||
if (player.getItemInHand(InteractionHand.MAIN_HAND).`is`(Items.BONE_MEAL) && !this.isMaxAge(state)) {
|
||||
return InteractionResult.PASS
|
||||
} else if (this.isMaxAge(state)) {
|
||||
world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundSource.BLOCKS, 0.4F, 1F)
|
||||
world.setBlock(pos, state.setValue(this.ageProperty, (0).coerceAtMost(this.maxAge)), UPDATE_CLIENTS)
|
||||
Block.popResource(world, pos, ItemStack(CobblemonItems.BUGWORT, Random.nextInt(2, 4)))
|
||||
return InteractionResult.sidedSuccess(world.isClientSide)
|
||||
}
|
||||
}
|
||||
return super.useWithoutItem(state, world, pos, player, hit)
|
||||
}
|
||||
// TODO after 1.7
|
||||
// override fun useWithoutItem(
|
||||
// state: BlockState,
|
||||
// world: Level,
|
||||
// pos: BlockPos,
|
||||
// player: Player,
|
||||
// hit: BlockHitResult
|
||||
// ): InteractionResult {
|
||||
// if (state != null && world != null && pos != null) {
|
||||
// // if bugwort is at max age then revert age to 0 and drop items
|
||||
// if (player.getItemInHand(InteractionHand.MAIN_HAND).`is`(Items.BONE_MEAL) && !this.isMaxAge(state)) {
|
||||
// return InteractionResult.PASS
|
||||
// } else if (this.isMaxAge(state)) {
|
||||
// world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundSource.BLOCKS, 0.4F, 1F)
|
||||
// world.setBlock(pos, state.setValue(this.ageProperty, (0).coerceAtMost(this.maxAge)), UPDATE_CLIENTS)
|
||||
// Block.popResource(world, pos, ItemStack(CobblemonItems.BUGWORT, Random.nextInt(2, 4)))
|
||||
// return InteractionResult.sidedSuccess(world.isClientSide)
|
||||
// }
|
||||
// }
|
||||
// return super.useWithoutItem(state, world, pos, player, hit)
|
||||
// }
|
||||
|
||||
override fun getBonemealAgeIncrease(world: Level): Int = 1
|
||||
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.Rotation
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
abstract class CakeBlock(settings: Properties): BaseEntityBlock(settings) {
|
||||
companion object {
|
||||
val FACING: DirectionProperty = DirectionProperty.create("facing", Direction.Plane.HORIZONTAL)
|
||||
|
||||
val SHAPE_BY_BITE = arrayOf(
|
||||
box(1.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(3.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(5.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(7.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(9.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(11.0, 0.0, 1.0, 15.0, 9.0, 15.0),
|
||||
box(13.0, 0.0, 1.0, 15.0, 9.0, 15.0)
|
||||
)
|
||||
|
||||
enum class Rotation {
|
||||
NONE,
|
||||
CLOCKWISE_90,
|
||||
CLOCKWISE_180,
|
||||
COUNTERCLOCKWISE_90
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH))
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
if (placer != null) {
|
||||
level.setBlock(pos, state.setValue(FACING, placer.direction.opposite), 2)
|
||||
}
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity ?: return ItemStack.EMPTY
|
||||
return blockEntity.toItemStack()
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape {
|
||||
val bites = getBites(level, pos)
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
val maxBites = blockEntity?.maxBites ?: 6
|
||||
|
||||
val visualStage = ((bites * SHAPE_BY_BITE.size) / (maxBites + 1))
|
||||
.coerceIn(0, SHAPE_BY_BITE.lastIndex)
|
||||
|
||||
val baseShape = SHAPE_BY_BITE[visualStage]
|
||||
|
||||
return when (state.getValue(FACING)) {
|
||||
Direction.NORTH -> baseShape
|
||||
Direction.SOUTH -> rotateShape(baseShape, Rotation.CLOCKWISE_180)
|
||||
Direction.WEST -> rotateShape(baseShape, Rotation.COUNTERCLOCKWISE_90)
|
||||
Direction.EAST -> rotateShape(baseShape, Rotation.CLOCKWISE_90)
|
||||
else -> baseShape
|
||||
}
|
||||
}
|
||||
|
||||
private fun rotateShape(shape: VoxelShape, rotation: Rotation): VoxelShape {
|
||||
var newShape = net.minecraft.world.phys.shapes.Shapes.empty()
|
||||
|
||||
for (box in shape.toAabbs()) {
|
||||
val minX = box.minX
|
||||
val minY = box.minY
|
||||
val minZ = box.minZ
|
||||
val maxX = box.maxX
|
||||
val maxY = box.maxY
|
||||
val maxZ = box.maxZ
|
||||
|
||||
val (rMinX, rMinZ, rMaxX, rMaxZ) = when (rotation) {
|
||||
Rotation.NONE -> arrayOf(minX, minZ, maxX, maxZ)
|
||||
Rotation.CLOCKWISE_90 -> arrayOf(minZ, 1.0 - maxX, maxZ, 1.0 - minX)
|
||||
Rotation.CLOCKWISE_180 -> arrayOf(1.0 - maxX, 1.0 - maxZ, 1.0 - minX, 1.0 - minZ)
|
||||
Rotation.COUNTERCLOCKWISE_90 -> arrayOf(1.0 - maxZ, minX, 1.0 - minZ, maxX)
|
||||
}
|
||||
|
||||
val rotatedVoxel = net.minecraft.world.phys.shapes.Shapes.box(rMinX, minY, rMinZ, rMaxX, maxY, rMaxZ)
|
||||
|
||||
newShape = net.minecraft.world.phys.shapes.Shapes.or(newShape, rotatedVoxel)
|
||||
}
|
||||
|
||||
return newShape
|
||||
}
|
||||
|
||||
override fun spawnDestroyParticles(
|
||||
level: Level,
|
||||
player: Player,
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) {
|
||||
val blockEntity = level.getBlockEntity(pos) as? CakeBlockEntity
|
||||
val color = blockEntity?.foodColourComponent?.colours?.lastOrNull()
|
||||
val woolBlock = when (color) {
|
||||
DyeColor.RED -> Blocks.RED_WOOL
|
||||
DyeColor.ORANGE -> Blocks.ORANGE_WOOL
|
||||
DyeColor.YELLOW -> Blocks.YELLOW_WOOL
|
||||
DyeColor.LIME -> Blocks.LIME_WOOL
|
||||
DyeColor.GREEN -> Blocks.GREEN_WOOL
|
||||
DyeColor.CYAN -> Blocks.CYAN_WOOL
|
||||
DyeColor.LIGHT_BLUE -> Blocks.LIGHT_BLUE_WOOL
|
||||
DyeColor.BLUE -> Blocks.BLUE_WOOL
|
||||
DyeColor.PURPLE -> Blocks.PURPLE_WOOL
|
||||
DyeColor.MAGENTA -> Blocks.MAGENTA_WOOL
|
||||
DyeColor.PINK -> Blocks.PINK_WOOL
|
||||
else -> Blocks.WHITE_WOOL
|
||||
}
|
||||
|
||||
level.levelEvent(player, 2001, pos, getId(woolBlock.defaultBlockState()))
|
||||
}
|
||||
|
||||
fun getFoodColourComponent(level: BlockGetter, pos: BlockPos): FoodColourComponent? {
|
||||
return (level.getBlockEntity(pos) as? CakeBlockEntity)?.foodColourComponent
|
||||
}
|
||||
|
||||
fun setFoodColourComponent(level: BlockGetter, pos: BlockPos, foodColourComponent: FoodColourComponent) {
|
||||
(level.getBlockEntity(pos) as? CakeBlockEntity)?.foodColourComponent = foodColourComponent
|
||||
}
|
||||
|
||||
fun getBites(level: BlockGetter, pos: BlockPos): Int = (level.getBlockEntity(pos) as? CakeBlockEntity)?.bites ?: 0
|
||||
fun setBites(level: BlockGetter, pos: BlockPos, bites: Int) {
|
||||
(level.getBlockEntity(pos) as? CakeBlockEntity)?.bites = bites
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.entity.CandlePokeCakeBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.block.CakeBlockEntityRenderer.Companion.TOP_LAYER_HEIGHT
|
||||
import com.cobblemon.mod.common.util.toEquipmentSlot
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
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.block.AbstractCandleBlock
|
||||
import net.minecraft.world.level.block.AbstractCandleBlock.extinguish
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks.*
|
||||
import net.minecraft.world.level.block.CandleBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class CandlePokeCakeBlock(settings: Properties) : CakeBlock(settings) {
|
||||
|
||||
companion object {
|
||||
val LIT = BlockStateProperties.LIT
|
||||
val CANDLE_COLOR = IntegerProperty.create("candle_color", 0, MapColor.MATERIAL_COLORS.size - 1)
|
||||
|
||||
fun getCandleByMapColorId(id: Int): CandleBlock {
|
||||
return when (id) {
|
||||
MapColor.SAND.id -> CANDLE
|
||||
MapColor.WOOL.id -> WHITE_CANDLE
|
||||
MapColor.COLOR_ORANGE.id -> ORANGE_CANDLE
|
||||
MapColor.COLOR_MAGENTA.id -> MAGENTA_CANDLE
|
||||
MapColor.COLOR_LIGHT_BLUE.id -> LIGHT_BLUE_CANDLE
|
||||
MapColor.COLOR_YELLOW.id -> YELLOW_CANDLE
|
||||
MapColor.COLOR_LIGHT_GREEN.id -> LIME_CANDLE
|
||||
MapColor.COLOR_PINK.id -> PINK_CANDLE
|
||||
MapColor.COLOR_GRAY.id -> GRAY_CANDLE
|
||||
MapColor.COLOR_LIGHT_GRAY.id -> LIGHT_GRAY_CANDLE
|
||||
MapColor.COLOR_CYAN.id -> CYAN_CANDLE
|
||||
MapColor.COLOR_PURPLE.id -> PURPLE_CANDLE
|
||||
MapColor.COLOR_BLUE.id -> BLUE_CANDLE
|
||||
MapColor.COLOR_BROWN.id -> BROWN_CANDLE
|
||||
MapColor.COLOR_GREEN.id -> GREEN_CANDLE
|
||||
MapColor.COLOR_RED.id -> RED_CANDLE
|
||||
MapColor.COLOR_BLACK.id -> BLACK_CANDLE
|
||||
else -> CANDLE
|
||||
} as CandleBlock
|
||||
}
|
||||
|
||||
val CANDLE_SHAPE: VoxelShape = box(7.0, 8.0, 7.0, 9.0, 15.0, 9.0)
|
||||
val SHAPE: VoxelShape = Shapes.or(SHAPE_BY_BITE[0], CANDLE_SHAPE)
|
||||
val PARTICLE_OFFSET = Vec3(0.5, 0.5 + TOP_LAYER_HEIGHT, 0.5)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(LIT, false)
|
||||
.setValue(CANDLE_COLOR, MapColor.SAND.id)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out BaseEntityBlock?> {
|
||||
return simpleCodec(::CandlePokeCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return CandlePokeCakeBlockEntity(pos, state)
|
||||
}
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
super.createBlockStateDefinition(builder)
|
||||
builder.add(LIT, CANDLE_COLOR)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hitResult: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
return if (!stack.`is`(Items.FLINT_AND_STEEL) && !stack.`is`(Items.FIRE_CHARGE)) {
|
||||
if (candleHit(hitResult) && stack.isEmpty && state.getValue(LIT) == true) {
|
||||
extinguish(player, state, level, pos)
|
||||
level.addParticle(ParticleTypes.SMOKE, PARTICLE_OFFSET.x + pos.x, pos.y + PARTICLE_OFFSET.y, pos.z + PARTICLE_OFFSET.z, 0.0, 0.1, 0.0)
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else {
|
||||
super.useItemOn(stack, state, level, pos, player, hand, hitResult)
|
||||
}
|
||||
} else if (state.getValue(LIT) == false) {
|
||||
level.playSound(player, pos, SoundEvents.FLINTANDSTEEL_USE, SoundSource.BLOCKS, 1.0f, level.random.nextFloat() * 0.4f + 0.8f)
|
||||
level.setBlock(pos, state.setValue(BlockStateProperties.LIT, true), UPDATE_ALL_IMMEDIATE)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
player.getItemInHand(hand).hurtAndBreak(1, player, hand.toEquipmentSlot())
|
||||
ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else {
|
||||
ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
||||
}
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
val oldCookingComponent = getFoodColourComponent(level, pos)
|
||||
val candle = getCandleByMapColorId(state.getValue(CANDLE_COLOR))
|
||||
|
||||
val newBlockState = CobblemonBlocks.POKE_CAKE.defaultBlockState()
|
||||
level.setBlockAndUpdate(pos, newBlockState)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
val pokeCakeBlock = newBlockState.block as PokeCakeBlock
|
||||
oldCookingComponent?.let {
|
||||
pokeCakeBlock.setFoodColourComponent(level, pos, it)
|
||||
}
|
||||
|
||||
val interactionResult = pokeCakeBlock.eat(level, pos, player)
|
||||
if (interactionResult.consumesAction()) {
|
||||
popResource(level, pos, ItemStack(candle.asItem()))
|
||||
}
|
||||
|
||||
return interactionResult
|
||||
}
|
||||
|
||||
private fun candleHit(hit: BlockHitResult): Boolean {
|
||||
return hit.location.y - hit.blockPos.y.toDouble() > TOP_LAYER_HEIGHT
|
||||
}
|
||||
|
||||
override fun animateTick(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
random: RandomSource
|
||||
) {
|
||||
if (state.getValue(LIT)) {
|
||||
AbstractCandleBlock.addParticlesAndSound(level, Vec3(PARTICLE_OFFSET.x + pos.x, PARTICLE_OFFSET.y + pos.y, PARTICLE_OFFSET.z + pos.z), random)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
level: BlockGetter,
|
||||
pos: BlockPos,
|
||||
context: CollisionContext
|
||||
): VoxelShape = SHAPE
|
||||
}
|
||||
@@ -10,24 +10,23 @@ package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.tags.FluidTags
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.ItemLike
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.*
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.CropBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.SoundType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
@@ -116,7 +115,7 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
}
|
||||
|
||||
private fun canGrow(world: LevelReader, pos: BlockPos, state: BlockState): Boolean {
|
||||
return !this.isMaxAge(state) && canSurvive(state, world, pos)
|
||||
return !this.isMaxAge(state) && canSurvive(state, world, pos) && !(!canGrowInto(world, pos.above()) && state.getValue(AGE) >= AGE_AFTER_HARVEST)
|
||||
}
|
||||
|
||||
private fun canGrowInto(world: LevelReader, pos: BlockPos): Boolean {
|
||||
@@ -124,28 +123,6 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
return blockState.isAir || blockState.`is`(CobblemonBlocks.HEARTY_GRAINS)
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(world: Level, pos: BlockPos, state: BlockState, player: Player): BlockState {
|
||||
if (!world.isClientSide) {
|
||||
if (state.getValue(HALF) == DoubleBlockHalf.UPPER) {
|
||||
val belowPos = pos.below()
|
||||
val belowState = world.getBlockState(belowPos)
|
||||
world.destroyBlock(pos, true) // Break the lower block without drops
|
||||
world.setBlock(
|
||||
belowPos,
|
||||
belowState.setValue(AGE, AGE_AFTER_HARVEST), // Reset age to simulate regrowth preparation
|
||||
UPDATE_CLIENTS
|
||||
)
|
||||
} else if (state.getValue(HALF) == DoubleBlockHalf.LOWER) {
|
||||
val abovePos = pos.above()
|
||||
val aboveState = world.getBlockState(abovePos)
|
||||
if (aboveState.block is HeartyGrainsBlock && aboveState.getValue(HALF) == DoubleBlockHalf.UPPER) {
|
||||
world.destroyBlock(abovePos, true) // Break the upper block without drops
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.playerWillDestroy(world, pos, state, player)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
@@ -157,15 +134,12 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
if (!state.canSurvive(world, pos)) return Blocks.AIR.defaultBlockState()
|
||||
val doubleBlockHalf = state.getValue(HALF)
|
||||
return if (direction.axis !== Direction.Axis.Y || doubleBlockHalf == DoubleBlockHalf.LOWER != (direction == Direction.UP) ||
|
||||
neighborState.`is`(this) && neighborState.getValue(HALF) != doubleBlockHalf) {
|
||||
if (doubleBlockHalf == DoubleBlockHalf.LOWER && direction == Direction.DOWN && !state.canSurvive(world, pos))
|
||||
Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
} else {
|
||||
Blocks.AIR.defaultBlockState()
|
||||
if (doubleBlockHalf == DoubleBlockHalf.LOWER && direction == Direction.UP) {
|
||||
if (state.getValue(AGE) > AGE_AFTER_HARVEST && !neighborState.`is`(CobblemonBlocks.HEARTY_GRAINS)) return state.setValue(AGE, AGE_AFTER_HARVEST)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
override fun isValidBonemealTarget(level: LevelReader, pos: BlockPos, state: BlockState): Boolean {
|
||||
@@ -211,7 +185,9 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBonemealAgeIncrease(world: Level): Int = 1
|
||||
override fun getBonemealAgeIncrease(level: Level): Int {
|
||||
return Mth.nextInt(level.random, 1, 3)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, world: LevelReader, pos: BlockPos): Boolean {
|
||||
return when (state.getValue(HALF)) {
|
||||
@@ -222,9 +198,9 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
// Check if bottom part is submerged in water
|
||||
val fluidState = world.getFluidState(pos)
|
||||
if (fluidState.`is`(FluidTags.WATER) && fluidState.isSource)
|
||||
return floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_WATER_PLANTABLE) && canGrowInto(world, pos.above())
|
||||
return floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_WATER_PLANTABLE)
|
||||
|
||||
floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_LAND_PLANTABLE) && canGrowInto(world, pos.above())
|
||||
floor.`is`(CobblemonBlockTags.HEARTY_GRAINS_LAND_PLANTABLE)
|
||||
}
|
||||
DoubleBlockHalf.UPPER -> {
|
||||
getLowerHalf(world, pos, state) != null
|
||||
@@ -248,4 +224,8 @@ class HeartyGrainsBlock(settings: Properties) : CropBlock(settings), SimpleWater
|
||||
|
||||
private fun isLower(state: BlockState): Boolean =
|
||||
state.`is`(CobblemonBlocks.HEARTY_GRAINS) && state.getValue(HALF) == DoubleBlockHalf.LOWER
|
||||
|
||||
override fun getSoundType(state: BlockState): SoundType? {
|
||||
return if (state.getValue(WATERLOGGED)) CobblemonSounds.HEARTY_GRAINS_WATER_SOUNDS else CobblemonSounds.HEARTY_GRAINS_SOUNDS
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.LureCakeBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class LureCakeBlock(settings: Properties): CakeBlock(settings) {
|
||||
override fun codec(): MapCodec<LureCakeBlock> {
|
||||
return simpleCodec(::LureCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return LureCakeBlockEntity(pos, state)
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.CANDLE_COLOR
|
||||
import com.cobblemon.mod.common.block.entity.PokeCakeBlockEntity
|
||||
import com.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.stats.Stats
|
||||
import net.minecraft.tags.ItemTags
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionHand.MAIN_HAND
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.BlockItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.BaseEntityBlock
|
||||
import net.minecraft.world.level.block.CandleBlock
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
|
||||
class PokeCakeBlock(settings: Properties): CakeBlock(settings) {
|
||||
override fun codec(): MapCodec<out BaseEntityBlock?> {
|
||||
return simpleCodec(::PokeCakeBlock)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
|
||||
return PokeCakeBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun setPlacedBy(
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
stack: ItemStack
|
||||
) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? PokeCakeBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
if (level.isClientSide) {
|
||||
if (eat(level, pos, player).consumesAction()) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (player.getItemInHand(MAIN_HAND).isEmpty) {
|
||||
return InteractionResult.CONSUME
|
||||
}
|
||||
}
|
||||
|
||||
return eat(level, pos, player)
|
||||
}
|
||||
|
||||
override fun useItemOn(
|
||||
stack: ItemStack,
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hand: InteractionHand,
|
||||
hitResult: BlockHitResult
|
||||
): ItemInteractionResult {
|
||||
val item = stack.item
|
||||
val block = state.block as? PokeCakeBlock
|
||||
|
||||
if (stack.`is`(ItemTags.CANDLES) && block?.getBites(level, pos) == 0) {
|
||||
val candleBlock = byItem(item)
|
||||
if (candleBlock is CandleBlock) {
|
||||
stack.consume(1, player)
|
||||
level.playSound(null, pos, SoundEvents.CAKE_ADD_CANDLE, SoundSource.BLOCKS, 1.0f, 1.0f)
|
||||
|
||||
val oldCookingComponent = block.getFoodColourComponent(level, pos)
|
||||
|
||||
val newBlockState = CobblemonBlocks.CANDLE_POKE_CAKE.defaultBlockState()
|
||||
.setValue(CANDLE_COLOR, (item as BlockItem).block.defaultMapColor().id)
|
||||
level.setBlockAndUpdate(pos, newBlockState)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
|
||||
oldCookingComponent?.let {
|
||||
(newBlockState.block as CakeBlock).setFoodColourComponent(level, pos, it)
|
||||
}
|
||||
|
||||
player.awardStat(Stats.ITEM_USED[item])
|
||||
return ItemInteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
|
||||
}
|
||||
|
||||
fun eat(level: LevelAccessor, pos: BlockPos, player: Player): InteractionResult {
|
||||
if (!player.canEat(false)) {
|
||||
return InteractionResult.PASS
|
||||
}
|
||||
|
||||
player.getFoodData().eat(2, 0.1F)
|
||||
level.gameEvent(player, GameEvent.EAT, pos)
|
||||
|
||||
var bites = getBites(level, pos)
|
||||
if (bites < 6) {
|
||||
setBites(level, pos, bites + 1)
|
||||
} else {
|
||||
level.removeBlock(pos, false)
|
||||
level.gameEvent(player, GameEvent.BLOCK_DESTROY, pos)
|
||||
}
|
||||
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.util.toEquipmentSlot
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.particles.BlockParticleOption
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.stats.Stats
|
||||
import net.minecraft.tags.ItemTags
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.ItemInteractionResult
|
||||
import net.minecraft.world.entity.EquipmentSlot
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.entity.projectile.Projectile
|
||||
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.LevelReader
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.MapColor
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class PokeSnackBlock(settings: Properties, val isLure: Boolean): BaseEntityBlock(settings) {
|
||||
companion object {
|
||||
val MAX_BITES = 8
|
||||
val CAKE_HEIGHT = 0.4375
|
||||
|
||||
val CANDLE_PARTICLE_POSITION = Vec3(0.5, CAKE_HEIGHT + 0.5, 0.5)
|
||||
|
||||
val BITES: IntegerProperty = IntegerProperty.create("bites", 0, MAX_BITES)
|
||||
val CANDLE: IntegerProperty = IntegerProperty.create("candle", 0, MapColor.MATERIAL_COLORS.size - 1)
|
||||
|
||||
val idToCandleBlock: Map<Int, Block> = mapOf(
|
||||
MapColor.WOOL.id to Blocks.WHITE_CANDLE,
|
||||
MapColor.COLOR_ORANGE.id to Blocks.ORANGE_CANDLE,
|
||||
MapColor.COLOR_MAGENTA.id to Blocks.MAGENTA_CANDLE,
|
||||
MapColor.COLOR_LIGHT_BLUE.id to Blocks.LIGHT_BLUE_CANDLE,
|
||||
MapColor.COLOR_YELLOW.id to Blocks.YELLOW_CANDLE,
|
||||
MapColor.COLOR_LIGHT_GREEN.id to Blocks.LIME_CANDLE,
|
||||
MapColor.COLOR_PINK.id to Blocks.PINK_CANDLE,
|
||||
MapColor.COLOR_GRAY.id to Blocks.GRAY_CANDLE,
|
||||
MapColor.COLOR_LIGHT_GRAY.id to Blocks.LIGHT_GRAY_CANDLE,
|
||||
MapColor.COLOR_CYAN.id to Blocks.CYAN_CANDLE,
|
||||
MapColor.COLOR_PURPLE.id to Blocks.PURPLE_CANDLE,
|
||||
MapColor.COLOR_BLUE.id to Blocks.BLUE_CANDLE,
|
||||
MapColor.COLOR_BROWN.id to Blocks.BROWN_CANDLE,
|
||||
MapColor.COLOR_GREEN.id to Blocks.GREEN_CANDLE,
|
||||
MapColor.COLOR_RED.id to Blocks.RED_CANDLE,
|
||||
MapColor.COLOR_BLACK.id to Blocks.BLACK_CANDLE
|
||||
)
|
||||
|
||||
fun getCandleById(id: Int): Block = idToCandleBlock[id] ?: Blocks.CANDLE
|
||||
fun getIdByCandle(candle: CandleBlock): Int = (idToCandleBlock.entries.associate { (id, block) -> block to id })[candle] ?: MapColor.SAND.id
|
||||
|
||||
private val CANDLE_SHAPE = Shapes.box(0.4375, CAKE_HEIGHT, 0.4375, 0.5625, 0.8125, 0.5625)
|
||||
|
||||
private val SHAPES = listOf(
|
||||
Shapes.box(0.0625, 0.0, 0.0625, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.or(
|
||||
Shapes.box(0.3125, 0.0, 0.0625, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.9375, CAKE_HEIGHT, 0.9375)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.625, 0.0, 0.0625, 0.9375, CAKE_HEIGHT, 0.3125),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.9375, CAKE_HEIGHT, 0.9375)
|
||||
),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.or(
|
||||
Shapes.box(0.6875, 0.0, 0.625, 0.9375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.9375)
|
||||
),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.or(
|
||||
Shapes.box(0.0625, 0.0, 0.6875, 0.375, CAKE_HEIGHT, 0.9375),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.6875)
|
||||
),
|
||||
Shapes.box(0.0625, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.6875),
|
||||
Shapes.box(0.3125, 0.0, 0.3125, 0.6875, CAKE_HEIGHT, 0.6875)
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(BITES, 0)
|
||||
.setValue(CANDLE, 0)
|
||||
.setValue(BlockStateProperties.LIT, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<PokeSnackBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
PrimitiveCodec.BOOL.fieldOf("isLure").forGetter { it.isLure }
|
||||
).apply(it, ::PokeSnackBlock) }
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||
builder.add(BITES, CANDLE, BlockStateProperties.LIT)
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = PokeSnackBlockEntity(pos, state)
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? {
|
||||
if (hasCandle(state)) {
|
||||
return Shapes.or(SHAPES[state.getValue(BITES)], CANDLE_SHAPE)
|
||||
}
|
||||
return SHAPES[state.getValue(BITES)]
|
||||
}
|
||||
|
||||
override fun getRenderShape(blockState: BlockState?) = RenderShape.MODEL
|
||||
|
||||
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, placer: LivingEntity?, stack: ItemStack) {
|
||||
super.setPlacedBy(level, pos, state, placer, stack)
|
||||
|
||||
val blockEntity = level.getBlockEntity(pos) as? PokeSnackBlockEntity
|
||||
blockEntity?.initializeFromItemStack(stack)
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
val blockEntity = level.getBlockEntity(pos) as? PokeSnackBlockEntity ?: return ItemStack.EMPTY
|
||||
return blockEntity.toItemStack()
|
||||
}
|
||||
|
||||
override fun useWithoutItem(state: BlockState, level: Level, pos: BlockPos, player: Player, hitResult: BlockHitResult): InteractionResult {
|
||||
if (!isLure) {
|
||||
if (!candleHit(hitResult)) {
|
||||
if (level.isClientSide) {
|
||||
if (playerEat(level, pos, state, player).consumesAction()) {
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (player.getItemInHand(InteractionHand.MAIN_HAND).isEmpty) {
|
||||
return InteractionResult.CONSUME
|
||||
}
|
||||
}
|
||||
return playerEat(level, pos, state, player)
|
||||
} else if (canBeLit(state) && player.mainHandItem.isEmpty && player.isCrouching()) {
|
||||
player.setItemSlot(EquipmentSlot.MAINHAND, ItemStack(getCandleById(state.getValue(CANDLE))))
|
||||
level.playSound(null, pos, SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.5F, level.random.nextFloat() * 0.4F + 0.8F)
|
||||
level.setBlockAndUpdate(pos, state.setValue(CANDLE, 0))
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
}
|
||||
}
|
||||
return super.useWithoutItem(state, level, pos, player, hitResult)
|
||||
}
|
||||
|
||||
override fun useItemOn(stack: ItemStack, state: BlockState, level: Level, pos: BlockPos, player: Player, hand: InteractionHand, hitResult: BlockHitResult): ItemInteractionResult {
|
||||
if (!isLure) {
|
||||
if (stack.`is`(ItemTags.CANDLES) && !hasCandle(state)) {
|
||||
val item = stack.getItem()
|
||||
val candle = byItem(item)
|
||||
if (candle is CandleBlock) {
|
||||
stack.consume(1, player)
|
||||
level.playSound(null as Player?, pos, SoundEvents.CAKE_ADD_CANDLE, SoundSource.BLOCKS, 1.0F, 1.0F)
|
||||
level.setBlockAndUpdate(pos, state.setValue(CANDLE, getIdByCandle(candle)))
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
player.awardStat(Stats.ITEM_USED.get(item))
|
||||
return ItemInteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
if (candleHit(hitResult) && hasCandle(state)) {
|
||||
val isFlintAndSteel = stack.`is`(Items.FLINT_AND_STEEL)
|
||||
if (canBeLit(state) && (isFlintAndSteel || stack.`is`(Items.FIRE_CHARGE))) {
|
||||
setCandleLit(level, state, pos, true)
|
||||
level.playSound(
|
||||
player,
|
||||
pos,
|
||||
if (isFlintAndSteel) SoundEvents.FLINTANDSTEEL_USE else SoundEvents.FIRECHARGE_USE,
|
||||
SoundSource.BLOCKS,
|
||||
1.0F,
|
||||
level.random.nextFloat() * 0.4F + 0.8F
|
||||
)
|
||||
|
||||
if (isFlintAndSteel) stack.hurtAndBreak(1, player, hand.toEquipmentSlot())
|
||||
else stack.consume(1, player)
|
||||
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
} else if (stack.isEmpty() && isCandleLit(state)) {
|
||||
extinguishCandle(player, state, level, pos)
|
||||
return ItemInteractionResult.sidedSuccess(level.isClientSide)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.useItemOn(stack, state, level, pos, player, hand, hitResult)
|
||||
}
|
||||
|
||||
override fun animateTick(state: BlockState, level: Level, pos: BlockPos, random: RandomSource) {
|
||||
if (isCandleLit(state)) {
|
||||
AbstractCandleBlock.addParticlesAndSound(
|
||||
level,
|
||||
Vec3(
|
||||
pos.x + CANDLE_PARTICLE_POSITION.x,
|
||||
pos.y + CANDLE_PARTICLE_POSITION.y,
|
||||
pos.z + CANDLE_PARTICLE_POSITION.z
|
||||
),
|
||||
random
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProjectileHit(level: Level, state: BlockState, hit: BlockHitResult, projectile: Projectile) {
|
||||
if (!level.isClientSide && projectile.isOnFire() && canBeLit(state)) {
|
||||
setCandleLit(level, state, hit.getBlockPos(), true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun playerWillDestroy(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player): BlockState {
|
||||
dropCandle(level, blockPos, blockState, player)
|
||||
return super.playerWillDestroy(level, blockPos, blockState, player)
|
||||
}
|
||||
|
||||
private fun dropCandle(level: Level, blockPos: BlockPos, blockState: BlockState, player: Player?) {
|
||||
if (hasCandle(blockState) && !level.isClientSide && (!(player?.isCreative ?: false))) {
|
||||
val itemEntity = ItemEntity(
|
||||
level,
|
||||
blockPos.x + 0.5,
|
||||
blockPos.y + CAKE_HEIGHT,
|
||||
blockPos.z + 0.5,
|
||||
ItemStack(getCandleById(blockState.getValue(CANDLE)))
|
||||
)
|
||||
itemEntity.setDefaultPickUpDelay()
|
||||
level.addFreshEntity(itemEntity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun playerEat(level: Level, pos: BlockPos, state: BlockState, player: Player): InteractionResult {
|
||||
if (!player.canEat(false)) {
|
||||
return InteractionResult.PASS
|
||||
} else {
|
||||
player.awardStat(Stats.EAT_CAKE_SLICE)
|
||||
player.getFoodData().eat(2, 0.1F)
|
||||
level.gameEvent(player, GameEvent.EAT, pos)
|
||||
eat(level, pos, state, player)
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
fun eat(level: Level, pos: BlockPos, state: BlockState, player: Player?) {
|
||||
val bites = state.getValue(BITES) as Int
|
||||
if (bites < MAX_BITES) {
|
||||
level.setBlock(pos, state.setValue(BITES, bites + 1) as BlockState, UPDATE_ALL)
|
||||
} else {
|
||||
dropCandle(level, pos, state, player)
|
||||
level.removeBlock(pos, false)
|
||||
level.removeBlockEntity(pos)
|
||||
}
|
||||
level.playSound(null, player?.blockPosition() ?: pos, SoundEvents.GENERIC_EAT, if (player != null) SoundSource.PLAYERS else SoundSource.NEUTRAL)
|
||||
spawnEatParticles(level, pos)
|
||||
player?.let {
|
||||
level.gameEvent(it, GameEvent.BLOCK_DESTROY, pos)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasCandle(state: BlockState) = state.getValue(CANDLE) > 0
|
||||
private fun isCandleLit(state: BlockState) = state.getValue(BlockStateProperties.LIT)
|
||||
private fun canBeLit(state: BlockState) = hasCandle(state) && !isCandleLit(state)
|
||||
|
||||
private fun setCandleLit(level: LevelAccessor, state: BlockState, pos: BlockPos, lit: Boolean) {
|
||||
level.setBlock(pos, state.setValue(BlockStateProperties.LIT, lit) as BlockState, UPDATE_ALL_IMMEDIATE)
|
||||
}
|
||||
|
||||
private fun extinguishCandle(player: Player?, state: BlockState, level: LevelAccessor, pos: BlockPos) {
|
||||
setCandleLit(level, state, pos, false)
|
||||
level.addParticle(
|
||||
ParticleTypes.SMOKE,
|
||||
pos.getX().toDouble() + CANDLE_PARTICLE_POSITION.x(),
|
||||
pos.getY().toDouble() + CANDLE_PARTICLE_POSITION.y(),
|
||||
pos.getZ().toDouble() + CANDLE_PARTICLE_POSITION.z(),
|
||||
0.0,
|
||||
0.1,
|
||||
0.0
|
||||
)
|
||||
|
||||
level.playSound(null as Player?, pos, SoundEvents.CANDLE_EXTINGUISH, SoundSource.BLOCKS, 1.0F, 1.0F)
|
||||
level.gameEvent(player, GameEvent.BLOCK_CHANGE, pos)
|
||||
}
|
||||
|
||||
private fun candleHit(hit: BlockHitResult): Boolean {
|
||||
return hit.getLocation().y - hit.getBlockPos().getY().toDouble() > CAKE_HEIGHT
|
||||
}
|
||||
|
||||
fun spawnEatParticles(level: Level, pos: BlockPos) {
|
||||
val random = level.random
|
||||
repeat(5) {
|
||||
level.addParticle(
|
||||
BlockParticleOption(ParticleTypes.BLOCK, CobblemonBlocks.POKE_SNACK.defaultBlockState()),
|
||||
pos.x + random.nextDouble(),
|
||||
pos.y + random.nextDouble() * CAKE_HEIGHT,
|
||||
pos.z + random.nextDouble(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,12 @@ package com.cobblemon.mod.common.block.campfirepot
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
import com.cobblemon.mod.common.item.CampfirePotItem
|
||||
import com.cobblemon.mod.common.util.playSoundServer
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
@@ -55,12 +56,16 @@ import org.jetbrains.annotations.Nullable
|
||||
import net.minecraft.world.level.block.CampfireBlock as MCCampfireBlock
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
|
||||
class CampfireBlock(settings: Properties, val isSoul: Boolean) : BaseEntityBlock(settings), SimpleWaterloggedBlock {
|
||||
companion object {
|
||||
val CODEC = simpleCodec(::CampfireBlock)
|
||||
val CODEC: MapCodec<CampfireBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
propertiesCodec(),
|
||||
Codec.BOOL.fieldOf("isSoul").forGetter(CampfireBlock::isSoul)
|
||||
).apply(it, ::CampfireBlock)
|
||||
}
|
||||
val ITEM_DIRECTION = DirectionProperty.create("item_facing")
|
||||
val LIT = BlockStateProperties.LIT
|
||||
var SOUL = BooleanProperty.create("soul")
|
||||
val POWERED = BlockStateProperties.POWERED
|
||||
|
||||
private val campfireAABB = Shapes.box(0.0, 0.0, 0.0, 1.0, 0.4375, 1.0)
|
||||
@@ -78,7 +83,6 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(LIT, true)
|
||||
.setValue(SOUL, false)
|
||||
.setValue(ITEM_DIRECTION, Direction.NORTH)
|
||||
.setValue(POWERED, false))
|
||||
}
|
||||
@@ -182,11 +186,9 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
blockEntity.setPotItem(ItemStack.EMPTY)
|
||||
level.playSoundServer(blockPos.bottomCenter, CobblemonSounds.CAMPFIRE_POT_RETRIEVE)
|
||||
|
||||
blockEntity.setItem(PREVIEW_ITEM_SLOT, ItemStack.EMPTY)
|
||||
Containers.dropContents(level, blockPos, blockEntity)
|
||||
|
||||
val facing = blockState.getValue(FACING)
|
||||
val isSoul = blockState.getValue(SOUL)
|
||||
blockEntity.setRemoved()
|
||||
|
||||
val newBlockState = if (isSoul) Blocks.SOUL_CAMPFIRE.defaultBlockState().setValue(FACING, facing)
|
||||
@@ -201,7 +203,7 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, ITEM_DIRECTION, LIT, SOUL, POWERED)
|
||||
builder.add(FACING, ITEM_DIRECTION, LIT, POWERED)
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
@@ -271,7 +273,7 @@ class CampfireBlock(settings: Properties) : BaseEntityBlock(settings), SimpleWat
|
||||
}
|
||||
|
||||
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
|
||||
return if (state.getValue(SOUL)) ItemStack(Blocks.SOUL_CAMPFIRE) else ItemStack(Blocks.CAMPFIRE)
|
||||
return if (isSoul) ItemStack(Blocks.SOUL_CAMPFIRE) else ItemStack(Blocks.CAMPFIRE)
|
||||
}
|
||||
|
||||
override fun neighborChanged(
|
||||
|
||||
+35
-7
@@ -36,6 +36,7 @@ import net.minecraft.world.item.crafting.RecipeHolder
|
||||
import net.minecraft.world.item.crafting.RecipeManager
|
||||
import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
import java.util.*
|
||||
|
||||
class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, ContainerListener {
|
||||
private val player: Player
|
||||
@@ -46,6 +47,8 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
val containerData: ContainerData
|
||||
private val recipeType: RecipeType<CookingPotRecipe> = CobblemonRecipeTypes.COOKING_POT_COOKING
|
||||
private val quickCheck = RecipeManager.createCheck(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
var currentActiveRecipe: RecipeHolder<CookingPotRecipeBase>? = null
|
||||
var previewItem = ItemStack.EMPTY
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory) : super(CobblemonMenuType.COOKING_POT, containerId) {
|
||||
this.playerInventory = playerInventory
|
||||
@@ -59,6 +62,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
this.player = playerInventory.player
|
||||
this.level = playerInventory.player.level()
|
||||
initializeSlots(playerInventory)
|
||||
this.addSlotListener(this)
|
||||
}
|
||||
|
||||
constructor(containerId: Int, playerInventory: Inventory, container: CraftingContainer, containerData: ContainerData) : super(
|
||||
@@ -91,9 +95,6 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
addSlot(SeasoningSlot(this.container, slotIndex, 110 + index * 18, 18))
|
||||
}
|
||||
|
||||
addSlot(CookingPotPreviewSlot(this.container,
|
||||
CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT, resultSlotX, resultSlotY))
|
||||
|
||||
for ((index, _) in CampfireBlockEntity.Companion.PLAYER_INVENTORY_SLOTS.withIndex()) {
|
||||
val i = index / CampfireBlockEntity.Companion.PLAYER_INVENTORY_WIDTH
|
||||
val j = index % CampfireBlockEntity.Companion.PLAYER_INVENTORY_WIDTH
|
||||
@@ -148,14 +149,40 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
}
|
||||
|
||||
override fun recipeMatches(recipe: RecipeHolder<CookingPotRecipeBase>): Boolean {
|
||||
val craftInput = CraftingInput.of(3,3, container.items.subList(1,10))
|
||||
val recipeValue = recipe.value()
|
||||
return if (recipeValue is CookingPotRecipeBase) {
|
||||
recipeValue.matches(container.asCraftInput(), level)
|
||||
recipeValue.matches(craftInput, level)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun recalculateRecipe() {
|
||||
val craftInput = CraftingInput.of(3,3, container.items.subList(1,10))
|
||||
fun <T : CookingPotRecipeBase> fetchRecipe(
|
||||
recipeType: RecipeType<T>
|
||||
): Optional<RecipeHolder<CookingPotRecipeBase>> {
|
||||
val optional = level.recipeManager.getRecipeFor(recipeType, craftInput, level)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return optional.map { it as RecipeHolder<CookingPotRecipeBase> }
|
||||
}
|
||||
|
||||
val recipe = fetchRecipe(CobblemonRecipeTypes.COOKING_POT_COOKING)
|
||||
.orElseGet { fetchRecipe(CobblemonRecipeTypes.COOKING_POT_SHAPELESS).orElse(null) }
|
||||
|
||||
currentActiveRecipe = recipe
|
||||
if (recipe != null) {
|
||||
previewItem = recipe.value.assemble(craftInput, level.registryAccess())
|
||||
recipe.value.applySeasoning(
|
||||
previewItem,
|
||||
container.items.subList(CampfireBlockEntity.SEASONING_SLOTS.first,
|
||||
CampfireBlockEntity.SEASONING_SLOTS.last + 1)
|
||||
.filterNotNull()
|
||||
.filter { !it.isEmpty })
|
||||
} else previewItem = ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun getResultSlotIndex(): Int {
|
||||
return CampfireBlockEntity.Companion.RESULT_SLOT
|
||||
}
|
||||
@@ -187,7 +214,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
}
|
||||
|
||||
override fun shouldMoveToInventory(slotIndex: Int): Boolean {
|
||||
return slotIndex != CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT && !CampfireBlockEntity.SEASONING_SLOTS.contains(slotIndex)
|
||||
return !CampfireBlockEntity.SEASONING_SLOTS.contains(slotIndex)
|
||||
}
|
||||
|
||||
override fun quickMoveStack(
|
||||
@@ -209,8 +236,8 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
slot.onQuickCraft(slotItemStack, itemStack);
|
||||
} else if (index in CampfireBlockEntity.Companion.PLAYER_INVENTORY_SLOTS || index in CampfireBlockEntity.Companion.PLAYER_HOTBAR_SLOTS) {
|
||||
if (Seasonings.isSeasoning(slotItemStack)) {
|
||||
if (!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.SEASONING_SLOTS.first, CampfireBlockEntity.Companion.SEASONING_SLOTS.last + 1, false) &&
|
||||
!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.first, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.last + 1, false)
|
||||
if (!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.first, CampfireBlockEntity.Companion.CRAFTING_GRID_SLOTS.last + 1, false) &&
|
||||
!this.moveItemStackTo(slotItemStack, CampfireBlockEntity.Companion.SEASONING_SLOTS.first, CampfireBlockEntity.Companion.SEASONING_SLOTS.last + 1, false)
|
||||
) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
@@ -244,6 +271,7 @@ class CookingPotMenu : RecipeBookMenu<CraftingInput, CookingPotRecipeBase>, Cont
|
||||
}
|
||||
|
||||
override fun slotChanged(containerToSend: AbstractContainerMenu, dataSlotIndex: Int, stack: ItemStack) {
|
||||
if (CampfireBlockEntity.CRAFTING_GRID_SLOTS.contains(dataSlotIndex) || CampfireBlockEntity.SEASONING_SLOTS.contains(dataSlotIndex)) recalculateRecipe()
|
||||
broadcastChanges()
|
||||
}
|
||||
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.campfirepot
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.RESULT_SLOT
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.inventory.CraftingContainer
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class CookingPotPreviewSlot(
|
||||
container: CraftingContainer,
|
||||
index: Int,
|
||||
x: Int,
|
||||
y: Int
|
||||
) : Slot(container, index, x, y) {
|
||||
override fun isActive(): Boolean {
|
||||
return container.getItem(RESULT_SLOT).isEmpty
|
||||
}
|
||||
|
||||
override fun isFake(): Boolean = true
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun mayPickup(player: Player): Boolean = false
|
||||
override fun isHighlightable() = false
|
||||
}
|
||||
+1
-2
@@ -31,12 +31,11 @@ class CookingPotResultSlot(
|
||||
val menu = player.containerMenu
|
||||
if (menu is CookingPotMenu) {
|
||||
menu.broadcastChanges()
|
||||
player.playNotifySound(CobblemonSounds.CAMPFIRE_POT_USE, SoundSource.MASTER, 1.0f, 1.0f)
|
||||
player.playNotifySound(CobblemonSounds.CAMPFIRE_POT_TAKE_ITEM, SoundSource.MASTER, 1.0f, 1.0f)
|
||||
}
|
||||
|
||||
super.onTake(player, stack)
|
||||
}
|
||||
|
||||
override fun mayPlace(stack: ItemStack): Boolean = false
|
||||
override fun isActive(): Boolean = hasItem()
|
||||
}
|
||||
+51
-41
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonRecipeTypes
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.cooking.getColourMixFromSeasonings
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.item.components.PotComponent
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
@@ -39,11 +40,12 @@ import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.FastColor
|
||||
import net.minecraft.world.ContainerHelper
|
||||
import net.minecraft.world.WorldlyContainer
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.entity.player.StackedContents
|
||||
import net.minecraft.world.inventory.*
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import net.minecraft.world.item.crafting.RecipeHolder
|
||||
import net.minecraft.world.item.crafting.RecipeManager
|
||||
@@ -51,6 +53,7 @@ import net.minecraft.world.item.crafting.RecipeType
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
|
||||
import net.minecraft.world.level.block.entity.HopperBlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.Vec3
|
||||
@@ -67,11 +70,10 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
const val RESULT_SLOT = 0
|
||||
val CRAFTING_GRID_SLOTS = 1..9
|
||||
val SEASONING_SLOTS = 10..12
|
||||
const val PREVIEW_ITEM_SLOT = 13
|
||||
const val ITEMS_SIZE = 14
|
||||
const val ITEMS_SIZE = 13
|
||||
|
||||
val PLAYER_INVENTORY_SLOTS = 14..40
|
||||
val PLAYER_HOTBAR_SLOTS = 41..49
|
||||
val PLAYER_INVENTORY_SLOTS = 13..39
|
||||
val PLAYER_HOTBAR_SLOTS = 40..48
|
||||
|
||||
const val CRAFTING_GRID_WIDTH = 3
|
||||
const val PLAYER_INVENTORY_WIDTH = 9
|
||||
@@ -89,15 +91,21 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
if (!level.isClientSide) return
|
||||
|
||||
val isLit = campfireBlockEntity.dataAccess.get(COOKING_PROGRESS_INDEX) > 0
|
||||
val isSoundActive = BlockEntitySoundTracker.isActive(pos, campfireBlockEntity.runningSound.location)
|
||||
val isRunningSoundActive = BlockEntitySoundTracker.isActive(pos, campfireBlockEntity.runningSound.location)
|
||||
val isAmbientSoundActive = BlockEntitySoundTracker.isActive(pos, campfireBlockEntity.ambientSound.location)
|
||||
val containsItems = campfireBlockEntity.getSeasonings().isNotEmpty() || campfireBlockEntity.getIngredients().isNotEmpty()
|
||||
|
||||
if (isLit && !isSoundActive) {
|
||||
BlockEntitySoundTracker.play(
|
||||
pos,
|
||||
CancellableSoundInstance(campfireBlockEntity.runningSound, pos, true, 0.8f, 1.0f)
|
||||
)
|
||||
} else if (!isLit && isSoundActive) {
|
||||
if (containsItems) {
|
||||
if (isLit) {
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.ambientSound.location)
|
||||
if (!isRunningSoundActive) BlockEntitySoundTracker.play(pos, CancellableSoundInstance(campfireBlockEntity.runningSound, pos, true, 1.0f, 1.0f))
|
||||
} else {
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.runningSound.location)
|
||||
if (!isAmbientSoundActive) BlockEntitySoundTracker.play(pos, CancellableSoundInstance(campfireBlockEntity.ambientSound, pos, true, 1.0f, 1.0f))
|
||||
}
|
||||
} else {
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.runningSound.location)
|
||||
BlockEntitySoundTracker.stop(pos, campfireBlockEntity.ambientSound.location)
|
||||
}
|
||||
|
||||
campfireBlockEntity.brothColor =
|
||||
@@ -149,7 +157,6 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
|
||||
if (optionalRecipe == null) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
campfireBlockEntity.setItem(PREVIEW_ITEM_SLOT, ItemStack.EMPTY)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -160,8 +167,6 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
|
||||
recipe.applySeasoning(cookedItem, campfireBlockEntity.getSeasonings())
|
||||
|
||||
campfireBlockEntity.setItem(PREVIEW_ITEM_SLOT, cookedItem)
|
||||
|
||||
if (campfireBlockEntity.isLidOpen) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
return
|
||||
@@ -171,7 +176,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
if (!ItemStack.isSameItemSameComponents(
|
||||
resultSlotItem,
|
||||
cookedItem
|
||||
) || resultSlotItem.count >= resultSlotItem.maxStackSize
|
||||
) || resultSlotItem.count + cookedItem.count > resultSlotItem.maxStackSize
|
||||
) {
|
||||
campfireBlockEntity.cookingProgress = 0
|
||||
return
|
||||
@@ -191,11 +196,11 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
resultSlotItem.grow(cookedItem.count)
|
||||
}
|
||||
|
||||
campfireBlockEntity.consumeCraftingIngredients(recipe)
|
||||
campfireBlockEntity.consumeCraftingIngredients(recipe, level, pos, state, campfireBlockEntity)
|
||||
|
||||
level.playSoundServer(
|
||||
position = pos.bottomCenter,
|
||||
sound = CobblemonSounds.CAMPFIRE_POT_CRAFT,
|
||||
sound = CobblemonSounds.CAMPFIRE_POT_COOK,
|
||||
)
|
||||
|
||||
setChanged(level, pos, state);
|
||||
@@ -204,7 +209,8 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
}
|
||||
}
|
||||
|
||||
private val runningSound = CobblemonSounds.CAMPFIRE_POT_COOK
|
||||
private val runningSound = CobblemonSounds.CAMPFIRE_POT_ACTIVE
|
||||
private val ambientSound = CobblemonSounds.CAMPFIRE_POT_AMBIENT
|
||||
private var cookingProgress: Int = 0
|
||||
private var cookingTotalTime: Int = COOKING_TOTAL_TIME
|
||||
private var isLidOpen: Boolean = true
|
||||
@@ -270,32 +276,17 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
return particleStorm
|
||||
}
|
||||
|
||||
fun consumeCraftingIngredients(recipe: CookingPotRecipeBase) {
|
||||
fun consumeCraftingIngredients(recipe: CookingPotRecipeBase, level: Level, pos: BlockPos, state: BlockState, campfireBlockEntity: CampfireBlockEntity) {
|
||||
val remainderItems = mutableMapOf<Item, Int>() //This is so we don't spawn multiple entities for buckets
|
||||
|
||||
fun consumeItem(slot: Int) {
|
||||
val itemInSlot = getItem(slot)
|
||||
if (!itemInSlot.isEmpty) {
|
||||
when (itemInSlot.item) {
|
||||
Items.LAVA_BUCKET, Items.WATER_BUCKET, Items.MILK_BUCKET -> {
|
||||
// Replace with empty bucket
|
||||
setItem(slot, ItemStack(Items.BUCKET))
|
||||
}
|
||||
|
||||
Items.HONEY_BOTTLE -> {
|
||||
// TODO: Currently eats the empty bottles until the honey bottle stack is empty, replace with better system later.
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) {
|
||||
setItem(slot, ItemStack.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Decrease the stack size by 1
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) {
|
||||
setItem(slot, ItemStack.EMPTY) // Clear the slot if empty
|
||||
}
|
||||
}
|
||||
if (itemInSlot.item.hasCraftingRemainingItem()) {
|
||||
remainderItems[itemInSlot.item.craftingRemainingItem!!] = (remainderItems[itemInSlot.item.craftingRemainingItem!!] ?: 0) + 1
|
||||
}
|
||||
itemInSlot.shrink(1)
|
||||
if (itemInSlot.count <= 0) setItem(slot, ItemStack.EMPTY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +296,24 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
for (i in SEASONING_SLOTS.first..SEASONING_SLOTS.last) {
|
||||
if (recipe.seasoningProcessors.any { it.consumesItem(getItem(i)) }) consumeItem(i)
|
||||
}
|
||||
|
||||
val direction = state.getValue(CampfireBlock.ITEM_DIRECTION)
|
||||
val container = HopperBlockEntity.getContainerAt(level, pos.relative(direction))
|
||||
|
||||
for (remainder in remainderItems) {
|
||||
var remainderItem = ItemStack(remainder.key, remainder.value)
|
||||
|
||||
if (container != null) {
|
||||
remainderItem = HopperBlockEntity.addItem(campfireBlockEntity, container, remainderItem, direction.opposite)
|
||||
}
|
||||
|
||||
if (!remainderItem.isEmpty) {
|
||||
val spawnPos = Vec3.atCenterOf(pos).relative(direction, 0.7)
|
||||
val itemEntity = ItemEntity(level, spawnPos.x, spawnPos.y, spawnPos.z, remainderItem)
|
||||
itemEntity.setDeltaMovement(direction.stepX * 0.05, 0.0, direction.stepZ * 0.05)
|
||||
level.addFreshEntity(itemEntity) //Wanted to use default dispenser behavior but its too much speed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSeasonings(): List<ItemStack> =
|
||||
@@ -429,6 +438,7 @@ class CampfireBlockEntity(pos: BlockPos, state: BlockState) : BaseContainerBlock
|
||||
|
||||
if (level?.isClientSide == true) {
|
||||
BlockEntitySoundTracker.stop(blockPos, runningSound.location)
|
||||
BlockEntitySoundTracker.stop(blockPos, ambientSound.location)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class CandlePokeCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.CANDLE_POKE_CAKE, pos, state)
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class LureCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.LURE_CAKE, pos, state) {
|
||||
override val maxBites: Int
|
||||
get() = 20
|
||||
|
||||
/**
|
||||
* Combine all the [SpawnBait.Effect] values from the [baitEffectsComponent] data.
|
||||
*/
|
||||
fun getBaitEffectsFromLureCake(): List<SpawnBait.Effect> {
|
||||
return baitEffectsComponent?.effects?.mapNotNull(SpawnBaitEffects::getFromIdentifier)?.flatMap { it.effects }.orEmpty()
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class PokeCakeBlockEntity(
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : CakeBlockEntity(CobblemonBlockEntities.POKE_CAKE, pos, state)
|
||||
+93
-96
@@ -8,130 +8,127 @@
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.cooking.getColourMixFromFlavours
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBait.Effect
|
||||
import com.cobblemon.mod.common.api.fishing.SpawnBaitEffects
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.item.components.BaitEffectsComponent
|
||||
import com.cobblemon.mod.common.item.components.FlavourComponent
|
||||
import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
import com.cobblemon.mod.common.item.components.IngredientComponent
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.NbtOps
|
||||
import net.minecraft.network.protocol.Packet
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener
|
||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
|
||||
import net.minecraft.nbt.*
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import kotlin.collections.orEmpty
|
||||
|
||||
open class CakeBlockEntity(
|
||||
type: BlockEntityType<*>,
|
||||
pos: BlockPos,
|
||||
state: BlockState
|
||||
) : BlockEntity(type, pos, state) {
|
||||
open val maxBites: Int
|
||||
get() = 6
|
||||
open class PokeSnackBlockEntity(pos: BlockPos, state: BlockState) : TintBlockEntity(CobblemonBlockEntities.POKE_SNACK, pos, state) {
|
||||
|
||||
var flavourComponent: FlavourComponent? = null
|
||||
var baitEffectsComponent: BaitEffectsComponent? = null
|
||||
var foodColourComponent: FoodColourComponent? = null
|
||||
var bites: Int = 0
|
||||
var ingredientComponent: IngredientComponent? = null
|
||||
|
||||
fun initializeFromItemStack(itemStack: ItemStack) {
|
||||
flavourComponent = itemStack.get(CobblemonItemComponents.FLAVOUR)
|
||||
baitEffectsComponent = itemStack.get(CobblemonItemComponents.BAIT_EFFECTS)
|
||||
foodColourComponent = itemStack.get(CobblemonItemComponents.FOOD_COLOUR)
|
||||
}
|
||||
ingredientComponent = itemStack.get(CobblemonItemComponents.INGREDIENT)
|
||||
if (isLure()) baitEffectsComponent = itemStack.get(CobblemonItemComponents.BAIT_EFFECTS)
|
||||
|
||||
override fun saveAdditional(
|
||||
tag: CompoundTag,
|
||||
registries: HolderLookup.Provider
|
||||
) {
|
||||
super.saveAdditional(tag, registries)
|
||||
|
||||
tag.putInt(DataKeys.CAKE_BITES, bites)
|
||||
flavourComponent?.let { component ->
|
||||
CobblemonItemComponents.FLAVOUR.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.CAKE_FLAVOUR, encodedTag)
|
||||
}
|
||||
}
|
||||
baitEffectsComponent?.let { component ->
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.CAKE_BAIT_EFFECTS, encodedTag)
|
||||
}
|
||||
}
|
||||
foodColourComponent?.let { component ->
|
||||
CobblemonItemComponents.FOOD_COLOUR.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.CAKE_FOOD_COLOUR, encodedTag)
|
||||
}
|
||||
flavourComponent?.let {
|
||||
getColourMixFromFlavours(it.getDominantFlavours())?.let {
|
||||
setTint(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(
|
||||
tag: CompoundTag,
|
||||
registries: HolderLookup.Provider
|
||||
) {
|
||||
super.loadAdditional(tag, registries)
|
||||
|
||||
bites = tag.getInt(DataKeys.CAKE_BITES)
|
||||
if (tag.contains(DataKeys.CAKE_FLAVOUR)) {
|
||||
CobblemonItemComponents.FLAVOUR.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.CAKE_FLAVOUR))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
flavourComponent = component
|
||||
}
|
||||
fun isLure(): Boolean {
|
||||
val block = blockState.block
|
||||
if (block is PokeSnackBlock) {
|
||||
return block.isLure
|
||||
}
|
||||
if (tag.contains(DataKeys.CAKE_BAIT_EFFECTS)) {
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.CAKE_BAIT_EFFECTS))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
baitEffectsComponent = component
|
||||
}
|
||||
}
|
||||
if (tag.contains(DataKeys.CAKE_FOOD_COLOUR)) {
|
||||
CobblemonItemComponents.FOOD_COLOUR.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.CAKE_FOOD_COLOUR))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
foodColourComponent = component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getUpdatePacket(): Packet<ClientGamePacketListener?>? {
|
||||
return ClientboundBlockEntityDataPacket.create(this)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag {
|
||||
val tag = CompoundTag()
|
||||
saveAdditional(tag, registries)
|
||||
return tag
|
||||
return false
|
||||
}
|
||||
|
||||
fun toItemStack(): ItemStack {
|
||||
val stack = ItemStack(this.blockState.block)
|
||||
if (stack.item == CobblemonItems.LURE_CAKE && baitEffectsComponent != null) {
|
||||
if (isLure() && baitEffectsComponent != null) {
|
||||
stack.set(CobblemonItemComponents.BAIT_EFFECTS, baitEffectsComponent)
|
||||
}
|
||||
if (flavourComponent != null) {
|
||||
stack.set(CobblemonItemComponents.FLAVOUR, flavourComponent)
|
||||
}
|
||||
if (foodColourComponent != null) {
|
||||
stack.set(CobblemonItemComponents.FOOD_COLOUR, foodColourComponent)
|
||||
}
|
||||
return stack
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine all the [SpawnBait.Effect] values from the [baitEffectsComponent] data.
|
||||
*/
|
||||
fun getBaitEffects(): List<Effect> {
|
||||
return baitEffectsComponent?.effects?.mapNotNull(SpawnBaitEffects::getFromIdentifier)?.flatMap { it.effects }.orEmpty()
|
||||
}
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registries)
|
||||
|
||||
flavourComponent?.let { component ->
|
||||
CobblemonItemComponents.FLAVOUR.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.FLAVOUR, encodedTag)
|
||||
}
|
||||
}
|
||||
|
||||
baitEffectsComponent?.let { component ->
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.BAIT_EFFECTS, encodedTag)
|
||||
}
|
||||
}
|
||||
|
||||
ingredientComponent?.let { component ->
|
||||
CobblemonItemComponents.INGREDIENT.codec()!!
|
||||
.encodeStart(NbtOps.INSTANCE, component)
|
||||
.result()
|
||||
.ifPresent { encodedTag ->
|
||||
tag.put(DataKeys.INGREDIENTS, encodedTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registries)
|
||||
|
||||
if (tag.contains(DataKeys.FLAVOUR)) {
|
||||
CobblemonItemComponents.FLAVOUR.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.FLAVOUR))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
flavourComponent = component
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.contains(DataKeys.BAIT_EFFECTS)) {
|
||||
CobblemonItemComponents.BAIT_EFFECTS.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.BAIT_EFFECTS))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
baitEffectsComponent = component
|
||||
}
|
||||
}
|
||||
|
||||
if (tag.contains(DataKeys.INGREDIENTS)) {
|
||||
CobblemonItemComponents.INGREDIENT.codec()
|
||||
?.parse(NbtOps.INSTANCE, tag.get(DataKeys.INGREDIENTS))
|
||||
?.result()
|
||||
?.ifPresent { component ->
|
||||
ingredientComponent = component
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -13,4 +13,6 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
class SweetIncenseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.INCENSE_SWEET, pos, state)
|
||||
class SweetIncenseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(
|
||||
null // CobblemonBlockEntities.INCENSE_SWEET // TODO after 1.7
|
||||
, pos, state)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.IntTag
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
|
||||
open class TintBlockEntity(type: BlockEntityType<*>?, blockPos: BlockPos, blockState: BlockState) : BlockEntity(type, blockPos, blockState) {
|
||||
companion object {
|
||||
const val TINT = "tint"
|
||||
}
|
||||
|
||||
var tint: Int? = null
|
||||
|
||||
override fun saveAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.saveAdditional(tag, registries)
|
||||
tint?.let { tag.put(TINT, IntTag.valueOf(it)) }
|
||||
}
|
||||
|
||||
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
|
||||
super.loadAdditional(tag, registries)
|
||||
if (tag.contains(TINT)) tint = tag.getInt(TINT)
|
||||
}
|
||||
|
||||
override fun getUpdateTag(registryLookup: HolderLookup.Provider): CompoundTag {
|
||||
return this.saveWithoutMetadata(registryLookup)
|
||||
}
|
||||
|
||||
fun getTint() = tint ?: 0xFFFFFF
|
||||
|
||||
fun setTint(tintValue: Int) {
|
||||
level?.let {
|
||||
tint = tintValue
|
||||
setChanged()
|
||||
it.blockEntityChanged(blockPos)
|
||||
it.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_ALL)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,19 +20,20 @@ import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientGeneralPlayerData
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.entity.TintBlockEntity
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
|
||||
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleOptionsRepository
|
||||
import com.cobblemon.mod.common.client.render.block.CandlePokeCakeBlockEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.ClientPlayerIcon
|
||||
import com.cobblemon.mod.common.client.render.DeferredRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.*
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.color.AprijuiceItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeBaitItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PokeSnackItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.PonigiriItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.color.SinisterTeaItemColorProvider
|
||||
import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
|
||||
@@ -59,6 +60,8 @@ import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexUsageContext
|
||||
import com.cobblemon.mod.common.util.isLookingAt
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.color.block.BlockColor
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.screens.MenuScreens
|
||||
import net.minecraft.client.gui.screens.Screen
|
||||
@@ -124,7 +127,7 @@ object CobblemonClient {
|
||||
|
||||
this.registerBlockEntityRenderers()
|
||||
registerBlockRenderTypes()
|
||||
//registerColors()
|
||||
registerColors()
|
||||
registerFlywheelRenderers()
|
||||
this.registerEntityRenderers()
|
||||
this.registerItemColors()
|
||||
@@ -196,16 +199,16 @@ object CobblemonClient {
|
||||
// .apply()
|
||||
}
|
||||
|
||||
/*
|
||||
fun registerColors() {
|
||||
this.implementation.registerBlockColors(BlockColorProvider { _, _, _, _ ->
|
||||
return@BlockColorProvider 0xE0A33A
|
||||
}, CobblemonBlocks.APRICORN_LEAVES)
|
||||
this.implementation.registerItemColors(ItemColorProvider { _, _ ->
|
||||
return@ItemColorProvider 0xE0A33A
|
||||
}, CobblemonItems.APRICORN_LEAVES)
|
||||
this.implementation.registerBlockColors(BlockColor { blockState, view, blockPos, tintIndex ->
|
||||
blockPos?.let { pos ->
|
||||
view?.getBlockEntity(pos)?.let { blockEntity ->
|
||||
if (blockEntity is TintBlockEntity) return@BlockColor blockEntity.getTint()
|
||||
}
|
||||
}
|
||||
return@BlockColor 0xFFFFFF
|
||||
}, CobblemonBlocks.POKE_SNACK, CobblemonBlocks.POKE_CAKE)
|
||||
}
|
||||
*/
|
||||
|
||||
private fun registerBlockRenderTypes() {
|
||||
|
||||
@@ -289,10 +292,10 @@ object CobblemonClient {
|
||||
CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_SAPLING,
|
||||
CobblemonBlocks.POTTED_SACCHARINE_SAPLING,
|
||||
CobblemonBlocks.LURE_CAKE,
|
||||
CobblemonBlocks.POKE_CAKE,
|
||||
CobblemonBlocks.POKE_SNACK,
|
||||
CobblemonBlocks.LECTERN,
|
||||
CobblemonBlocks.CAMPFIRE,
|
||||
CobblemonBlocks.SOUL_CAMPFIRE,
|
||||
CobblemonBlocks.BLACK_CAMPFIRE_POT,
|
||||
CobblemonBlocks.BLUE_CAMPFIRE_POT,
|
||||
CobblemonBlocks.GREEN_CAMPFIRE_POT,
|
||||
@@ -349,12 +352,7 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LECTERN, ::LecternBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.CAMPFIRE, ::CampfireBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.LURE_CAKE, ::CakeBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_CAKE, ::CakeBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(
|
||||
CobblemonBlockEntities.CANDLE_POKE_CAKE,
|
||||
::CandlePokeCakeBlockEntityRenderer
|
||||
)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.POKE_SNACK, ::PokeSnackBlockEntityRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -383,9 +381,8 @@ object CobblemonClient {
|
||||
|
||||
private fun registerItemColors() {
|
||||
implementation.registerItemColors(AprijuiceItemColorProvider, *CobblemonItems.aprijuices.toTypedArray())
|
||||
implementation.registerItemColors(PokeSnackItemColorProvider, CobblemonItems.POKE_SNACK, CobblemonItems.POKE_CAKE)
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_BAIT)
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.LURE_CAKE)
|
||||
implementation.registerItemColors(PokeBaitItemColorProvider, CobblemonItems.POKE_CAKE)
|
||||
implementation.registerItemColors(PonigiriItemColorProvider, CobblemonItems.PONIGIRI)
|
||||
implementation.registerItemColors(SinisterTeaItemColorProvider, CobblemonItems.SINISTER_TEA)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.aspectParticleMap
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.ParticleData
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.RidingBehaviourState
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnClient
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.riding.behaviour.types.air.*
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.composite.CompositeState
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.HorseBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.MinekartBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.land.VehicleBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BoatBehaviour
|
||||
import com.cobblemon.mod.common.api.riding.behaviour.types.liquid.BurstBehaviour
|
||||
@@ -99,13 +100,14 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
|
||||
// Don't render if duration config set to 0 or less
|
||||
if (maxDurationFrames <= 0F) return
|
||||
|
||||
val rideBehaviourSettings = riddenEntity.ridingBehaviourSettings
|
||||
val rideBehaviourKey = rideBehaviourSettings?.key
|
||||
val rideBehaviourSettings = riddenEntity.ridingController?.context?.settings ?: return
|
||||
val ridingBehaviourState = riddenEntity.ridingController?.context?.state ?: return
|
||||
val rideBehaviourKey = rideBehaviourSettings.key
|
||||
|
||||
// Update current ride behaviour if changed
|
||||
// If composite, get active behaviour instead
|
||||
if (rideBehaviourKey == CompositeBehaviour.KEY) {
|
||||
val activeBehaviour = (riddenEntity.ridingState as CompositeState).activeBehaviour.get()
|
||||
val activeBehaviour = (ridingBehaviourState as CompositeState).activeBehaviour.get()
|
||||
if (currentBehaviourKey != activeBehaviour) {
|
||||
currentBehaviourKey = activeBehaviour
|
||||
resetOverlayState()
|
||||
@@ -133,25 +135,34 @@ class RideControlsOverlay : Gui(Minecraft.getInstance()) {
|
||||
// Configure what controls to show for each behaviour
|
||||
when (currentBehaviourKey) {
|
||||
BirdBehaviour.KEY -> {}
|
||||
BoatBehaviour.KEY -> {}
|
||||
BurstBehaviour.KEY -> {}
|
||||
DolphinBehaviour.KEY -> {}
|
||||
GliderBehaviour.KEY -> {}
|
||||
HelicopterBehaviour.KEY -> {}
|
||||
HorseBehaviour.KEY -> {
|
||||
showVerticalMouse = false
|
||||
disableHorizontalMovementKeys = true
|
||||
showSneakKey = false
|
||||
}
|
||||
BoatBehaviour.KEY -> {}
|
||||
GliderBehaviour.KEY -> {}
|
||||
HelicopterBehaviour.KEY -> {}
|
||||
HoverBehaviour.KEY -> {
|
||||
showVerticalMouse = false
|
||||
}
|
||||
JetBehaviour.KEY -> {
|
||||
showMovementKeys = false
|
||||
showSneakKey = false
|
||||
showJumpKey = false
|
||||
}
|
||||
BurstBehaviour.KEY -> {}
|
||||
VehicleBehaviour.KEY -> {}
|
||||
HoverBehaviour.KEY -> {
|
||||
MinekartBehaviour.KEY -> {
|
||||
showVerticalMouse = false
|
||||
showHorizontalMouse = false
|
||||
showSneakKey = false
|
||||
}
|
||||
RocketBehaviour.KEY -> {
|
||||
showVerticalMouse = false
|
||||
showHorizontalMouse = false
|
||||
}
|
||||
VehicleBehaviour.KEY -> {}
|
||||
}
|
||||
|
||||
val centerX = minecraft.window.guiScaledWidth / 2
|
||||
|
||||
+2
-2
@@ -201,10 +201,10 @@ class BattleMoveSelection(
|
||||
val move = moveTiles.find { it.isHovered(mouseX, mouseY) }
|
||||
val gimmick = gimmickButtons.find { it.isHovered(mouseX, mouseY) }
|
||||
if (move != null) {
|
||||
if(this.request.activePokemon.getFormat().battleType.pokemonPerSide == 1) {
|
||||
if(request.activePokemon.getFormat().battleType.pokemonPerSide == 1) {
|
||||
move.onClick()
|
||||
} else {
|
||||
battleGUI.changeActionSelection(BattleTargetSelection(battleGUI, request, move.move))
|
||||
battleGUI.changeActionSelection(BattleTargetSelection(battleGUI, request, move.move, move.response.gimmickID, move.move.gimmickMove))
|
||||
playDownSound(Minecraft.getInstance().soundManager)
|
||||
}
|
||||
return true
|
||||
|
||||
+7
-3
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.font
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.InBattleGimmickMove
|
||||
import com.cobblemon.mod.common.battles.InBattleMove
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
@@ -43,7 +44,9 @@ import kotlin.math.sin
|
||||
class BattleTargetSelection(
|
||||
battleGUI: BattleGUI,
|
||||
request: SingleActionRequest,
|
||||
val move: InBattleMove
|
||||
val move: InBattleMove,
|
||||
val gimmickID: String?,
|
||||
gimmickMove: InBattleGimmickMove?
|
||||
) : BattleActionSelection(
|
||||
battleGUI = battleGUI,
|
||||
request = request,
|
||||
@@ -81,9 +84,10 @@ class BattleTargetSelection(
|
||||
|
||||
val targets = request.activePokemon.getAllActivePokemon()
|
||||
|
||||
val targetType = if (gimmickID != null && gimmickMove != null) gimmickMove.target else move.target
|
||||
val backButton = BattleBackButton(x + 9F, Minecraft.getInstance().window.guiScaledHeight - 22F)
|
||||
val selectableTargetList = move.target.targetList(request.activePokemon)
|
||||
val multiTargetList = if(selectableTargetList == null) request.activePokemon.getMultiTargetList(move.target) else null
|
||||
val selectableTargetList = targetType.targetList(request.activePokemon)
|
||||
val multiTargetList = if(selectableTargetList == null) request.activePokemon.getMultiTargetList(targetType) else null
|
||||
|
||||
val baseTiles = targets.mapIndexed { index, target ->
|
||||
val isAlly = target.isAllied(request.activePokemon)
|
||||
|
||||
+26
-13
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.client.gui.cookingpot
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
|
||||
import com.cobblemon.mod.common.block.campfirepot.SeasoningSlot
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.IS_LID_OPEN_INDEX
|
||||
import com.cobblemon.mod.common.block.entity.CampfireBlockEntity.Companion.PREVIEW_ITEM_SLOT
|
||||
import com.cobblemon.mod.common.integration.jei.CobblemonJeiPlugin
|
||||
import com.cobblemon.mod.common.integration.jei.cooking.CampfirePotRecipeCategory
|
||||
import com.cobblemon.mod.common.item.crafting.CookingPotRecipe
|
||||
@@ -39,6 +39,7 @@ import net.minecraft.world.inventory.ClickType
|
||||
import net.minecraft.world.inventory.RecipeBookMenu
|
||||
import net.minecraft.world.inventory.Slot
|
||||
import net.minecraft.world.item.crafting.CraftingInput
|
||||
import java.util.*
|
||||
import kotlin.math.ceil
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@@ -174,24 +175,36 @@ class CookingPotScreen(
|
||||
this.recipeBookComponent.renderGhostRecipe(context, this.leftPos, this.topPos, true, partialTicks)
|
||||
}
|
||||
|
||||
val resultSlot = menu.slots[menu.resultSlotIndex]
|
||||
val optionalRecipe = menu.currentActiveRecipe
|
||||
if (optionalRecipe != null && !optionalRecipe.value.result.isEmpty && !resultSlot.hasItem()) {
|
||||
val resultItem = menu.previewItem
|
||||
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 0.5F)
|
||||
|
||||
context.renderFakeItem(resultItem, leftPos + resultSlot.x, topPos + resultSlot.y);
|
||||
context.renderItemDecorations(font, resultItem, leftPos + resultSlot.x, topPos + resultSlot.y)
|
||||
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F)
|
||||
RenderSystem.disableBlend()
|
||||
|
||||
if (isHovering(resultSlot.x, resultSlot.y, 16, 16, mouseX.toDouble(), mouseY.toDouble()) && menu.carried.isEmpty) {
|
||||
context.renderTooltip(font, getTooltipFromContainerItem(resultItem), resultItem.tooltipImage, mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
this.renderTooltip(context, mouseX, mouseY)
|
||||
this.recipeBookComponent.renderTooltip(context, this.leftPos, this.topPos, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun renderSlot(guiGraphics: GuiGraphics, slot: Slot) {
|
||||
if (slot.index != PREVIEW_ITEM_SLOT) {
|
||||
super.renderSlot(guiGraphics, slot)
|
||||
return
|
||||
if (slot is SeasoningSlot && !menu.carried.isEmpty && !slot.mayPlace(menu.carried)) {
|
||||
val x = slot.x
|
||||
val y = slot.y
|
||||
guiGraphics.fill(x,y,x+16,y+16,822018048)
|
||||
}
|
||||
|
||||
RenderSystem.enableBlend()
|
||||
RenderSystem.setShaderColor(1F, 1F, 1F, 0.5F)
|
||||
|
||||
guiGraphics.renderFakeItem(slot.item, slot.x, slot.y);
|
||||
guiGraphics.renderItemDecorations(minecraft!!.font, slot.item, slot.x, slot.y)
|
||||
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F)
|
||||
RenderSystem.disableBlend()
|
||||
super.renderSlot(guiGraphics, slot)
|
||||
}
|
||||
|
||||
override fun recipesUpdated() {
|
||||
|
||||
+8
-6
@@ -146,19 +146,19 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
|
||||
val maxStamina = maxStaminaInput.value.toIntOrNull() ?: getRidingStatRange(RidingStat.STAMINA).last
|
||||
|
||||
if (minSpeed < maxSpeed) {
|
||||
vehicle.rideProp.stats[RidingStat.SPEED]?.ranges?.put(ridingStyle, minSpeed..maxSpeed)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.SPEED, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minAcceleration < maxAcceleration) {
|
||||
vehicle.rideProp.stats[RidingStat.ACCELERATION]?.ranges?.put(ridingStyle, minAcceleration..maxAcceleration)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.ACCELERATION, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minSkill < maxSkill) {
|
||||
vehicle.rideProp.stats[RidingStat.SKILL]?.ranges?.put(ridingStyle, minSkill..maxSkill)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.SKILL, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minJump < maxJump) {
|
||||
vehicle.rideProp.stats[RidingStat.JUMP]?.ranges?.put(ridingStyle, minJump..maxJump)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.JUMP, minSpeed..maxSpeed)
|
||||
}
|
||||
if (minStamina < maxStamina) {
|
||||
vehicle.rideProp.stats[RidingStat.STAMINA]?.ranges?.put(ridingStyle, minStamina..maxStamina)
|
||||
vehicle.rideProp.behaviours?.get(ridingStyle)?.stats?.put(RidingStat.STAMINA, minSpeed..maxSpeed)
|
||||
}
|
||||
|
||||
CobblemonNetwork.sendToServer(
|
||||
@@ -198,7 +198,9 @@ class RidingStatsDebugGUI(val vehicle: PokemonEntity) : Screen(lang("ui.debug.ri
|
||||
}
|
||||
|
||||
fun getRidingStatRange(ridingStat: RidingStat): IntRange {
|
||||
return vehicle.rideProp.stats[ridingStat]?.ranges?.get(ridingStyle) ?: 0..0
|
||||
// TODO
|
||||
return 0..0
|
||||
// return vehicle.rideProp.stats[ridingStat]?.ranges?.get(ridingStyle) ?: 0..0
|
||||
}
|
||||
|
||||
fun getScaledWidth() = Minecraft.getInstance().window.guiScaledWidth
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@ import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.Button
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
@@ -25,7 +26,7 @@ class SummaryTab(
|
||||
val label: MutableComponent? = null,
|
||||
val icon: ResourceLocation? = null,
|
||||
onPress: OnPress
|
||||
): Button(pX, pY, WIDTH, HEIGHT, label, onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
): Button(pX, pY, WIDTH, HEIGHT, label ?: Component.empty(), onPress, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
companion object {
|
||||
private const val WIDTH = 39
|
||||
private const val HEIGHT = 13
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.effect
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.PokeSnackBlockParticlesPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
|
||||
object PokeSnackBlockParticlesHandler : ClientNetworkPacketHandler<PokeSnackBlockParticlesPacket> {
|
||||
override fun handle(packet: PokeSnackBlockParticlesPacket, client: Minecraft) {
|
||||
val level = Minecraft.getInstance().level ?: return
|
||||
|
||||
val blockState = level.getBlockState(packet.blockPos)
|
||||
val block = blockState.block
|
||||
if (block is PokeSnackBlock) {
|
||||
block.spawnEatParticles(level, packet.blockPos)
|
||||
}
|
||||
|
||||
val entityPos = packet.entityPos
|
||||
if (entityPos != null) {
|
||||
val random = level.random
|
||||
repeat(5) {
|
||||
level.addParticle(
|
||||
ParticleTypes.POOF,
|
||||
entityPos.x + random.nextDouble(),
|
||||
entityPos.y + random.nextDouble(),
|
||||
entityPos.z + random.nextDouble(),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -32,7 +32,7 @@ object SaccharineLogBlockParticlesHandler : ClientNetworkPacketHandler<Saccharin
|
||||
val entityPos = packet.entityPos
|
||||
if (entityPos != null) {
|
||||
val random = level.random
|
||||
repeat(10) {
|
||||
repeat(5) {
|
||||
level.addParticle(
|
||||
ParticleTypes.POOF,
|
||||
entityPos.x + random.nextDouble(),
|
||||
|
||||
+4
-2
@@ -21,8 +21,10 @@ object ClientboundUpdateRidingStateHandler : ClientNetworkPacketHandler<Clientbo
|
||||
if (entity !is PokemonEntity) return@executeIfPossible
|
||||
if (entity.controllingPassenger == player) return@executeIfPossible
|
||||
val buffer = packet.data ?: return@executeIfPossible
|
||||
if (entity.riding?.key != packet.behaviour) return@executeIfPossible
|
||||
entity.ridingState?.decode(buffer)
|
||||
if (entity.ridingController?.context?.settings?.key != packet.behaviour) {
|
||||
entity.ridingController?.changeBehaviour(packet.behaviour)
|
||||
}
|
||||
entity.ridingController?.context?.state?.decode(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.net.riding
|
||||
|
||||
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
|
||||
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.player.RemotePlayer
|
||||
|
||||
object ClientboundUpdateDriverInputHandler : ClientNetworkPacketHandler<ClientboundUpdateDriverInputPacket> {
|
||||
override fun handle(packet: ClientboundUpdateDriverInputPacket, client: Minecraft) {
|
||||
client.executeIfPossible {
|
||||
val level = client.level ?: return@executeIfPossible
|
||||
val entity = level.getEntity(packet.entityId)
|
||||
if (entity is RemotePlayer) {
|
||||
entity.xxa = packet.driverInput.x
|
||||
entity.zza = packet.driverInput.z
|
||||
entity.jumping = packet.driverInput.y == 1.0f
|
||||
entity.isShiftKeyDown = packet.driverInput.y == -1.0f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-295
@@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.block.CakeBlock
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.cobblemon.mod.common.item.components.FoodColourComponent
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import com.mojang.math.Axis
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.inventory.InventoryMenu.BLOCK_ATLAS
|
||||
import org.joml.Vector3d
|
||||
|
||||
open class CakeBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<CakeBlockEntity> {
|
||||
|
||||
companion object {
|
||||
const val PIXEL_SHIFT = 1.0 / 16.0
|
||||
const val WHITE_OPAQUE_COLOR = 0xFFFFFFFF.toInt()
|
||||
|
||||
const val ATLAS_START = 0.0f
|
||||
const val ATLAS_END = 1.0f
|
||||
const val HEIGHT = 0.5f
|
||||
const val TOP_LAYER_HEIGHT = 0.5625f
|
||||
const val BOTTOM_LAYER_HEIGHT = 0.0f
|
||||
|
||||
object DefaultCakeLayout : CakeLayout {
|
||||
override val topLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_top_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_top_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val sideLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val bottomLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
|
||||
)
|
||||
override val innerLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
}
|
||||
|
||||
object HeartCakeLayout : CakeLayout {
|
||||
override val topLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_top_layer4", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_top_layer5", LayerColor.PRIMARY),
|
||||
)
|
||||
override val sideLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_side_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_side_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_side_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
override val bottomLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_bottom_layer1", LayerColor.TERTIARY),
|
||||
)
|
||||
override val innerLayers: List<CakeTexture> = mutableListOf(
|
||||
CakeTexture("poke_snack_inner_layer1", LayerColor.TERTIARY),
|
||||
CakeTexture("poke_snack_inner_layer2", LayerColor.SECONDARY),
|
||||
CakeTexture("poke_snack_inner_layer3", LayerColor.PRIMARY),
|
||||
)
|
||||
}
|
||||
|
||||
val CAKE_LAYOUTS = listOf(DefaultCakeLayout, HeartCakeLayout)
|
||||
|
||||
fun loadTexture(textureName: String): TextureAtlasSprite {
|
||||
return Minecraft.getInstance().getTextureAtlas(BLOCK_ATLAS).apply(cobblemonResource(textureName))
|
||||
}
|
||||
}
|
||||
|
||||
interface CakeLayout {
|
||||
val topLayers: List<CakeTexture>
|
||||
val sideLayers: List<CakeTexture>
|
||||
val bottomLayers: List<CakeTexture>
|
||||
val innerLayers: List<CakeTexture>
|
||||
}
|
||||
|
||||
class CakeTexture(textureName: String, val color: LayerColor) {
|
||||
|
||||
val texture = loadTexture("block/food/${textureName}")
|
||||
|
||||
fun getColor(primaryColor: Int, secondaryColor: Int, tertiaryColor: Int): Int {
|
||||
return when (color) {
|
||||
LayerColor.PRIMARY -> primaryColor
|
||||
LayerColor.SECONDARY -> secondaryColor
|
||||
LayerColor.TERTIARY -> tertiaryColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class LayerColor {
|
||||
PRIMARY, SECONDARY, TERTIARY
|
||||
}
|
||||
|
||||
override fun render(
|
||||
blockEntity: CakeBlockEntity,
|
||||
partialTick: Float,
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
packedOverlay: Int
|
||||
) {
|
||||
val foodColourComponent = blockEntity.foodColourComponent
|
||||
renderCake(blockEntity, foodColourComponent, poseStack, bufferSource, packedLight, blockEntity.bites)
|
||||
}
|
||||
|
||||
fun renderCake(
|
||||
blockEntity: CakeBlockEntity,
|
||||
foodColourComponent: FoodColourComponent?,
|
||||
poseStack: PoseStack,
|
||||
multiBufferSource: MultiBufferSource,
|
||||
light: Int,
|
||||
bites: Int,
|
||||
) {
|
||||
val seedString = foodColourComponent?.colours?.joinToString()
|
||||
val random = Random(seedString.hashCode())
|
||||
val cakeLayout: CakeLayout = CAKE_LAYOUTS[random.nextInt(CAKE_LAYOUTS.size)]
|
||||
|
||||
val visualStages = 7f
|
||||
val maxBites = blockEntity.maxBites.toFloat()
|
||||
val bitesPerStage = ((maxBites + 1) / visualStages).coerceAtLeast(1f)
|
||||
val visualStage = (bites / bitesPerStage).toInt().coerceIn(0, visualStages.toInt() - 1)
|
||||
val biteOffset = (15f - 2f * visualStage) / 16f
|
||||
val clippedWidth = (ATLAS_END - ATLAS_START) * biteOffset
|
||||
val topLayer = cakeLayout.topLayers.first().texture
|
||||
val uClipped = topLayer.u0 + (topLayer.u1 - topLayer.u0) * biteOffset
|
||||
|
||||
val vertexConsumer = multiBufferSource.getBuffer(RenderType.cutout())
|
||||
val primaryColor = foodColourComponent?.colours?.getOrNull(0)?.textureDiffuseColor ?: WHITE_OPAQUE_COLOR
|
||||
val secondaryColor = foodColourComponent?.colours?.getOrNull(1)?.textureDiffuseColor ?: WHITE_OPAQUE_COLOR
|
||||
val tertiaryColor = foodColourComponent?.colours?.getOrNull(2)?.textureDiffuseColor ?: WHITE_OPAQUE_COLOR
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.5, 0.5, 0.5)
|
||||
val facing = blockEntity.blockState.getValue(CakeBlock.FACING)
|
||||
val angle = when (facing) {
|
||||
Direction.NORTH -> 180f
|
||||
Direction.SOUTH -> 0f
|
||||
Direction.WEST -> 90f
|
||||
Direction.EAST -> -90f
|
||||
else -> 0f
|
||||
}
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(angle))
|
||||
|
||||
poseStack.translate(-0.5, -0.5, -0.5)
|
||||
|
||||
poseStack.pushPose()
|
||||
for (layer in cakeLayout.topLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, TOP_LAYER_HEIGHT, ATLAS_START, clippedWidth, TOP_LAYER_HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
poseStack.popPose()
|
||||
|
||||
val translations = arrayOf(
|
||||
Vector3d(-1.0, 1.0, 0.5 - PIXEL_SHIFT),
|
||||
Vector3d(0.0, 1.0, 0.5 - PIXEL_SHIFT),
|
||||
Vector3d(0.0, 1.0, -0.5 - PIXEL_SHIFT),
|
||||
Vector3d(-1.0, 1.0, -0.5 - PIXEL_SHIFT),
|
||||
)
|
||||
|
||||
val rotationsY = arrayOf(
|
||||
Axis.YP.rotationDegrees(90f),
|
||||
Axis.YP.rotationDegrees(0f),
|
||||
Axis.YP.rotationDegrees(-90f),
|
||||
Axis.YP.rotationDegrees(-180f)
|
||||
)
|
||||
|
||||
val cakeIsFull = bites == 0
|
||||
|
||||
for (i in translations.indices) {
|
||||
poseStack.pushPose()
|
||||
poseStack.mulPose(rotationsY[i])
|
||||
poseStack.translate(translations[i].x, translations[i].y, translations[i].z)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(90f))
|
||||
|
||||
if (i == 0 && !cakeIsFull) {
|
||||
poseStack.translate(0.0, -visualStage * PIXEL_SHIFT * 2, 0.0)
|
||||
for (layer in cakeLayout.innerLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 0 || i == 1) {
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, clippedWidth, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 2) {
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
} else if (i == 3) {
|
||||
val clippedStartX = ATLAS_START + (ATLAS_END - ATLAS_START) * (1 - biteOffset)
|
||||
|
||||
for (layer in cakeLayout.sideLayers) {
|
||||
val texture = layer.texture
|
||||
val clippedUStart = texture.u0 + (texture.u1 - texture.u0) * (1 - biteOffset)
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
clippedStartX, HEIGHT, ATLAS_START, ATLAS_END, HEIGHT, ATLAS_END,
|
||||
clippedUStart, texture.v0, texture.u1, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.5, 0.0, 0.5)
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(180f))
|
||||
poseStack.translate(-0.5, 0.0, -0.5)
|
||||
|
||||
for (layer in cakeLayout.bottomLayers) {
|
||||
val texture = layer.texture
|
||||
drawQuad(
|
||||
vertexConsumer, poseStack,
|
||||
ATLAS_START, BOTTOM_LAYER_HEIGHT, ATLAS_START, clippedWidth, BOTTOM_LAYER_HEIGHT, ATLAS_END,
|
||||
texture.u0, texture.v0, uClipped, texture.v1,
|
||||
light, layer.getColor(primaryColor, secondaryColor, tertiaryColor)
|
||||
)
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
private fun drawQuad(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x0: Float, y0: Float, z0: Float,
|
||||
x1: Float, y1: Float, z1: Float,
|
||||
u0: Float, v0: Float, u1: Float, v1: Float,
|
||||
packedLight: Int, color: Int,
|
||||
) {
|
||||
drawVertex(builder, poseStack, x0, y0, z0, u0, v0, packedLight, color)
|
||||
drawVertex(builder, poseStack, x0, y1, z1, u0, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y1, z1, u1, v1, packedLight, color)
|
||||
drawVertex(builder, poseStack, x1, y0, z0, u1, v0, packedLight, color)
|
||||
}
|
||||
|
||||
private fun drawVertex(
|
||||
builder: VertexConsumer,
|
||||
poseStack: PoseStack,
|
||||
x: Float, y: Float, z: Float,
|
||||
u: Float, v: Float,
|
||||
packedLight: Int, color: Int
|
||||
) {
|
||||
builder.addVertex(poseStack.last().pose(), x, y, z)
|
||||
.setColor(color)
|
||||
.setUv(u, v)
|
||||
.setLight(packedLight)
|
||||
.setNormal(0f, 1f, 0f)
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.CANDLE_COLOR
|
||||
import com.cobblemon.mod.common.block.CandlePokeCakeBlock.Companion.getCandleByMapColorId
|
||||
import com.cobblemon.mod.common.block.entity.CakeBlockEntity
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
|
||||
class CandlePokeCakeBlockEntityRenderer(
|
||||
ctx: BlockEntityRendererProvider.Context,
|
||||
) : CakeBlockEntityRenderer(ctx) {
|
||||
|
||||
override fun render(
|
||||
blockEntity: CakeBlockEntity,
|
||||
partialTick: Float,
|
||||
poseStack: PoseStack,
|
||||
bufferSource: MultiBufferSource,
|
||||
packedLight: Int,
|
||||
packedOverlay: Int
|
||||
) {
|
||||
super.render(blockEntity, partialTick, poseStack, bufferSource, packedLight, packedOverlay)
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, TOP_LAYER_HEIGHT.toDouble(), 0.0)
|
||||
|
||||
val candleBlock = getCandleByMapColorId(blockEntity.blockState.getValue(CANDLE_COLOR))
|
||||
Minecraft.getInstance().blockRenderer.renderSingleBlock(
|
||||
candleBlock.defaultBlockState().setValue(LIT, blockEntity.blockState.getValue(LIT)),
|
||||
poseStack,
|
||||
bufferSource,
|
||||
packedLight,
|
||||
packedOverlay
|
||||
)
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.api.berry.Berries
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock.Companion.CANDLE
|
||||
import com.cobblemon.mod.common.block.PokeSnackBlock.Companion.getCandleById
|
||||
import com.cobblemon.mod.common.block.entity.PokeSnackBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.atlas.CobblemonAtlases.BERRY_SPRITE_ATLAS
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.setPosition
|
||||
import com.cobblemon.mod.common.util.math.geometry.Axis
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.LIT
|
||||
|
||||
open class PokeSnackBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) : BlockEntityRenderer<PokeSnackBlockEntity> {
|
||||
override fun render(blockEntity: PokeSnackBlockEntity, partialTick: Float, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||
renderCandle(blockEntity, poseStack, bufferSource, packedLight, packedOverlay)
|
||||
renderBerries(blockEntity, poseStack, bufferSource, packedLight, packedOverlay)
|
||||
}
|
||||
|
||||
fun renderCandle(blockEntity: PokeSnackBlockEntity, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||
val candleId = blockEntity.blockState.getValue(CANDLE)
|
||||
|
||||
if (candleId > 0) {
|
||||
val candleBlock = getCandleById(blockEntity.blockState.getValue(CANDLE))
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, PokeSnackBlock.CAKE_HEIGHT, 0.0)
|
||||
Minecraft.getInstance().blockRenderer.renderSingleBlock(
|
||||
candleBlock.defaultBlockState().setValue(LIT, blockEntity.blockState.getValue(LIT)),
|
||||
poseStack,
|
||||
bufferSource,
|
||||
packedLight,
|
||||
packedOverlay
|
||||
)
|
||||
poseStack.popPose()
|
||||
}
|
||||
}
|
||||
|
||||
fun renderBerries(blockEntity: PokeSnackBlockEntity, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||
// RenderType.entityCutoutNoCull for shading, as shading is absent in RenderType CobblemonRenderLayers.BERRY_LAYER
|
||||
val buffer = bufferSource.getBuffer(
|
||||
RenderType.entityCutoutNoCull(BERRY_SPRITE_ATLAS.textureAtlas.location())
|
||||
)
|
||||
|
||||
val berryIngredients = (blockEntity.ingredientComponent?.ingredientIds?.take(3) ?: listOf())
|
||||
.mapNotNull { Berries.getByIdentifier(it) }
|
||||
|
||||
berryIngredients.forEachIndexed { index, berry ->
|
||||
val model = BerryModelRepository.modelOf(berry.fruitModelIdentifier) ?: return
|
||||
|
||||
when (berryIngredients.size) {
|
||||
1 -> {
|
||||
val pos = berry.pokeSnackPositionings[0].position
|
||||
|
||||
model.setPosition(Axis.X_AXIS.ordinal, 8F)
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, 8F)
|
||||
|
||||
model.setRotation(
|
||||
Math.toRadians(180.0).toFloat(),
|
||||
Math.toRadians(0.0).toFloat(),
|
||||
Math.toRadians(0.0).toFloat()
|
||||
)
|
||||
}
|
||||
2 -> {
|
||||
val pos = berry.pokeSnackPositionings[0].position
|
||||
val rot = berry.pokeSnackPositionings[0].rotation
|
||||
val isFirst = index == 0
|
||||
|
||||
model.setPosition(Axis.X_AXIS.ordinal, pos.x.toFloat())
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, if (isFirst) pos.z.toFloat() else (16F - pos.z.toFloat()))
|
||||
|
||||
model.setRotation(
|
||||
Math.toRadians(
|
||||
if (isFirst) rot.x else (180.0 + (180.0 - rot.x))
|
||||
).toFloat(),
|
||||
Math.toRadians(rot.y).toFloat(),
|
||||
Math.toRadians(rot.z).toFloat()
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val pos = berry.pokeSnackPositionings[index].position
|
||||
val rot = berry.pokeSnackPositionings[index].rotation
|
||||
|
||||
model.setPosition(Axis.X_AXIS.ordinal, pos.x.toFloat())
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, pos.z.toFloat())
|
||||
|
||||
model.setRotation(
|
||||
Math.toRadians(rot.x).toFloat(),
|
||||
Math.toRadians(rot.y).toFloat(),
|
||||
Math.toRadians(rot.z).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
model.render(poseStack, buffer, packedLight, packedOverlay)
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.color
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.api.cooking.getColourMixFromFlavours
|
||||
import net.minecraft.client.color.item.ItemColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object PokeSnackItemColorProvider : ItemColor {
|
||||
private const val BASE_LAYER_INDEX = 0
|
||||
|
||||
override fun getColor(stack: ItemStack, layer: Int): Int {
|
||||
val flavourComponent = stack.get(CobblemonItemComponents.FLAVOUR) ?: return -1
|
||||
if (layer == BASE_LAYER_INDEX) {
|
||||
val colorMix = getColourMixFromFlavours(flavourComponent.getDominantFlavours())
|
||||
if (colorMix != null) return colorMix
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -32,13 +32,10 @@ object MountedLivingEntityRenderer {
|
||||
) {
|
||||
if (entity.vehicle !is Rideable) return
|
||||
val matrix = stack.last().pose()
|
||||
|
||||
val seatIndex = pokemon.passengers.indexOf(entity)
|
||||
val seat = pokemon.seats[seatIndex]
|
||||
val delegate = pokemon.delegate as PokemonClientDelegate
|
||||
val locator = delegate.locatorStates[seat.locator]
|
||||
val locator = delegate.locatorStates[delegate.getSeatLocator(entity)]
|
||||
|
||||
//Positions player
|
||||
//Positions entity
|
||||
if (locator != null) {
|
||||
MountedPokemonAnimationRenderController.setup(pokemon, partialTicks)
|
||||
|
||||
|
||||
+7
-7
@@ -113,13 +113,13 @@ object CobblemonModelPredicateRegistry {
|
||||
"minecraft:brown_mushroom" -> 0.07f
|
||||
"minecraft:pumpkin" -> 0.08f
|
||||
"cobblemon:rice" -> 0.09f
|
||||
"minecraft:raw_chicken" -> 0.10f
|
||||
"minecraft:raw_cod" -> 0.11f
|
||||
"minecraft:raw_mutton" -> 0.12f
|
||||
"minecraft:raw_porkchop" -> 0.13f
|
||||
"minecraft:raw_rabbit" -> 0.14f
|
||||
"minecraft:raw_salmon" -> 0.15f
|
||||
"minecraft:raw_beef" -> 0.16f
|
||||
"minecraft:chicken" -> 0.10f
|
||||
"minecraft:cod" -> 0.11f
|
||||
"minecraft:mutton" -> 0.12f
|
||||
"minecraft:porkchop" -> 0.13f
|
||||
"minecraft:rabbit" -> 0.14f
|
||||
"minecraft:salmon" -> 0.15f
|
||||
"minecraft:beef" -> 0.16f
|
||||
"minecraft:egg" -> 0.17f
|
||||
"minecraft:poisonous_potato" -> 0.18f
|
||||
"minecraft:rotten_flesh" -> 0.19f
|
||||
|
||||
+44
-1
@@ -8,17 +8,26 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.render.models.blockbench
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel.Companion.getOrMakeShard
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexFormat
|
||||
import net.minecraft.client.model.EntityModel
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import org.apache.commons.lang3.tuple.Triple
|
||||
|
||||
/**
|
||||
* A wrapping around a [PosableModel] that presents as an [EntityModel]. This is used to continue using
|
||||
@@ -29,13 +38,46 @@ import net.minecraft.resources.ResourceLocation
|
||||
* @since January 5th, 2024
|
||||
*/
|
||||
abstract class PosableEntityModel<T : Entity>(
|
||||
renderTypeFunc: (ResourceLocation) -> RenderType = RenderType::entityCutout
|
||||
renderTypeFunc: (ResourceLocation) -> RenderType = {
|
||||
val textures = ImmutableList.of(
|
||||
Triple.of(it, false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
)
|
||||
makeLayer(RenderStateShard.MultiTextureStateShard(textures), false, false, false)
|
||||
}
|
||||
) : EntityModel<T>(renderTypeFunc) {
|
||||
val context: RenderContext = RenderContext().also {
|
||||
it.put(RenderContext.RENDER_STATE, RenderContext.RenderState.WORLD)
|
||||
}
|
||||
lateinit var posableModel: PosableModel
|
||||
|
||||
companion object {
|
||||
fun makeLayer(texture: RenderStateShard.EmptyTextureStateShard, emissive: Boolean, translucent: Boolean, translucentCull: Boolean): RenderType {
|
||||
val multiPhaseParameters: RenderType.CompositeState = RenderType.CompositeState.builder()
|
||||
.setShaderState(getOrMakeShard() as RenderStateShard.ShaderStateShard)
|
||||
.setTextureState(texture)
|
||||
.setTransparencyState(if (translucent) RenderStateShard.TRANSLUCENT_TRANSPARENCY else RenderStateShard.NO_TRANSPARENCY)
|
||||
.setLightmapState(if (!emissive) RenderStateShard.LIGHTMAP else RenderStateShard.LightmapStateShard(false))
|
||||
.setCullState(RenderStateShard.CULL)
|
||||
.setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE)
|
||||
.setOverlayState(RenderStateShard.OVERLAY)
|
||||
.createCompositeState(false)
|
||||
|
||||
return RenderType.create(
|
||||
"cobblemon_entity_layer",
|
||||
DefaultVertexFormat.NEW_ENTITY,
|
||||
VertexFormat.Mode.QUADS,
|
||||
256,
|
||||
true,
|
||||
translucent,
|
||||
multiPhaseParameters
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderToBuffer(
|
||||
stack: PoseStack,
|
||||
buffer: VertexConsumer,
|
||||
@@ -49,6 +91,7 @@ abstract class PosableEntityModel<T : Entity>(
|
||||
// We're using the living entity renderer, ergo we need to apply a Y offset of 24 model units (1.5 blocks) for reasons only God can tell us.
|
||||
stack.translate(0.0, 1.5, 0.0)
|
||||
}
|
||||
// RenderSystem.setShader { CobblemonShaders.OVERLAY }
|
||||
posableModel.render(context, stack, buffer, packedLight, overlay, color)
|
||||
}
|
||||
|
||||
|
||||
+32
-11
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.animationFunctions
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
|
||||
import com.cobblemon.mod.common.client.render.AnimatedModelTextureSupplier
|
||||
import com.cobblemon.mod.common.client.render.ModelLayer
|
||||
@@ -33,6 +34,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.SimpleQuirk
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunction
|
||||
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
|
||||
@@ -40,26 +42,33 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.plus
|
||||
import com.cobblemon.mod.common.util.toRGBA
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
import com.mojang.blaze3d.vertex.VertexFormat
|
||||
import com.mojang.math.Axis
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.model.geom.ModelPart
|
||||
import net.minecraft.client.renderer.GameRenderer
|
||||
import net.minecraft.client.renderer.MultiBufferSource
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.client.renderer.ShaderInstance
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.ItemDisplayContext
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.apache.commons.lang3.tuple.Triple
|
||||
import org.joml.Matrix4f
|
||||
import org.joml.Vector3f
|
||||
import java.lang.reflect.Constructor
|
||||
|
||||
/**
|
||||
* A model that can be posed and animated using [PoseAnimation]s and [ActiveAnimation]s. This
|
||||
@@ -414,6 +423,7 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
/** Needed a custom one, so I can make it take in a dynamic texture instead of resource location */
|
||||
class DynamicStateShard(texture: DynamicTexture) : RenderStateShard.EmptyTextureStateShard(Runnable {
|
||||
RenderSystem.setShaderTexture(0, texture.id)
|
||||
RenderSystem.setShaderTexture(1, cobblemonResource("textures/tera_layer.png"))
|
||||
}, Runnable {
|
||||
texture.close() // Cleanup
|
||||
})
|
||||
@@ -453,13 +463,14 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
if (provider != null) {
|
||||
for (layer in currentLayers) {
|
||||
var renderLayer : RenderType
|
||||
var texture: ResourceLocation? = null
|
||||
if (layer.texture is AnimatedModelTextureSupplier && layer.texture.interpolation) {
|
||||
//Handle Interpolation
|
||||
val texture = layer.texture.interpolatedTexture(currentState ?: FloatingState()) ?: continue
|
||||
renderLayer = makeLayer(DynamicStateShard(texture), layer.emissive, layer.translucent, layer.translucent_cull)
|
||||
}
|
||||
else {
|
||||
val texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
|
||||
texture = layer.texture?.invoke(currentState ?: FloatingState()) ?: continue
|
||||
renderLayer = getLayer(texture, layer.emissive, layer.translucent, layer.translucent_cull)
|
||||
}
|
||||
val consumer = provider.getBuffer(renderLayer)
|
||||
@@ -490,18 +501,21 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
return ItemDisplayContext.valueOf(displayContextString)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var test: Any? = null
|
||||
|
||||
fun getOrMakeShard(): Any {
|
||||
if (test == null) {
|
||||
test = RenderStateShard.ShaderStateShard({ CobblemonShaders.OVERLAY })
|
||||
}
|
||||
return test!!
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates a [RenderType] by the power of god and anime. Only possible thanks to 100 access wideners. */
|
||||
fun makeLayer(texture: RenderStateShard.EmptyTextureStateShard, emissive: Boolean, translucent: Boolean, translucentCull: Boolean): RenderType {
|
||||
val multiPhaseParameters: RenderType.CompositeState = RenderType.CompositeState.builder()
|
||||
.setShaderState(
|
||||
when {
|
||||
emissive && translucent -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_EMISSIVE_SHADER
|
||||
!emissive && translucent && translucentCull -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_CULL_SHADER
|
||||
!emissive && translucent -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_SHADER
|
||||
!emissive && !translucent -> RenderStateShard.RENDERTYPE_ENTITY_CUTOUT_SHADER
|
||||
else -> RenderStateShard.RENDERTYPE_ENTITY_TRANSLUCENT_EMISSIVE_SHADER // This one should be changed to maybe a custom shader? Translucent stuffs with things
|
||||
}
|
||||
)
|
||||
.setShaderState(getOrMakeShard() as RenderStateShard.ShaderStateShard)
|
||||
.setTextureState(texture)
|
||||
.setTransparencyState(if (translucent) RenderStateShard.TRANSLUCENT_TRANSPARENCY else RenderStateShard.NO_TRANSPARENCY)
|
||||
.setLightmapState(if (!emissive) RenderStateShard.LIGHTMAP else RenderStateShard.LightmapStateShard(false))
|
||||
@@ -528,7 +542,14 @@ open class PosableModel(@Transient override val rootPart: Bone) : ModelFrame {
|
||||
} else if (!emissive && !translucentCull) {
|
||||
RenderType.entityTranslucent(texture)
|
||||
} else {
|
||||
makeLayer(RenderStateShard.TextureStateShard(texture, false, false), emissive = emissive, translucent = translucent, translucentCull = translucentCull)
|
||||
val texture = RenderStateShard.MultiTextureStateShard(ImmutableList.of(
|
||||
Triple.of(texture, false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
Triple.of(cobblemonResource("textures/tera_layer.png"), false, false),
|
||||
))
|
||||
makeLayer(texture, emissive = emissive, translucent = translucent, translucentCull = translucentCull)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user