From d7b3a938556cf656a29638b023fa09ab6ba85f46 Mon Sep 17 00:00:00 2001 From: Floral <49110090+floral-qua-floral@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:18:05 -0500 Subject: [PATCH] Getting some weirdness where Mario transitions back into Fall on the client side after landing...? :( Need to figure that out... --- .../states/CharacterDefinition.java | 3 + .../actions/GroundedActionDefinition.java | 8 + .../actions/MountedActionDefinition.java | 32 ++++ .../actions/util/TransitionDefinition.java | 2 +- .../util/TransitionInjectionDefinition.java | 3 +- .../actions/airborne/Fall.java | 90 +++++++++++ .../actions/airborne/Jump.java | 53 +++++++ .../actions/grounded/SubWalk.java | 8 +- .../actions/grounded/WalkRun.java | 15 +- .../actions/mounted/Mounted.java | 90 +++++++++++ .../fqf/mario_qua_mario/characters/Mario.java | 5 + content/src/main/resources/fabric.mod.json | 13 +- .../mariodata/MarioMainClientData.java | 4 +- .../mariodata/MarioPlayerData.java | 23 +-- .../mariodata/MarioServerPlayerData.java | 147 +++++++++++------- .../mixin/PlayerEntityMixin.java | 36 +++-- .../packets/MarioDataPackets.java | 2 + .../registries/RegistryManager.java | 1 + .../actions/AbstractParsedAction.java | 8 +- .../actions/ParsedActionHelper.java | 7 +- .../UniversalActionDefinitionHelper.java | 59 +++++-- .../actions/parsed/ParsedAirborneAction.java | 3 +- .../actions/parsed/ParsedAquaticAction.java | 3 +- .../actions/parsed/ParsedGenericAction.java | 3 +- .../actions/parsed/ParsedGroundedAction.java | 3 +- .../actions/parsed/ParsedMountedAction.java | 48 ++++++ .../actions/parsed/ParsedWallboundAction.java | 8 +- .../power_granting/ParsedCharacter.java | 7 + .../resources/mario_qua_mario.accesswidener | 3 +- 29 files changed, 570 insertions(+), 117 deletions(-) create mode 100644 api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/MountedActionDefinition.java create mode 100644 content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Fall.java create mode 100644 content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Jump.java create mode 100644 content/src/main/java/com/fqf/mario_qua_mario/actions/mounted/Mounted.java create mode 100644 mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedMountedAction.java diff --git a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/CharacterDefinition.java b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/CharacterDefinition.java index 6346985..d9b2f87 100644 --- a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/CharacterDefinition.java +++ b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/CharacterDefinition.java @@ -1,8 +1,11 @@ package com.fqf.mario_qua_mario.definitions.states; +import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; public interface CharacterDefinition extends StatAlteringStateDefinition { Identifier getInitialAction(); Identifier getInitialPowerUp(); + + Identifier getMountedAction(Entity vehicle); } diff --git a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/GroundedActionDefinition.java b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/GroundedActionDefinition.java index a82cc84..b28a285 100644 --- a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/GroundedActionDefinition.java +++ b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/GroundedActionDefinition.java @@ -5,7 +5,9 @@ import com.fqf.mario_qua_mario.mariodata.IMarioReadableMotionData; import com.fqf.mario_qua_mario.mariodata.IMarioTravelData; import com.fqf.mario_qua_mario.util.CharaStat; +import net.minecraft.util.Pair; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -73,5 +75,11 @@ void applyDrag( ); double getSlipFactor(IMarioReadableMotionData data); + + void performJump( + IMarioTravelData data, + CharaStat jumpVel, + @Nullable CharaStat speedAddend + ); } } diff --git a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/MountedActionDefinition.java b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/MountedActionDefinition.java new file mode 100644 index 0000000..74edf1d --- /dev/null +++ b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/MountedActionDefinition.java @@ -0,0 +1,32 @@ +package com.fqf.mario_qua_mario.definitions.states.actions; + +import com.fqf.mario_qua_mario.definitions.states.actions.util.IncompleteActionDefinition; +import com.fqf.mario_qua_mario.definitions.states.actions.util.TransitionDefinition; +import com.fqf.mario_qua_mario.mariodata.IMarioReadableMotionData; +import com.fqf.mario_qua_mario.mariodata.IMarioTravelData; +import com.fqf.mario_qua_mario.util.CharaStat; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.Vec3d; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface MountedActionDefinition extends IncompleteActionDefinition { + boolean travelHook(IMarioTravelData data, Entity mount, MountedActionHelper helper); + + @NotNull List getBasicTransitions(MountedActionHelper helper); + @NotNull List getInputTransitions(MountedActionHelper helper); + @NotNull List getWorldCollisionTransitions(MountedActionHelper helper); + + /** + * Contains a number of methods intended to help with the creation of Mounted Actions. Can be cast to any of the + * other ActionHelpers, if for whatever reason you need them. + */ + interface MountedActionHelper { + Entity getMount(IMarioReadableMotionData data); + + void dismount(IMarioTravelData data, boolean reposition); + + double getSlipFactor(Entity mount); + } +} diff --git a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionDefinition.java b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionDefinition.java index 88519a5..9b02698 100644 --- a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionDefinition.java +++ b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionDefinition.java @@ -39,7 +39,7 @@ public TransitionDefinition(@NotNull Identifier targetID, @NotNull Evaluator eva * when you're the one transitioning. */ @FunctionalInterface public interface TravelExecutor { - boolean execute(IMarioTravelData data); + void execute(IMarioTravelData data); } /** diff --git a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionInjectionDefinition.java b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionInjectionDefinition.java index 6a9693e..48f73d6 100644 --- a/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionInjectionDefinition.java +++ b/api/src/main/java/com/fqf/mario_qua_mario/definitions/states/actions/util/TransitionInjectionDefinition.java @@ -24,6 +24,7 @@ public enum ActionCategory { GROUNDED, AIRBORNE, AQUATIC, - WALL + WALLBOUND, + MOUNTED } } diff --git a/content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Fall.java b/content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Fall.java new file mode 100644 index 0000000..3539e55 --- /dev/null +++ b/content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Fall.java @@ -0,0 +1,90 @@ +package com.fqf.mario_qua_mario.actions.airborne; + +import com.fqf.mario_qua_mario.MarioQuaMarioContent; +import com.fqf.mario_qua_mario.definitions.states.actions.AirborneActionDefinition; +import com.fqf.mario_qua_mario.definitions.states.actions.util.*; +import com.fqf.mario_qua_mario.mariodata.IMarioAuthoritativeData; +import com.fqf.mario_qua_mario.mariodata.IMarioClientData; +import com.fqf.mario_qua_mario.mariodata.IMarioTravelData; +import com.fqf.mario_qua_mario.util.CharaStat; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +import static com.fqf.mario_qua_mario.util.StatCategory.*; + +public class Fall implements AirborneActionDefinition { + @Override public @NotNull Identifier getID() { + return MarioQuaMarioContent.makeID("fall"); + } + + @Override public @Nullable String getAnimationName() { + return null; + } + @Override public @Nullable CameraAnimationSet getCameraAnimations() { + return null; + } + @Override public @NotNull SlidingStatus getSlidingStatus() { + return SlidingStatus.NOT_SLIDING; + } + + @Override public @NotNull SneakingRule getSneakingRule() { + return SneakingRule.PROHIBIT; + } + @Override public @NotNull SprintingRule getSprintingRule() { + return SprintingRule.PROHIBIT; + } + + @Override public @Nullable BumpType getBumpType() { + return null; + } + @Override public @Nullable Identifier getStompTypeID() { + return null; + } + + public static final CharaStat FALL_ACCEL = new CharaStat(-0.115, NORMAL_GRAVITY); + public static final CharaStat FALL_SPEED = new CharaStat(-3.25, TERMINAL_VELOCITY); + + public static final TransitionDefinition FALL = new TransitionDefinition( + MarioQuaMarioContent.makeID("fall"), + data -> !data.getMario().isOnGround(), + EvaluatorEnvironment.COMMON + ); + + @Override public void clientTick(IMarioClientData data, boolean isSelf) { + + } + @Override public void serverTick(IMarioAuthoritativeData data) { + + } + @Override public void travelHook(IMarioTravelData data, AirborneActionHelper helper) { + helper.applyGravity(data, FALL_ACCEL, null, FALL_SPEED); + } + + @Override public @NotNull List getBasicTransitions(AirborneActionHelper helper) { + return List.of(); + } + @Override public @NotNull List getInputTransitions(AirborneActionHelper helper) { + return List.of(); + } + @Override public @NotNull List getWorldCollisionTransitions(AirborneActionHelper helper) { + return List.of( + new TransitionDefinition( + MarioQuaMarioContent.makeID("sub_walk"), + data -> data.getMario().isOnGround(), + EvaluatorEnvironment.CLIENT_CHECKED + ) + ); + } + + @Override public @NotNull Set getTransitionInjections() { + return Set.of(); + } + + @Override public @NotNull List getUnarmedAttackInterceptions() { + return List.of(); + } +} diff --git a/content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Jump.java b/content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Jump.java new file mode 100644 index 0000000..850f873 --- /dev/null +++ b/content/src/main/java/com/fqf/mario_qua_mario/actions/airborne/Jump.java @@ -0,0 +1,53 @@ +package com.fqf.mario_qua_mario.actions.airborne; + +import com.fqf.mario_qua_mario.MarioQuaMarioContent; +import com.fqf.mario_qua_mario.definitions.states.actions.AirborneActionDefinition; +import com.fqf.mario_qua_mario.definitions.states.actions.GroundedActionDefinition; +import com.fqf.mario_qua_mario.definitions.states.actions.util.*; +import com.fqf.mario_qua_mario.mariodata.IMarioAuthoritativeData; +import com.fqf.mario_qua_mario.mariodata.IMarioClientData; +import com.fqf.mario_qua_mario.mariodata.IMarioTravelData; +import com.fqf.mario_qua_mario.util.CharaStat; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +import static com.fqf.mario_qua_mario.util.StatCategory.*; + +public class Jump extends Fall implements AirborneActionDefinition { + @Override public @NotNull Identifier getID() { + return MarioQuaMarioContent.makeID("jump"); + } + + @Override public @Nullable String getAnimationName() { + return null; + } + + public static final CharaStat JUMP_GRAVITY = new CharaStat(-0.095, JUMPING_GRAVITY); + + public static final CharaStat JUMP_VEL = new CharaStat(0.858, JUMP_VELOCITY); + public static final CharaStat JUMP_ADDEND = new CharaStat(0.2, JUMP_VELOCITY); + + public static TransitionDefinition makeJumpTransition(GroundedActionDefinition.GroundedActionHelper helper) { + return new TransitionDefinition( + MarioQuaMarioContent.makeID("jump"), + data -> data.getInputs().JUMP.isPressed(), + EvaluatorEnvironment.CLIENT_ONLY, + data -> helper.performJump(data, JUMP_VEL, JUMP_ADDEND), + (data, isSelf, seed) -> data.playJumpSound(seed) + ); + } + + @Override public void clientTick(IMarioClientData data, boolean isSelf) { + + } + @Override public void serverTick(IMarioAuthoritativeData data) { + + } + @Override public void travelHook(IMarioTravelData data, AirborneActionHelper helper) { + helper.applyGravity(data, Fall.FALL_ACCEL, JUMP_GRAVITY, Fall.FALL_SPEED); + } +} diff --git a/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/SubWalk.java b/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/SubWalk.java index 95f7c80..baf6d3e 100644 --- a/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/SubWalk.java +++ b/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/SubWalk.java @@ -1,6 +1,8 @@ package com.fqf.mario_qua_mario.actions.grounded; import com.fqf.mario_qua_mario.MarioQuaMarioContent; +import com.fqf.mario_qua_mario.actions.airborne.Fall; +import com.fqf.mario_qua_mario.actions.airborne.Jump; import com.fqf.mario_qua_mario.definitions.states.actions.GroundedActionDefinition; import com.fqf.mario_qua_mario.definitions.states.actions.util.*; import com.fqf.mario_qua_mario.mariodata.IMarioAuthoritativeData; @@ -150,11 +152,13 @@ else if(data.getForwardVel() < BACKPEDAL_SPEED.getAsLimit(data)) { } @Override public @NotNull List getInputTransitions(GroundedActionHelper helper) { return List.of( - + Jump.makeJumpTransition(helper) ); } @Override public @NotNull List getWorldCollisionTransitions(GroundedActionHelper helper) { - return List.of(); + return List.of( + Fall.FALL + ); } @Override public @NotNull Set getTransitionInjections() { diff --git a/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/WalkRun.java b/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/WalkRun.java index dd8f7c5..5cb30e6 100644 --- a/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/WalkRun.java +++ b/content/src/main/java/com/fqf/mario_qua_mario/actions/grounded/WalkRun.java @@ -73,6 +73,19 @@ public static boolean meetsWalkRunRequirement(IMarioReadableMotionData data) { } @Override public @NotNull Set getTransitionInjections() { - return Set.of(); + return Set.of( + new TransitionInjectionDefinition( + TransitionInjectionDefinition.InjectionPlacement.BEFORE, + MarioQuaMarioContent.makeID("sub_walk"), + TransitionInjectionDefinition.ActionCategory.AIRBORNE, + nearbyTransition -> new TransitionDefinition( + this.getID(), + data -> meetsWalkRunRequirement(data) && nearbyTransition.evaluator().shouldTransition(data), + EvaluatorEnvironment.CLIENT_ONLY, + nearbyTransition.travelExecutor(), + nearbyTransition.clientsExecutor() + ) + ) + ); } } diff --git a/content/src/main/java/com/fqf/mario_qua_mario/actions/mounted/Mounted.java b/content/src/main/java/com/fqf/mario_qua_mario/actions/mounted/Mounted.java new file mode 100644 index 0000000..30e7c2e --- /dev/null +++ b/content/src/main/java/com/fqf/mario_qua_mario/actions/mounted/Mounted.java @@ -0,0 +1,90 @@ +package com.fqf.mario_qua_mario.actions.mounted; + +import com.fqf.mario_qua_mario.MarioQuaMarioContent; +import com.fqf.mario_qua_mario.definitions.states.actions.MountedActionDefinition; +import com.fqf.mario_qua_mario.definitions.states.actions.util.*; +import com.fqf.mario_qua_mario.mariodata.IMarioAuthoritativeData; +import com.fqf.mario_qua_mario.mariodata.IMarioClientData; +import com.fqf.mario_qua_mario.mariodata.IMarioTravelData; +import net.minecraft.entity.Entity; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +public class Mounted implements MountedActionDefinition { + @Override public @NotNull Identifier getID() { + return MarioQuaMarioContent.makeID("mounted"); + } + + @Override public @Nullable String getAnimationName() { + return null; + } + @Override public @Nullable CameraAnimationSet getCameraAnimations() { + return null; + } + @Override public @NotNull SlidingStatus getSlidingStatus() { + return SlidingStatus.NOT_SLIDING; + } + + @Override public @NotNull SneakingRule getSneakingRule() { + return SneakingRule.PROHIBIT; + } + @Override public @NotNull SprintingRule getSprintingRule() { + return SprintingRule.PROHIBIT; + } + + @Override public @Nullable BumpType getBumpType() { + return null; + } + @Override public @Nullable Identifier getStompTypeID() { + return null; + } + + @Override public void clientTick(IMarioClientData data, boolean isSelf) { + + } + @Override public void serverTick(IMarioAuthoritativeData data) { + + } + @Override public boolean travelHook(IMarioTravelData data, Entity mount, MountedActionHelper helper) { + return false; + } + + @Override public @NotNull List getBasicTransitions(MountedActionHelper helper) { + return List.of( + new TransitionDefinition( + MarioQuaMarioContent.makeID("sub_walk"), + data -> helper.getMount(data) == null, + EvaluatorEnvironment.COMMON + ) + ); + } + @Override public @NotNull List getInputTransitions(MountedActionHelper helper) { + return List.of( + new TransitionDefinition( + MarioQuaMarioContent.makeID("walk_run"), + data -> data.getInputs().DUCK.isHeld() && data.getInputs().JUMP.isPressed(), + EvaluatorEnvironment.CLIENT_ONLY, + data -> { + data.setYVel(1); + helper.dismount(data, false); + }, + (data, isSelf, seed) -> data.playJumpSound(seed) + ) + ); + } + @Override public @NotNull List getWorldCollisionTransitions(MountedActionHelper helper) { + return List.of(); + } + + @Override public @NotNull Set getTransitionInjections() { + return Set.of(); + } + + @Override public @NotNull List getUnarmedAttackInterceptions() { + return List.of(); + } +} diff --git a/content/src/main/java/com/fqf/mario_qua_mario/characters/Mario.java b/content/src/main/java/com/fqf/mario_qua_mario/characters/Mario.java index 8bcc19d..7aec756 100644 --- a/content/src/main/java/com/fqf/mario_qua_mario/characters/Mario.java +++ b/content/src/main/java/com/fqf/mario_qua_mario/characters/Mario.java @@ -4,6 +4,7 @@ import com.fqf.mario_qua_mario.definitions.states.CharacterDefinition; import com.fqf.mario_qua_mario.mariodata.IMarioAuthoritativeData; import com.fqf.mario_qua_mario.mariodata.IMarioClientData; +import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; @@ -21,6 +22,10 @@ public class Mario implements CharacterDefinition { return MarioQuaMarioContent.makeID("super"); } + @Override public Identifier getMountedAction(Entity vehicle) { + return MarioQuaMarioContent.makeID("mounted"); + } + @Override public float getWidthFactor() { return 1; } diff --git a/content/src/main/resources/fabric.mod.json b/content/src/main/resources/fabric.mod.json index b463d01..94ee8eb 100644 --- a/content/src/main/resources/fabric.mod.json +++ b/content/src/main/resources/fabric.mod.json @@ -16,19 +16,24 @@ "environment": "*", "entrypoints": { "mqm-generic-actions": [ - "com.fqf.mario_qua_mario.actions.generic.Debug", - "com.fqf.mario_qua_mario.actions.generic.DebugSprint" + "com.fqf.mario_qua_mario.actions.generic.Debug", + "com.fqf.mario_qua_mario.actions.generic.DebugSprint" ], "mqm-grounded-actions": [ - "com.fqf.mario_qua_mario.actions.grounded.SubWalk", - "com.fqf.mario_qua_mario.actions.grounded.WalkRun" + "com.fqf.mario_qua_mario.actions.grounded.SubWalk", + "com.fqf.mario_qua_mario.actions.grounded.WalkRun" ], "mqm-airborne-actions": [ + "com.fqf.mario_qua_mario.actions.airborne.Fall", + "com.fqf.mario_qua_mario.actions.airborne.Jump" ], "mqm-aquatic-actions": [ ], "mqm-wallbound-actions": [ ], + "mqm-mounted-actions": [ + "com.fqf.mario_qua_mario.actions.mounted.Mounted" + ], "mqm-power-ups": [ "com.fqf.mario_qua_mario.powerups.Small", diff --git a/mod/src/client/java/com/fqf/mario_qua_mario/mariodata/MarioMainClientData.java b/mod/src/client/java/com/fqf/mario_qua_mario/mariodata/MarioMainClientData.java index d5e8ca0..8581f1c 100644 --- a/mod/src/client/java/com/fqf/mario_qua_mario/mariodata/MarioMainClientData.java +++ b/mod/src/client/java/com/fqf/mario_qua_mario/mariodata/MarioMainClientData.java @@ -36,7 +36,7 @@ public boolean travelHook(double forwardInput, double strafeInput) { ParsedActionHelper.attemptTransitions(this, TransitionPhase.WORLD_COLLISION); ParsedActionHelper.attemptTransitions(this, TransitionPhase.BASIC); - this.getAction().travelHook(this); + boolean cancelVanillaTravel = this.getAction().travelHook(this); ParsedActionHelper.attemptTransitions(this, TransitionPhase.INPUT); @@ -46,7 +46,7 @@ public boolean travelHook(double forwardInput, double strafeInput) { ParsedActionHelper.attemptTransitions(this, TransitionPhase.WORLD_COLLISION); this.getMario().updateLimbs(false); - return true; + return cancelVanillaTravel; } private final RealInputs INPUTS = new RealInputs(); diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioPlayerData.java b/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioPlayerData.java index 2cb4b1d..fa7077f 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioPlayerData.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioPlayerData.java @@ -60,21 +60,12 @@ public AbstractParsedAction getAction() { } public boolean setAction(@Nullable AbstractParsedAction fromAction, AbstractParsedAction toAction, long seed, boolean forced, boolean fromCommand) { - if(!this.getAction().equals(fromAction) && !forced && !fromCommand) { - // Check if we were recently in fromAction. If not, return false. - if(fromAction == null || this.RECENT_ACTIONS.stream().noneMatch(pair -> pair.getLeft().equals(fromAction))) { - Identifier fromActionID = fromAction == null ? null : fromAction.ID; - MarioQuaMario.LOGGER.info("TRANSITION REJECTED:\nCurrent action: {}\nFrom action: {}\nTo action: {}", this.getActionID(), fromActionID, toAction.ID); - return false; - } - } boolean transitionedNaturally = ParsedActionHelper.attemptTransitionTo(this, fromAction == null ? this.getAction() : fromAction, toAction, seed); if(transitionedNaturally && this instanceof MarioMoveableData moveableData) moveableData.applyModifiedVelocity(); else if(forced) this.setActionTransitionless(toAction); return transitionedNaturally || forced; } public void setActionTransitionless(AbstractParsedAction action) { - this.RECENT_ACTIONS.add(new Pair<>(this.action, this.getMario().getWorld().getTime() + 10L)); this.action = action; } @@ -131,8 +122,6 @@ public void setMario(PlayerEntity mario) { } public void tick() { - long worldTime = this.getMario().getWorld().getTime(); - this.RECENT_ACTIONS.removeIf(pair -> worldTime > pair.getRight()); } @Override public double getStat(CharaStat stat) { @@ -147,14 +136,14 @@ public void tick() { return this.getPowerUp().BUMP_STRENGTH_MODIFIER + this.getCharacter().BUMP_STRENGTH_MODIFIER; } - private final Set> RECENT_ACTIONS = new HashSet<>(); - private boolean recentlyInAction(AbstractParsedAction action) { - return true; - } - public boolean doMarioTravel() { return this.isEnabled() && !this.getMario().getAbilities().flying && !this.getMario().isFallFlying() && !this.getMario().isUsingRiptide(); } - public boolean attemptDismount; + public DismountType attemptDismount; + public enum DismountType { + REMAIN_MOUNTED, + DISMOUNT_IN_PLACE, + VANILLA_DISMOUNT + } } diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioServerPlayerData.java b/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioServerPlayerData.java index fc57dd7..6f6d40d 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioServerPlayerData.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/mariodata/MarioServerPlayerData.java @@ -1,5 +1,6 @@ package com.fqf.mario_qua_mario.mariodata; +import com.fqf.mario_qua_mario.MarioQuaMario; import com.fqf.mario_qua_mario.packets.MarioDataPackets; import com.fqf.mario_qua_mario.registries.RegistryManager; import com.fqf.mario_qua_mario.registries.actions.AbstractParsedAction; @@ -11,9 +12,13 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; +import net.minecraft.util.Pair; import net.minecraft.util.math.random.RandomSeed; +import org.jetbrains.annotations.Nullable; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; public class MarioServerPlayerData extends MarioMoveableData implements IMarioAuthoritativeData { private ServerPlayerEntity mario; @@ -32,6 +37,90 @@ public MarioServerPlayerData() { } + private final Set> RECENT_ACTIONS = new HashSet<>(); + + @Override + public boolean setAction(@Nullable AbstractParsedAction fromAction, AbstractParsedAction toAction, long seed, boolean forced, boolean fromCommand) { + if(!this.getAction().equals(fromAction) && !forced && !fromCommand) { + // Check if we were recently in fromAction. If not, return false. + if(fromAction == null || this.RECENT_ACTIONS.stream().noneMatch(pair -> pair.getLeft().ID.equals(fromAction.ID))) { + Identifier fromActionID = fromAction == null ? null : fromAction.ID; + MarioQuaMario.LOGGER.warn( + "TRANSITION REJECTED:\nCurrent action: {}\nFrom action: {}\nTo action: {}\nRecent actions: {}", + this.getActionID(), fromActionID, toAction.ID, this.RECENT_ACTIONS.stream().findFirst().get().getLeft().ID); + return false; + } + } + return super.setAction(fromAction, toAction, seed, forced, fromCommand); + } + + @Override + public void setActionTransitionless(AbstractParsedAction action) { + this.RECENT_ACTIONS.add(new Pair<>(this.getAction(), this.getMario().getWorld().getTime() + 10L)); + super.setActionTransitionless(action); + } + + @Override + public boolean isClient() { + return false; + } + + @Override + public void tick() { + super.tick(); + this.getAction().serverTick(this); + this.getPowerUp().serverTick(this); + this.getCharacter().serverTick(this); + + long worldTime = this.getMario().getWorld().getTime(); + this.RECENT_ACTIONS.removeIf(pair -> worldTime > pair.getRight()); + } + + @Override + public boolean travelHook(double forwardInput, double strafeInput) { + ParsedActionHelper.attemptTransitions(this, TransitionPhase.WORLD_COLLISION); + ParsedActionHelper.attemptTransitions(this, TransitionPhase.BASIC); + + boolean cancelVanillaTravel = this.getAction().travelHook(this); + + ParsedActionHelper.attemptTransitions(this, TransitionPhase.INPUT); + + this.applyModifiedVelocity(); + this.getMario().move(MovementType.SELF, this.getMario().getVelocity()); + + ParsedActionHelper.attemptTransitions(this, TransitionPhase.WORLD_COLLISION); + + this.getMario().updateLimbs(false); + return cancelVanillaTravel; + } + + @Override public MarioInputs getInputs() { + return PHONY_INPUTS; + } + + private static final MarioInputs PHONY_INPUTS; + static { + MarioInputs.MarioButton phonyButton = new MarioInputs.MarioButton() { + @Override public boolean isPressed() { + return false; + } + @Override public boolean isHeld() { + return false; + } + }; + PHONY_INPUTS = new MarioInputs(phonyButton, phonyButton, phonyButton) { + @Override public double getForwardInput() { + return 0; + } + @Override public double getStrafeInput() { + return 0; + } + @Override public boolean isReal() { + return false; + } + }; + } + @Override public boolean transitionToAction(Identifier actionID) { long seed = RandomSeed.getSeed(); AbstractParsedAction toAction = Objects.requireNonNull(RegistryManager.ACTIONS.get(actionID), @@ -100,62 +189,4 @@ public MarioServerPlayerData() { @Override public void assignCharacter(String characterID) { this.assignCharacter(Identifier.of(characterID)); } - - @Override - public boolean isClient() { - return false; - } - - @Override - public void tick() { - super.tick(); - this.getAction().serverTick(this); - this.getPowerUp().serverTick(this); - this.getCharacter().serverTick(this); - } - - @Override - public boolean travelHook(double forwardInput, double strafeInput) { - ParsedActionHelper.attemptTransitions(this, TransitionPhase.WORLD_COLLISION); - ParsedActionHelper.attemptTransitions(this, TransitionPhase.BASIC); - - this.getAction().travelHook(this); - - ParsedActionHelper.attemptTransitions(this, TransitionPhase.INPUT); - - this.applyModifiedVelocity(); - this.getMario().move(MovementType.SELF, this.getMario().getVelocity()); - - ParsedActionHelper.attemptTransitions(this, TransitionPhase.WORLD_COLLISION); - - this.getMario().updateLimbs(false); - return true; - } - - @Override public MarioInputs getInputs() { - return PHONY_INPUTS; - } - - private static final MarioInputs PHONY_INPUTS; - static { - MarioInputs.MarioButton phonyButton = new MarioInputs.MarioButton() { - @Override public boolean isPressed() { - return false; - } - @Override public boolean isHeld() { - return false; - } - }; - PHONY_INPUTS = new MarioInputs(phonyButton, phonyButton, phonyButton) { - @Override public double getForwardInput() { - return 0; - } - @Override public double getStrafeInput() { - return 0; - } - @Override public boolean isReal() { - return false; - } - }; - } } diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/mixin/PlayerEntityMixin.java b/mod/src/main/java/com/fqf/mario_qua_mario/mixin/PlayerEntityMixin.java index 3c1a717..9edcf45 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/mixin/PlayerEntityMixin.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/mixin/PlayerEntityMixin.java @@ -6,6 +6,8 @@ import com.fqf.mario_qua_mario.mariodata.MarioMoveableData; import com.fqf.mario_qua_mario.mariodata.MarioPlayerData; import com.fqf.mario_qua_mario.mariodata.injections.MarioDataHolder; +import com.fqf.mario_qua_mario.registries.RegistryManager; +import com.fqf.mario_qua_mario.registries.actions.ParsedActionHelper; import com.fqf.mario_qua_mario.registries.power_granting.ParsedCharacter; import com.fqf.mario_qua_mario.registries.power_granting.ParsedPowerUp; import com.fqf.mario_qua_mario.util.MarioGamerules; @@ -13,10 +15,7 @@ import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import net.minecraft.block.BlockState; -import net.minecraft.entity.EntityDimensions; -import net.minecraft.entity.EntityPose; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.*; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.NbtCompound; @@ -35,7 +34,6 @@ public abstract class PlayerEntityMixin extends LivingEntity implements MarioDat @Shadow public abstract EntityDimensions getBaseDimensions(EntityPose pose); @Shadow public float strideDistance; - @Shadow public float prevStrideDistance; private PlayerEntityMixin(EntityType entityType, World world) { @@ -126,10 +124,30 @@ private void preventViewBobbing(CallbackInfo ci) { strideDistance = prevStrideDistance * 0.6F; } -// @Inject(method = "shouldDismount", at = @At("HEAD")) -// private void changeDismounting(CallbackInfoReturnable cir) { -// MarioPlayerData data = mqm$getMarioData(); -// } + @Override + public boolean startRiding(Entity entity, boolean force) { + MarioPlayerData data = mqm$getMarioData(); + boolean mounted = data.getCharacter().getMountedAction(entity) != null && super.startRiding(entity, force); + if(mounted) { + data.setActionTransitionless(data.getCharacter().getMountedAction(entity)); + data.attemptDismount = MarioPlayerData.DismountType.REMAIN_MOUNTED; + } + return mounted; + } + + @Inject(method = "shouldDismount", at = @At("HEAD"), cancellable = true) + private void changeDismounting(CallbackInfoReturnable cir) { + MarioPlayerData data = mqm$getMarioData(); + if(data.isEnabled()) cir.setReturnValue(data.attemptDismount != MarioPlayerData.DismountType.REMAIN_MOUNTED); + } + + @Override + protected void onDismounted(Entity vehicle) { + if(mqm$getMarioData().attemptDismount == MarioPlayerData.DismountType.DISMOUNT_IN_PLACE) + requestTeleportAndDismount(this.getX(), this.getY(), this.getZ()); + else + super.onDismounted(vehicle); + } @WrapWithCondition(method = "jump", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;jump()V")) private boolean preventLivingEntityJump(LivingEntity instance) { diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/packets/MarioDataPackets.java b/mod/src/main/java/com/fqf/mario_qua_mario/packets/MarioDataPackets.java index 2a9285f..0db9fe7 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/packets/MarioDataPackets.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/packets/MarioDataPackets.java @@ -1,5 +1,6 @@ package com.fqf.mario_qua_mario.packets; +import com.fqf.mario_qua_mario.MarioQuaMario; import com.fqf.mario_qua_mario.registries.RegistryManager; import com.fqf.mario_qua_mario.registries.actions.AbstractParsedAction; import com.fqf.mario_qua_mario.registries.actions.ParsedActionHelper; @@ -79,6 +80,7 @@ protected record SetActionC2SPayload(int fromAction, int toAction, long seed) im public static void receive(SetActionC2SPayload payload, ServerPlayNetworking.Context context) { AbstractParsedAction fromAction = ParsedActionHelper.get(payload.fromAction()); AbstractParsedAction toAction = ParsedActionHelper.get(payload.toAction()); + MarioQuaMario.LOGGER.info("Received setActionC2S: {}->{}", fromAction.ID, toAction.ID); boolean rejectInvalid = context.player().getWorld().getGameRules().getBoolean(MarioGamerules.REJECT_INVALID_ACTION_TRANSITIONS); if(context.player().mqm$getMarioData().setAction(fromAction, toAction, payload.seed, !rejectInvalid, false)) { MarioPackets.sendToTrackers(context.player(), new ActionTransitionS2CPayload( diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/registries/RegistryManager.java b/mod/src/main/java/com/fqf/mario_qua_mario/registries/RegistryManager.java index 47897b4..904a968 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/registries/RegistryManager.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/registries/RegistryManager.java @@ -76,6 +76,7 @@ private static void registerActions() { actionDefinitions.addAll(getEntrypoints("mqm-airborne-actions", AirborneActionDefinition.class)); actionDefinitions.addAll(getEntrypoints("mqm-aquatic-actions", AquaticActionDefinition.class)); actionDefinitions.addAll(getEntrypoints("mqm-wallbound-actions", WallboundActionDefinition.class)); + actionDefinitions.addAll(getEntrypoints("mqm-mounted-actions", MountedActionDefinition.class)); HashMap> allInjections = new HashMap<>(); for(IncompleteActionDefinition definition : actionDefinitions) { diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/AbstractParsedAction.java b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/AbstractParsedAction.java index b61aef7..e03b4d0 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/AbstractParsedAction.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/AbstractParsedAction.java @@ -70,8 +70,14 @@ private void parseTransitions( for(TransitionDefinition definition : transitions) { Set relevantInjections = new HashSet<>(allInjections.getOrDefault(definition.targetID(), Set.of())); + relevantInjections.removeIf(injection -> injection.category() != TransitionInjectionDefinition.ActionCategory.ANY && injection.category() != this.getCategory()); + MarioQuaMario.LOGGER.info("TRANSITION INJECTIONS RELEVANT TO {}:", definition.targetID()); + for (TransitionInjectionDefinition relevantInjection : relevantInjections) { + MarioQuaMario.LOGGER.info("This one: {}", relevantInjection); + } + this.conditionallyInjectTransitions(buildingClientList, buildingServerList, relevantInjections, TransitionInjectionDefinition.InjectionPlacement.BEFORE, definition); MarioQuaMario.LOGGER.info("Parsing transition into {}", definition.targetID()); @@ -111,7 +117,7 @@ public int getIntID() { return RegistryManager.ACTIONS.getRawIdOrThrow(this); } - abstract public void travelHook(MarioMoveableData data); + abstract public boolean travelHook(MarioMoveableData data); abstract protected TransitionInjectionDefinition.ActionCategory getCategory(); } diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/ParsedActionHelper.java b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/ParsedActionHelper.java index 4b6dc4f..ad30a00 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/ParsedActionHelper.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/ParsedActionHelper.java @@ -15,6 +15,7 @@ import net.minecraft.util.Identifier; import java.util.HashMap; +import java.util.Objects; import java.util.Set; public class ParsedActionHelper { @@ -28,12 +29,16 @@ public static AbstractParsedAction parseAction( case AirborneActionDefinition def -> new ParsedAirborneAction(def, allInjections); case AquaticActionDefinition def -> new ParsedAquaticAction(def, allInjections); case WallboundActionDefinition def -> new ParsedWallboundAction(def, allInjections); - default -> null; + case MountedActionDefinition def -> new ParsedMountedAction(def, allInjections); + default -> throw new AssertionError("Action Definition wasn't one of the known types?!?!"); }; } public static void attemptTransitions(MarioMoveableData data, TransitionPhase phase) { +// MarioQuaMario.LOGGER.info("Start checking on {}:", data.isClient() ? "CLIENT": "SERVER"); for(ParsedTransition transition : data.isClient() ? data.getAction().CLIENT_TRANSITIONS.get(phase) : data.getAction().SERVER_TRANSITIONS.get(phase)) { +// if(Objects.equals(data.getActionID(), MarioQuaMario.makeID("jump"))) +// MarioQuaMario.LOGGER.info("Testing transition from {}->{}:\n{}", data.getActionID(), transition.targetAction().ID, transition.evaluator().shouldTransition(data)); if(transition.evaluator().shouldTransition(data)) { long seed = data.getMario().getRandom().nextLong(); diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/UniversalActionDefinitionHelper.java b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/UniversalActionDefinitionHelper.java index e0a281c..e6f5ae8 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/UniversalActionDefinitionHelper.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/UniversalActionDefinitionHelper.java @@ -1,15 +1,14 @@ package com.fqf.mario_qua_mario.registries.actions; -import com.fqf.mario_qua_mario.definitions.states.actions.AirborneActionDefinition; -import com.fqf.mario_qua_mario.definitions.states.actions.AquaticActionDefinition; -import com.fqf.mario_qua_mario.definitions.states.actions.GroundedActionDefinition; -import com.fqf.mario_qua_mario.definitions.states.actions.WallboundActionDefinition; +import com.fqf.mario_qua_mario.definitions.states.actions.*; import com.fqf.mario_qua_mario.definitions.states.actions.util.IncompleteActionDefinition; import com.fqf.mario_qua_mario.definitions.states.actions.util.TransitionDefinition; import com.fqf.mario_qua_mario.mariodata.IMarioReadableMotionData; import com.fqf.mario_qua_mario.mariodata.IMarioTravelData; import com.fqf.mario_qua_mario.mariodata.MarioMoveableData; +import com.fqf.mario_qua_mario.mariodata.MarioPlayerData; import com.fqf.mario_qua_mario.util.CharaStat; +import net.minecraft.entity.Entity; import net.minecraft.util.math.BlockPos; import org.jetbrains.annotations.Nullable; import org.joml.Vector2d; @@ -18,7 +17,8 @@ public class UniversalActionDefinitionHelper implements GroundedActionDefinition.GroundedActionHelper, AirborneActionDefinition.AirborneActionHelper, AquaticActionDefinition.AquaticActionHelper, - WallboundActionDefinition.WallboundActionHelper { + WallboundActionDefinition.WallboundActionHelper, + MountedActionDefinition.MountedActionHelper { public static final UniversalActionDefinitionHelper INSTANCE = new UniversalActionDefinitionHelper(); private UniversalActionDefinitionHelper() {} @@ -75,22 +75,35 @@ public void applyDrag( @Override public double getSlipFactor(IMarioReadableMotionData data) { - return Math.pow(0.6 / getFloorSlipperiness(data), 3); + return Math.pow(0.6 / getFloorSlipperiness(data.getMario()), 3); } - private static float getFloorSlipperiness(IMarioReadableMotionData data) { - if(data.getMario().isOnGround()) { - BlockPos blockPos = data.getMario().getVelocityAffectingPos(); - return data.getMario().getWorld().getBlockState(blockPos).getBlock().getSlipperiness(); + private static float getFloorSlipperiness(Entity stepper) { + if(stepper.isOnGround()) { + BlockPos blockPos = stepper.getVelocityAffectingPos(); + return stepper.getWorld().getBlockState(blockPos).getBlock().getSlipperiness(); } return 0.6F; } + @Override + public void performJump(IMarioTravelData data, CharaStat jumpVel, @Nullable CharaStat speedAddend) { + double newYVel = jumpVel.get(data); + if(speedAddend != null) newYVel += speedAddend.get(data) * getSpeedFactor(data); + data.setYVel(newYVel); + } + private double getSpeedFactor(IMarioTravelData data) { + double scaledForwardVel = data.getForwardVel(); + if(scaledForwardVel < 0) return scaledForwardVel * 0.2; + if(scaledForwardVel < 1) return scaledForwardVel * scaledForwardVel; + return scaledForwardVel; + } + @Override public void applyGravity( IMarioTravelData data, CharaStat gravity, @Nullable CharaStat jumpingGravity, CharaStat terminalVelocity ) { - + this.applyGravity(data, (jumpingGravity == null || data.getTimers().jumpCapped) ? gravity : jumpingGravity, terminalVelocity); } @Override public void airborneAccel( @@ -110,7 +123,12 @@ public TransitionDefinition makeJumpCapTransition(IncompleteActionDefinition for @Override public void applyGravity(IMarioTravelData data, CharaStat gravity, CharaStat terminalVelocity) { - + double maxFallSpeed = terminalVelocity.get(data); + double yVel = data.getYVel(); + if(yVel > maxFallSpeed) { + yVel += gravity.get(data); + data.setYVel(Math.max(maxFallSpeed, yVel)); + } } @Override @@ -147,4 +165,21 @@ public WallboundActionDefinition.WallInfo getWallInfo(IMarioReadableMotionData d public void setSidleVel(IMarioTravelData data, double sidleVel) { } + + @Override + public Entity getMount(IMarioReadableMotionData data) { + return data.getMario().getVehicle(); + } + + @Override + public void dismount(IMarioTravelData data, boolean reposition) { + ((MarioPlayerData) data).attemptDismount = reposition + ? MarioPlayerData.DismountType.VANILLA_DISMOUNT + : MarioPlayerData.DismountType.DISMOUNT_IN_PLACE; + } + + @Override + public double getSlipFactor(Entity mount) { + return mount.isOnGround() ? 1.0 : Math.pow(0.6 / getFloorSlipperiness(mount), 3); + } } diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedAirborneAction.java b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedAirborneAction.java index 1e95d20..b5bbe7c 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedAirborneAction.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedAirborneAction.java @@ -21,8 +21,9 @@ public ParsedAirborneAction(AirborneActionDefinition definition, HashMap> allInjections) { + super(definition, allInjections); + this.MOUNTED_DEFINITION = definition; + } + + @Override + public boolean travelHook(MarioMoveableData data) { + UniversalActionDefinitionHelper helper = UniversalActionDefinitionHelper.INSTANCE; + return this.MOUNTED_DEFINITION.travelHook(data, helper.getMount(data), helper); + } + + @Override + protected TransitionInjectionDefinition.ActionCategory getCategory() { + return TransitionInjectionDefinition.ActionCategory.MOUNTED; + } + + @Override + protected List getBasicTransitions() { + return this.MOUNTED_DEFINITION.getBasicTransitions(UniversalActionDefinitionHelper.INSTANCE); + } + + @Override + protected List getInputTransitions() { + return this.MOUNTED_DEFINITION.getInputTransitions(UniversalActionDefinitionHelper.INSTANCE); + } + + @Override + protected List getWorldCollisionTransitions() { + return this.MOUNTED_DEFINITION.getWorldCollisionTransitions(UniversalActionDefinitionHelper.INSTANCE); + } +} diff --git a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedWallboundAction.java b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedWallboundAction.java index 2a7651f..1b8eef9 100644 --- a/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedWallboundAction.java +++ b/mod/src/main/java/com/fqf/mario_qua_mario/registries/actions/parsed/ParsedWallboundAction.java @@ -21,13 +21,15 @@ public ParsedWallboundAction(WallboundActionDefinition definition, HashMap